# CLUSTER MIGRATION

> Move slots to the receiving node in one atomic step, and list or cancel those migrations (the Redis 8 form).

URL: https://docs.hotkv.com/commands/cluster-migration/

```
CLUSTER MIGRATION IMPORT start-slot end-slot [start-slot end-slot ...]
CLUSTER MIGRATION STATUS ID task-id | ALL
CLUSTER MIGRATION CANCEL ID task-id | ALL
```

- Group: Cluster
- Complexity: O(N) where N is the number of keys in the slots (IMPORT), or the number of tasks (STATUS, CANCEL)
- HotKV Cloud: not available
- On-Premise: no license needed

`CLUSTER MIGRATION` is the Redis 8 way to move slots atomically. You send
`IMPORT` to the primary that is to **receive** the slots. It finds the node
that owns them, copies the keys and the AI feature data of those slots, follows the
writes made meanwhile, and takes the slots over in one step. Clients never see
a half-moved slot, so there are no `ASK` redirects and no per-key `MIGRATE`.
The command replies with a task ID at once and the move runs in the
background. `STATUS` shows a task and `CANCEL` ends one.

`redis-cli --cluster reshard` and `rebalance` of Redis 8.4 and later use this
command by themselves when every node of the cluster reports `redis_version`
8.4.0 or later (see `compat-redis-version`, default `8.4.0`).
[Scaling](https://docs.hotkv.com/clustering/scaling/index.md) compares it with the other ways of moving
slots. The same move can be started from the source with
[`CLUSTER MIGRATESLOTS`](https://docs.hotkv.com/commands/cluster-migrateslots/index.md).

## Options

| Form | Effect |
| --- | --- |
| `IMPORT start-slot end-slot ...` | Take the slots in the inclusive ranges from their current owner. Ranges can come in any order, and a slot can be named only once. Every slot must have an owner, the same owner for all of them, and not this node. |
| `STATUS ID task-id` | Show one task, whether it is running or finished. |
| `STATUS ALL` | Show every task this node has: those in progress, or failed and waiting for their next try, first, then the finished ones, newest first, up to `cluster-slot-migration-max-archived-tasks` (default `32`). |
| `CANCEL ID task-id` | Cancel one task that is in progress or failed. |
| `CANCEL ALL` | Cancel every such task. |

A node runs one `IMPORT` at a time.

To hand the slots over, the source holds client writes to them once the target
is within `cluster-slot-migration-handoff-max-lag-bytes` (default `1mb`) of the
writes the source has sent, for at most
`cluster-slot-migration-write-pause-timeout` (default `10000` milliseconds).
`write_pause_ms` reports how long that took.

A task that fails is not over: the target tries the import again every
second until it succeeds or you cancel it. `retries` counts the tries.

## Return value

`IMPORT` replies with the task ID, a bulk string of 40 hexadecimal characters,
in RESP2 and RESP3.

`STATUS` replies with an array of tasks. Each task is a map in RESP3 and a flat
array of field and value pairs in RESP2. `STATUS ID` of an ID that does not
exist replies with an empty array.

| Field | Type | Meaning |
| --- | --- | --- |
| `id` | Bulk string | The task ID |
| `slots` | Bulk string | The slots, as ranges separated by spaces |
| `source` | Bulk string | The node ID of the node that owns the slots |
| `dest` | Bulk string | The node ID of the node that receives them |
| `operation` | Bulk string | `import` on the target, `migrate` on the source |
| `state` | Bulk string | Where the task is (below) |
| `last_error` | Bulk string | The reason the task last failed or was cancelled, empty otherwise |
| `retries` | Integer | How many times the task has been tried again |
| `create_time` | Integer | When the task was created, Unix time in milliseconds |
| `start_time` | Integer | When the current try started, Unix time in milliseconds |
| `end_time` | Integer | When the task ended, Unix time in milliseconds, and `-1` while it has not |
| `write_pause_ms` | Integer | How long the source held writes to hand the slots over |

The states on the target are `none`, `connecting`, `auth-reply`,
`handshake-reply`, `syncslots-reply`, `init-rdbchannel`, `accumulate-buffer`
(the copy arrives), `streaming-buffer` and `wait-stream-eof` (the writes made
since the copy), and `takeover`. On the source they are `wait-rdbchannel`,
`wait-bgsave-start`, `send-bulk-and-stream` (the copy), `send-stream`,
`handoff` and `stream-eof`. A task ends as `completed`, `canceled` or
`failed`. `CLUSTER GETSLOTMIGRATIONS` shows the same tasks with different field
and state names.

`CANCEL` replies with an integer, the number of tasks it cancelled, which is `0`
when no task matched. A cancelled task has the state `canceled`, and
`last_error` says `Cancelled due to user request`. A task also ends as
cancelled when this node becomes a replica, when the slots change owner by
another command, when `CLUSTER FORGET` removes the other node, when
`CLIENT PAUSE` is used, and when the server shuts down. Cancelling a task on
the source with `CLUSTER CANCELSLOTMIGRATIONS` does not end it: the target
tries again.

Errors:

| Case | Error |
| --- | --- |
| No subcommand, a missing argument, an odd number of slots, or a task ID missing after `ID` | `ERR wrong number of arguments for 'cluster\|migration' command` |
| `STATUS` or `CANCEL` followed by something other than `ID` or `ALL` | `ERR unknown argument` |
| `IMPORT` on a replica | `ERR slot migration not allowed on replica.` |
| A slot is open by hand (`IMPORTING` or `MIGRATING`) on this node | `ERR all slot states must be STABLE to start a slot migration task.` |
| A slot outside 0 to 16383, or not a number | `ERR Invalid or out of range slot` |
| A range that starts after it ends | `ERR start slot number <start> is greater than end slot number <end>` |
| A slot named more than once | `ERR Slot <slot> specified multiple times` |
| A slot has no owner | `ERR slot has no owner: <slot>` |
| The slots have more than one owner | `ERR slots belong to different source nodes` |
| This node owns the slots | `ERR this node is already the owner of the slot range` |
| Another task is running on this node | `ERR an atomic slot migration task is still running here` |
| Cluster mode is off | `ERR This instance has cluster support disabled` |

## Examples

Import slots 100 to 199 into the primary on port 7003, which does not own them.
The task ends `completed` on the target, and the source reports the same task
as `migrate`:

```
127.0.0.1:7003> CLUSTER MIGRATION IMPORT 100 199
"0fdd612b88e9a3e41e32718ae4111f9b81e98128"
127.0.0.1:7003> CLUSTER MIGRATION STATUS ID 0fdd612b88e9a3e41e32718ae4111f9b81e98128
1)  1) "id"
    2) "0fdd612b88e9a3e41e32718ae4111f9b81e98128"
    3) "slots"
    4) "100-199"
    5) "source"
    6) "7318275656a439e2520d240886b8ed2cddb464fc"
    7) "dest"
    8) "66cf057e48f56b104d6abf3169c982e3f02ec4cc"
    9) "operation"
   10) "import"
   11) "state"
   12) "completed"
   13) "last_error"
   14) ""
   15) "retries"
   16) (integer) 0
   17) "create_time"
   18) (integer) 1791064444227
   19) "start_time"
   20) (integer) 1791064444228
   21) "end_time"
   22) (integer) 1791064444247
   23) "write_pause_ms"
   24) (integer) 0
127.0.0.1:7000> CLUSTER MIGRATION STATUS ID 0fdd612b88e9a3e41e32718ae4111f9b81e98128
1)  1) "id"
    2) "0fdd612b88e9a3e41e32718ae4111f9b81e98128"
    ...
   10) "migrate"
    ...
   24) (integer) 18
```

A task that was running when its export was cancelled on the source. It failed,
was tried again (`retries` is 1), and then you cancel it on the target:

```
127.0.0.1:7003> CLUSTER MIGRATION STATUS ID 9ec1175e94f56fd3e5082beb8011f2d1e3a2c0ab
1)  1) "id"
    2) "9ec1175e94f56fd3e5082beb8011f2d1e3a2c0ab"
    ...
   11) "state"
   12) "accumulate-buffer"
   13) "last_error"
   14) "Main channel - the link to the source dropped (state: accumulate-buffer, rdb_channel_state: rdbchannel-transfer)"
   15) "retries"
   16) (integer) 1
    ...
127.0.0.1:7003> CLUSTER MIGRATION CANCEL ID 9ec1175e94f56fd3e5082beb8011f2d1e3a2c0ab
(integer) 1
127.0.0.1:7003> CLUSTER MIGRATION STATUS ID 9ec1175e94f56fd3e5082beb8011f2d1e3a2c0ab
1)  1) "id"
    2) "9ec1175e94f56fd3e5082beb8011f2d1e3a2c0ab"
    ...
   11) "state"
   12) "canceled"
   13) "last_error"
   14) "Cancelled due to user request (state: accumulate-buffer, rdb_channel_state: rdbchannel-transfer)"
    ...
```

The same status as RESP3 maps:

```
$ redis-cli -3 --no-raw -p 7003 CLUSTER MIGRATION STATUS ALL
1)  1# "id" => "8077cb03457a205de36e93e964e8245133687196"
    2# "slots" => "150-199"
    3# "source" => "ea0c1bd255fc04ab7dfc3aabf682cb8f64f84cba"
    4# "dest" => "49a04a859cbe2f18b168fdff41f4f2346045f9e3"
    5# "operation" => "import"
    6# "state" => "completed"
    7# "last_error" => ""
    8# "retries" => (integer) 0
    9# "create_time" => (integer) 1791064214167
   10# "start_time" => (integer) 1791064214167
   11# "end_time" => (integer) 1791064214179
   12# "write_pause_ms" => (integer) 0
```

Refusals:

```
127.0.0.1:7003> CLUSTER MIGRATION IMPORT 100 199
(error) ERR this node is already the owner of the slot range
127.0.0.1:7002> CLUSTER MIGRATION IMPORT 5000 6000
(error) ERR slots belong to different source nodes
127.0.0.1:7004> CLUSTER MIGRATION IMPORT 500 599
(error) ERR slot migration not allowed on replica.
127.0.0.1:7003> CLUSTER MIGRATION IMPORT 500 500 500 500
(error) ERR Slot 500 specified multiple times
127.0.0.1:7005> CLUSTER MIGRATION IMPORT 0 10
(error) ERR slot has no owner: 0
127.0.0.1:7003> CLUSTER SETSLOT 700 IMPORTING 7318275656a439e2520d240886b8ed2cddb464fc
OK
127.0.0.1:7003> CLUSTER MIGRATION IMPORT 500 549
(error) ERR all slot states must be STABLE to start a slot migration task.
127.0.0.1:7003> CLUSTER MIGRATION IMPORT 600 649
(error) ERR an atomic slot migration task is still running here
127.0.0.1:7003> CLUSTER MIGRATION STATUS ID 0000000000000000000000000000000000000000
(empty array)
```

## Compatibility

The subcommands, replies, error texts, task states and the `retries` behaviour
follow Redis 8.4. In `last_error`, the text in parentheses gives the state of
the task's main channel and of its RDB channel, as Redis does. HotKV's own AI
data moves with the slots. `CLUSTER MIGRATION STATUS` also lists migrations
started with `CLUSTER MIGRATESLOTS`, and `CLUSTER GETSLOTMIGRATIONS` lists the
tasks started with `CLUSTER MIGRATION IMPORT`, each in the other command's
field names.

## See also

[CLUSTER MIGRATESLOTS](https://docs.hotkv.com/commands/cluster-migrateslots/index.md),
[CLUSTER GETSLOTMIGRATIONS](https://docs.hotkv.com/commands/cluster-getslotmigrations/index.md),
[CLUSTER CANCELSLOTMIGRATIONS](https://docs.hotkv.com/commands/cluster-cancelslotmigrations/index.md),
[CLUSTER SETSLOT](https://docs.hotkv.com/commands/cluster-setslot/index.md),
[Scaling a cluster](https://docs.hotkv.com/clustering/scaling/index.md)