Repository navigation
Introduce atomic slot migration - #1949
Merged
madolson merged 125 commits intoAug 12, 2025
Merged
Conversation
1. Define new structure slotRange and clusterSlotSyncLink; 2. Add CLUSTER SLOTLINK command to manage all the slot sync links. Signed-off-by: Binbin <binloveplay1314@qq.com>
1. Add CLUSTER SLOTSYNC/SLOTSYNCFORCE command to trigger slot sync. Signed-off-by: Binbin <binloveplay1314@qq.com>
1. Extend the SYNC command, let it specify the slot ranges; 2. Enable to filter the keys in the specified slots when generate rdb; 3. Implement the handshake process before rdb transfer for slot sync.f Signed-off-by: Binbin <binloveplay1314@qq.com>
1. Implement the rdb transfer and loading for slot sync. Signed-off-by: Binbin <binloveplay1314@qq.com>
1. Enable to filter the cmds in the specified slots when feed slaves; 2. Implement the messages exchange for slot sync; 3. Add clusterSlotSyncCron() to handle time events for slot sync. Signed-off-by: Binbin <binloveplay1314@qq.com>
1. Add CLUSTER FAILOVER command to trigger slot failover; 2. Implement the process of slot failover. Signed-off-by: Binbin <binloveplay1314@qq.com>
1. Improve the delDbKeysInSlot() to support time limit; 2. Implement the slot pending delete. Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
β¦ot RDBs In slot sync, we can load multiple slot RDBs from different nodes. slot RDBs will contain functions, which may encounter an `already exists` error when loading since every node will have the function data. This is a limitation in ours slot RDB design. This commit added a new RDBFLAGS_SLOT_SYNC flag, it is a hint that means we are targeting slot rdb. With this flag, when loading the function, we will treat it like FUNCTION LOAD REPLACE, so we won't have errors and the function reached last will win (which won't be a problem at the moment since all the function is the same in cluster). Signed-off-by: Binbin <binloveplay1314@qq.com>
We have three nodes, A and B doing the slot sync, A is the src node, and B is the dst node. Before we doing the CLUSTER SLOTFAILOVER, we adds a new C, and C is the replica of B. In this time, if the connection between A and B breaks, in B's views, when doing freeClient, it will call onSlotSyncClientClose to reset the link, and then B will try to do a new slot SYNC in cron. And then B will call delkeysNotOwnedByMySelf to delete all the keys in that slot, which will propagate to C and C will becoma a empty DB. When B doing the new slot SYNC, A will generate a new slot RDB, and B will re-load the new slot RDB. But this new RDB file will not be propagated to node C because the master-replica connection between B and C is normal, which will result a data loss in C. In this commit, after the master node loads the slot RDB, we need to disconnect all slave nodes and allow the slave nodes to fully synchronize (we don't support slot psync). Signed-off-by: Binbin <binloveplay1314@qq.com>
In SET command, the expire time will be rewrite to a new
robj, which mostly is an INT:
```
/* Propagate as SET Key Value PXAT millisecond-timestamp if there is
* EX/PX/EXAT/PXAT flag. */
robj *milliseconds_obj = createStringObjectFromLongLong(milliseconds);
rewriteClientCommandVector(c, 5, shared.set, key, val, shared.pxat, milliseconds_obj);
```
And then if we are doing resharding, the server will crash.
That is because we will call isCommandInSlotRanges to check
the slot, and we will call getKeysFromCommand to get the key
from command argv. The milliseconds_obj is an INT and the code
in setGetKeys treat it as a STRING, so the server crash.
In this fix, we decided that in isCommandInSlotRanges, if we
find an INT, we decode it to a STRING.
In addition, we added a new debug enable-debug-assert so that
we can try to cover the isCommandInSlotRanges function in ours
TCL tests.
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Let's take expire as an example. If the target node deleted an expired key or the key is logic expires during the process of the loading of the slot RDB, the source node may propagate the expire command after loading the slot RDB. These expire commands will become invalid on the logically expired keys, resulting in data loss. During the slot migration process, the keys are not considered expired in the expiration check of the command propagated by the source node. Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
The reply will mess up the slot replication. Signed-off-by: Binbin <binloveplay1314@qq.com>
This causes the source node to not trim the reply, causing the querybuf of the target node to be too large, resulting in the target node being disconnected dut to the querybuf limit (1G). Signed-off-by: Binbin <binloveplay1314@qq.com>
We need to make sure this block is only executed in the source node, otherwise targe node call it will reset the slot failover. Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
β¦flags Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
This can happend when multiple targets doing slot sync at the same time. When doing disk-based slot RDB replication, this replicationSetupReplicaForFullResync call here may result in sending the slot RDB to a different target, or to a normal replica, or sending a normal RDB to a target. Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
zuiderkwast
pushed a commit
to zuiderkwast/valkey
that referenced
this pull request
Jan 30, 2026
β¦n and data corruption (valkey-io#3004) When loading AOF in cluster mode, keys inside a MULTI/EXEC block could be inserted into wrong hash slots, causing key duplication and data corruption. The root cause was the slot caching optimization in getKeySlot(). This optimization reuses a cached slot value to avoid recalculating the hash for every key operation. However, when replaying AOF, a transaction may contain commands affecting keys in different slots. The cached slot from a previous command (e.g., SET k1) would incorrectly be used for subsequent commands in the transaction (e.g., SET k0), causing k0 to be stored in k1's slot. The existing code already skipped this optimization for replicated clients (commands from primary) using isReplicatedClient(). This change extends that to also skip for AOF clients by using mustObeyClient() instead, which covers both replicated clients and the AOF client. Fixes valkey-io#2995, introduced in valkey-io#1949. Signed-off-by: aditya.teltia <teltia.aditya22@gmail.com>
zuiderkwast
pushed a commit
that referenced
this pull request
Feb 3, 2026
β¦n and data corruption (#3004) When loading AOF in cluster mode, keys inside a MULTI/EXEC block could be inserted into wrong hash slots, causing key duplication and data corruption. The root cause was the slot caching optimization in getKeySlot(). This optimization reuses a cached slot value to avoid recalculating the hash for every key operation. However, when replaying AOF, a transaction may contain commands affecting keys in different slots. The cached slot from a previous command (e.g., SET k1) would incorrectly be used for subsequent commands in the transaction (e.g., SET k0), causing k0 to be stored in k1's slot. The existing code already skipped this optimization for replicated clients (commands from primary) using isReplicatedClient(). This change extends that to also skip for AOF clients by using mustObeyClient() instead, which covers both replicated clients and the AOF client. Fixes #2995, introduced in #1949. Signed-off-by: aditya.teltia <teltia.aditya22@gmail.com>
harrylin98
pushed a commit
to harrylin98/valkey_forked
that referenced
this pull request
Feb 19, 2026
β¦n and data corruption (valkey-io#3004) When loading AOF in cluster mode, keys inside a MULTI/EXEC block could be inserted into wrong hash slots, causing key duplication and data corruption. The root cause was the slot caching optimization in getKeySlot(). This optimization reuses a cached slot value to avoid recalculating the hash for every key operation. However, when replaying AOF, a transaction may contain commands affecting keys in different slots. The cached slot from a previous command (e.g., SET k1) would incorrectly be used for subsequent commands in the transaction (e.g., SET k0), causing k0 to be stored in k1's slot. The existing code already skipped this optimization for replicated clients (commands from primary) using isReplicatedClient(). This change extends that to also skip for AOF clients by using mustObeyClient() instead, which covers both replicated clients and the AOF client. Fixes valkey-io#2995, introduced in valkey-io#1949. Signed-off-by: aditya.teltia <teltia.aditya22@gmail.com>
hpatro
pushed a commit
to hpatro/valkey
that referenced
this pull request
Mar 5, 2026
β¦n and data corruption (valkey-io#3004) When loading AOF in cluster mode, keys inside a MULTI/EXEC block could be inserted into wrong hash slots, causing key duplication and data corruption. The root cause was the slot caching optimization in getKeySlot(). This optimization reuses a cached slot value to avoid recalculating the hash for every key operation. However, when replaying AOF, a transaction may contain commands affecting keys in different slots. The cached slot from a previous command (e.g., SET k1) would incorrectly be used for subsequent commands in the transaction (e.g., SET k0), causing k0 to be stored in k1's slot. The existing code already skipped this optimization for replicated clients (commands from primary) using isReplicatedClient(). This change extends that to also skip for AOF clients by using mustObeyClient() instead, which covers both replicated clients and the AOF client. Fixes valkey-io#2995, introduced in valkey-io#1949. Signed-off-by: aditya.teltia <teltia.aditya22@gmail.com> Signed-off-by: Harkrishn Patro <bunty.hari@gmail.com>
6 of 12 tasks
This was referenced Mar 26, 2026
enjoy-binbin
added a commit
to enjoy-binbin/valkey
that referenced
this pull request
Apr 4, 2026
In valkey.conf, slot-migration-max-failover-repl-bytes allows setting to -1 to disable the limit. ``` Setting this to -1 will disable this limit ``` But slot-migration-max-failover-repl-bytes is defined as MEMORY_CONFIG and memtoull() rejects negative inputs, making it impossible to set the value to -1 via config file or CONFIG SET. ``` >>> 'slot-migration-max-failover-repl-bytes "-1"' argument must be a memory value ``` Introduce SIGNED_MEMORY_CONFIG flag for memory configs that also accept plain negative number. When memtoull() fails and this flag is set, fall back to string2ll() for parsing. Use ll2string() for CONFIG GET and rewriteConfigNumericalOption() for CONFIG REWRITE when the value is negative. Add a serverAssert in initConfigValues() to enforce that PERCENT_CONFIG and SIGNED_MEMORY_CONFIG are never combined on the same config, since both use negative values with different semantics. This means we have had this issue since it was introduced in valkey-io#1949. Signed-off-by: Binbin <binloveplay1314@qq.com>
enjoy-binbin
added a commit
that referenced
this pull request
Apr 8, 2026
In valkey.conf, slot-migration-max-failover-repl-bytes allows setting to -1 to disable the limit. ``` Setting this to -1 will disable this limit ``` But slot-migration-max-failover-repl-bytes is defined as MEMORY_CONFIG and memtoull() rejects negative inputs, making it impossible to set the value to -1 via config file or CONFIG SET. ``` >>> 'slot-migration-max-failover-repl-bytes "-1"' argument must be a memory value ``` Introduce SIGNED_MEMORY_CONFIG flag for memory configs that also accept plain negative number. When memtoull() fails and this flag is set, fall back to string2ll() for parsing. Use ll2string() for CONFIG GET and rewriteConfigNumericalOption() for CONFIG REWRITE when the value is negative. Add a serverAssert in initConfigValues() to enforce that PERCENT_CONFIG and SIGNED_MEMORY_CONFIG are never combined on the same config, since both use negative values with different semantics. This means we have had this issue since it was introduced in #1949. Signed-off-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Viktor SΓΆderqvist <viktor.soderqvist@est.tech>
sarthakaggarwal97
pushed a commit
to sarthakaggarwal97/valkey
that referenced
this pull request
Apr 16, 2026
β¦y-io#3443) In valkey.conf, slot-migration-max-failover-repl-bytes allows setting to -1 to disable the limit. ``` Setting this to -1 will disable this limit ``` But slot-migration-max-failover-repl-bytes is defined as MEMORY_CONFIG and memtoull() rejects negative inputs, making it impossible to set the value to -1 via config file or CONFIG SET. ``` >>> 'slot-migration-max-failover-repl-bytes "-1"' argument must be a memory value ``` Introduce SIGNED_MEMORY_CONFIG flag for memory configs that also accept plain negative number. When memtoull() fails and this flag is set, fall back to string2ll() for parsing. Use ll2string() for CONFIG GET and rewriteConfigNumericalOption() for CONFIG REWRITE when the value is negative. Add a serverAssert in initConfigValues() to enforce that PERCENT_CONFIG and SIGNED_MEMORY_CONFIG are never combined on the same config, since both use negative values with different semantics. This means we have had this issue since it was introduced in valkey-io#1949. Signed-off-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Viktor SΓΆderqvist <viktor.soderqvist@est.tech>
madolson
pushed a commit
that referenced
this pull request
Apr 27, 2026
In valkey.conf, slot-migration-max-failover-repl-bytes allows setting to -1 to disable the limit. ``` Setting this to -1 will disable this limit ``` But slot-migration-max-failover-repl-bytes is defined as MEMORY_CONFIG and memtoull() rejects negative inputs, making it impossible to set the value to -1 via config file or CONFIG SET. ``` >>> 'slot-migration-max-failover-repl-bytes "-1"' argument must be a memory value ``` Introduce SIGNED_MEMORY_CONFIG flag for memory configs that also accept plain negative number. When memtoull() fails and this flag is set, fall back to string2ll() for parsing. Use ll2string() for CONFIG GET and rewriteConfigNumericalOption() for CONFIG REWRITE when the value is negative. Add a serverAssert in initConfigValues() to enforce that PERCENT_CONFIG and SIGNED_MEMORY_CONFIG are never combined on the same config, since both use negative values with different semantics. This means we have had this issue since it was introduced in #1949. Signed-off-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Viktor SΓΆderqvist <viktor.soderqvist@est.tech>
This was referenced May 3, 2026
[Shadow] Fix slot-migration-max-failover-repl-bytes unable to accept -1
sarthakaggarwal97/valkey#150
Closed
sarthakaggarwal97
pushed a commit
to sarthakaggarwal97/valkey
that referenced
this pull request
May 7, 2026
β¦y-io#3443) In valkey.conf, slot-migration-max-failover-repl-bytes allows setting to -1 to disable the limit. ``` Setting this to -1 will disable this limit ``` But slot-migration-max-failover-repl-bytes is defined as MEMORY_CONFIG and memtoull() rejects negative inputs, making it impossible to set the value to -1 via config file or CONFIG SET. ``` >>> 'slot-migration-max-failover-repl-bytes "-1"' argument must be a memory value ``` Introduce SIGNED_MEMORY_CONFIG flag for memory configs that also accept plain negative number. When memtoull() fails and this flag is set, fall back to string2ll() for parsing. Use ll2string() for CONFIG GET and rewriteConfigNumericalOption() for CONFIG REWRITE when the value is negative. Add a serverAssert in initConfigValues() to enforce that PERCENT_CONFIG and SIGNED_MEMORY_CONFIG are never combined on the same config, since both use negative values with different semantics. This means we have had this issue since it was introduced in valkey-io#1949. Signed-off-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Viktor SΓΆderqvist <viktor.soderqvist@est.tech>
12 tasks done
valkeyrie-ops Bot
pushed a commit
that referenced
this pull request
May 18, 2026
In valkey.conf, slot-migration-max-failover-repl-bytes allows setting to -1 to disable the limit. ``` Setting this to -1 will disable this limit ``` But slot-migration-max-failover-repl-bytes is defined as MEMORY_CONFIG and memtoull() rejects negative inputs, making it impossible to set the value to -1 via config file or CONFIG SET. ``` >>> 'slot-migration-max-failover-repl-bytes "-1"' argument must be a memory value ``` Introduce SIGNED_MEMORY_CONFIG flag for memory configs that also accept plain negative number. When memtoull() fails and this flag is set, fall back to string2ll() for parsing. Use ll2string() for CONFIG GET and rewriteConfigNumericalOption() for CONFIG REWRITE when the value is negative. Add a serverAssert in initConfigValues() to enforce that PERCENT_CONFIG and SIGNED_MEMORY_CONFIG are never combined on the same config, since both use negative values with different semantics. This means we have had this issue since it was introduced in #1949. Signed-off-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Viktor SΓΆderqvist <viktor.soderqvist@est.tech>
Merged
1 task done
This was referenced Oct 16, 2026
This was referenced Oct 25, 2026
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.
Introduces a new family of commands for migrating slots via replication. The procedure is driven by the source node which pushes an AOF formatted snapshot of the slots to the target, followed by a replication stream of changes on that slot (a la manual failover).
This solution is an adaptation of the solution provided by @enjoy-binbin, combined with the solution I previously posted at #1591, modified to meet the designs we had outlined in #23.
New commands
CLUSTER MIGRATESLOTS SLOTSRANGE start end [start end]... NODE node-id: Begin sending the slot via replication to the target. Multiple targets can be specified by repeatingSLOTSRANGE ... NODE ...CLUSTER CANCELMIGRATION ALL: Cancel all slot migrationsCLUSTER GETSLOTMIGRATIONS: See a recent log of migrationsThis PR only implements "one shot" semantics with an asynchronous model. Later, "two phase" (e.g. slot level replicate/failover commands) can be added with the same core.
Slot migration jobs
Introduces the concept of a slot migration job. While active, a job tracks a connection created by the source to the target over which the contents of the slots are sent. This connection is used for control messages as well as replicated slot data. Each job is given a 40 character random name to help uniquely identify it.
All jobs, including those that finished recently, can be observed using the
CLUSTER GETSLOTMIGRATIONScommand.Replication
CLUSTER SYNCSLOTSTo coordinate the state machine transitions across the two nodes, a new command is added,
CLUSTER SYNCSLOTS, that performs this control flow.Each end of the slot migration connection is expected to install a read handler in order to handle
CLUSTER SYNCSLOTScommands:ESTABLISH: Begins a slot migration. Provides slot migration information to the target and authorizes the connection to write to unowned slots.SNAPSHOT-EOF: appended to the end of the snapshot to signal that the snapshot is done being written to the target.PAUSE: informs the source node to pause whenever it gets the opportunityPAUSED: added to the end of the client output buffer when the pause is performed. The pause is only performed after the buffer shrinks below a configurable sizeREQUEST-FAILOVER: request the source to either grant or deny a failover for the slot migration. The grant is only granted if the target is still paused. Once a failover is granted, the paused is refreshed for a short durationFAILOVER-GRANTED: sent to the target to inform that REQUEST-FAILOVER is grantedACK: heartbeat command used to ensure livenessInteractions with other commands
Error handling
repl-timeout, the connection will be dropped, resulting in migration failureState machine
Closes #23.
Co-authored-by: Binbin binloveplay1314@qq.com