MigrateConnection moves the connection to another worker when reducing the number of workers. To make it simple, we do not migrate connections that are blocked on a key or stream, or that are paused (CLIENT PAUSE). Such connections stay on the worker being shut down and will be closed when it stops.
| 391 | // or stream, or that are paused (CLIENT PAUSE). Such connections stay on the |
| 392 | // worker being shut down and will be closed when it stops. |
| 393 | void Worker::MigrateConnection(Worker *target, redis::Connection *conn) { |
| 394 | if (!target || !conn) return; |
| 395 | |
| 396 | auto bev = conn->GetBufferEvent(); |
| 397 | // disable read/write event to prevent the connection from being processed during migration |
| 398 | bufferevent_disable(bev, EV_READ | EV_WRITE); |
| 399 | // We cannot migrate the connection if it has a running command |
| 400 | // since it will cause data race since the old worker may still process the command. |
| 401 | if (!conn->CanMigrate()) { |
| 402 | bufferevent_enable(bev, EV_READ | EV_WRITE); |
| 403 | return; |
| 404 | } |
| 405 | // Paused connections are not migrated; they will be closed when the worker stops. |
| 406 | // Only re-enable WRITE here; READ must remain disabled to preserve the paused state. |
| 407 | if (conn->IsPaused()) { |
| 408 | bufferevent_enable(bev, EV_WRITE); |
| 409 | return; |
| 410 | } |
| 411 | |
| 412 | // remove the connection from current worker |
| 413 | DetachConnection(conn); |
| 414 | if (!target->AddConnection(conn).IsOK()) { |
| 415 | conn->Close(); |
| 416 | return; |
| 417 | } |
| 418 | bufferevent_base_set(target->base_, bev); |
| 419 | conn->SetCB(bev); |
| 420 | // SetOwner before bufferevent_enable so callbacks see the correct owner. |
| 421 | conn->SetOwner(target); |
| 422 | bufferevent_enable(bev, EV_READ | EV_WRITE); |
| 423 | } |
| 424 | |
| 425 | void Worker::DetachConnection(redis::Connection *conn) { |
| 426 | if (!conn) return; |
no test coverage detected