Repository navigation
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes scheduled retries on Redis Cluster by hash-tagging the rsmq queue keys, with a Redis migration that moves pending retries to the new keys.
The bug
Each rsmq queue is two keys: a sorted set
rsmq:deliverymq-retryand a hashrsmq:deliverymq-retry:Q. Schedule (SendMessage), receive (the receive Lua script) and cancel (DeleteMessage) each touch both in one transaction or script. On Redis Cluster the two keys hash to different slots, so all three fail withCROSSSLOT. Tenant keys were hash-tagged for cluster support in #616; the rsmq keys were missed.What a Redis Cluster user sees (reproduced with
make test/e2e/rediscluster, see #1138):alert.attempt.exhausted_retriesoperator event never fires.Standalone Redis and Dragonfly have no slots, so they're unaffected.
The fix
Hash-tagged keys (
internal/rsmq/rsmq.go,queueKey). Every rsmq key is now<ns>{<qname>}/<ns>{<qname>}:Q, with the deployment ID inside the tag when set:rsmq:deliverymq-retry,…:Qrsmq:{deliverymq-retry},…:QDEPLOYMENT_ID=dp_1dp_1:rsmq:deliverymq-retry,…:Qdp_1:rsmq:{dp_1:deliverymq-retry},…:QThe deployment ID goes inside the tag so deployments sharing one cluster spread across slots; it stays at the front too, so
SCAN MATCH dp_1:*still finds the queue. This applies everywhere, not only in cluster mode, so there's one key layout. TheQUEUESset is untouched.Redis migration
004_rsmq_hash_tags(internal/migrator/migratorredis/004_rsmq_hash_tags, README there). Fordeliverymq-retryanddeliverymq-retry-dlq, under the configured deployment ID:<id>,<id>:rc,<id>:fr. Messages being processed keep their score. The tagged hash gets the old queue's settings (vt,delay,maxsize, …) where it doesn't have them. The old hash keeps its settings, so instances still on the previous version see a valid, empty queue and keep working without errors.outpost migrate cleanup <migration>runs an applied migration's cleanup under the migration lock, with a confirmation prompt (--yesskips it). Without a name it lists the applied migrations that have something to clean up, with the number of keys each would remove. It takes a name because001_hash_tags' cleanup deletes every untaggedtenant:*key, which an operator cleaning up the retry queues shouldn't get as a side effect. Documented inmigration.mdoc.NewRedisSMQtakes options (WithLogger,WithHashTagPrefix,WithUntaggedKeys) in place of the variadic logger.WithUntaggedKeysgives the old layout; the tests use it to play the previous version.cmd/redis-debugand its sample output introubleshooting-redis.mdoclist both layouts.Rollout
For standalone Redis and Dragonfly. Outpost refuses to start with pending migrations, so step 1 is required:
outpost migrate apply, before starting the new version. Moves scheduled retries to the new keys.outpost migrate cleanup 004_rsmq_hash_tags. Moves retries that old instances scheduled during the rollout (newest wins) and deletes the old keys.In between:
On Redis Cluster the migration is a no-op (marked not applicable) and there's nothing to clean up.
Behavior changes
deliverymq-retry-dlq, live under the tagged keys after the migration.outpost migrate cleanup.Tests
TestE2E_Compat_RedisClusterpasses in full, including the 4 subtests that fail on fix: make the redis cluster e2e test target run against the test stack #1138; noCROSSSLOTin the logs. Default e2e (Dragonfly) and the short suite pass.TestRSMQHashTags_*(miniredis, Dragonfly, and Redis withTESTCOMPAT=1), with and without a deployment ID:TestRSMQHashTags_NotApplicableOnCluster.TestRetryScheduler_MigratedLegacyQueue: a retry scheduled the way the previous version did fires through the real retry scheduler after apply, with attempt 2.TestCoordinator_Cleanup: runs only the named migration, refuses pending and unknown ones, nothing for not-applicable ones, fails while the lock is held.🤖 Generated with Claude Code