Redis Cluster Consistency Plan
July 31, 2026 · View on GitHub
Current State
This backend stores one logical client across multiple Redis keys:
client:client:<id>client:name:<name>client:api_key:<api_key>client:idx:<id>client:countclient:id_seq
In Redis Cluster, those keys map to different hash slots by default. Legacy
cluster mode still works, but multi-key writes are not atomic. A failure in
the middle of add_item(), update_client(), or remove_client() can leave
indexes or counters temporarily inconsistent.
sync() repairs this drift, but it is a recovery tool, not a transaction
boundary.
The backend also supports an optional cluster_hash_tag setting. When
enabled, all keys for a namespace are stored in the same Redis Cluster slot
and writes use RedisCluster.pipeline(transaction=True).
Smallest Safe Production Migration
The safest next step is not a broad redesign. It is a targeted schema update for cluster deployments:
- Enable a fixed cluster hash tag, for example
cluster_hash_tag="clients". - Generate all Redis keys for that deployment under the same slot, for example:
client:{clients}:client:1client:{clients}:name:alphaclient:{clients}:api_key:alpha-keyclient:{clients}:idx:1client:{clients}:countclient:{clients}:id_seq
- In cluster mode with that tag enabled, the backend uses
RedisCluster.pipeline(transaction=True).
Because every key shares the same hash tag, all commands map to the same slot. That is the minimum change that allows real Redis Cluster transactions for this schema.
Why This Is The Right Tradeoff
- The client database is small and metadata-heavy. It gains little from distributing individual index keys across shards.
- A single-slot namespace keeps the existing schema and query model.
- The application gets real atomic updates in cluster mode without a more complex write protocol.
- Recovery and rollback stay simple.
Recommended Rollout
- Keep the legacy key layout as the default for backward compatibility.
- Migrate existing cluster deployments during a maintenance window:
- stop writers
- snapshot the Redis namespace
- scan legacy
client:client:*records - re-write them through the new tagged backend into the new namespace
- run
sync()on the new namespace - validate
len(db),search_by_value("name", ...), andsearch_by_value("api_key", ...) - switch production config to the tagged namespace
- Keep the legacy namespace for rollback until the new deployment is stable.
Migration Tool
This repository ships a helper command:
hivemind-redis-migrate-cluster \
--config ~/.config/hivemind-core/server.json \
--target-cluster-hash-tag clients \
--clear-target
What it does:
- reads the existing HiveMind Redis plugin config
- copies raw client records from the source namespace to the target namespace
- skips stale in-progress create markers
- runs
sync()on the target namespace to rebuild indexes, counters, and search hashes
Use --dry-run first to inspect the plan without writing data.
What Not To Do
- Do not try to fake atomicity across cluster slots with ordinary pipelines.
- Do not use distributed locks here unless you have a much stronger consistency requirement. Locks add operational complexity without solving the schema issue.
- Do not switch the default key format in place. That would silently orphan existing data.
Practical Recommendation
For new cluster deployments, enable cluster_hash_tag from the start.
For existing cluster deployments, migrate deliberately and keep sync() as
the repair path during rollout.