Theory / 10 min
Client Connection Lifecycle
A Redis client is not just a socket. It is a bundle of state that lets the server transform a raw byte stream into ordered command execution and ordered replies.
The basic lifecycle looks like this:
accept -> read -> parse -> dispatch -> buffer replies -> write -> close
That path is simple, but every serious Redis feature eventually touches it. Transactions add a per-client queue. Pub/Sub changes what commands are allowed. Replication creates special clients with different output-buffer rules. Authentication and selected databases live on the connection. A client object becomes the server's memory of an ongoing conversation.
The Client As A State Container
A small client representation might begin like this:
type Client = {
fd: number;
input: Buffer;
output: Buffer[];
selectedDb: number;
flags: Set<string>;
};
The input buffer exists because TCP can deliver partial or multiple commands in one read. The output buffer exists because the socket may not accept all reply bytes at once. The selected database exists because the same command should resolve keys against the right keyspace. Flags exist because a normal client, a subscribed client, a replica, and a transaction-building client do not behave identically.
Reads Do Not Equal Commands
Imagine a client sends:
GET name
GET age
The server might receive both commands in one read. It might receive the first command split across several reads. It might receive the first command and half of the second. None of these cases should change semantics.
That is why the read path is not "read a command." It is:
append bytes to input buffer
parse as many complete frames as possible
leave incomplete trailing bytes in place
The connection is where stream-oriented network reality is reconciled with command-oriented database semantics.
Writes Are Also Partial
A command handler can produce a complete reply, but the operating system does not promise to write all of it to the socket immediately. The socket may accept the first 200 bytes of a 1,000-byte response and ask the process to try again later.
So replies are queued:
handler reply -> client output buffer -> event loop writes when socket is writable
This is not merely an implementation detail. Output buffers are part of Redis' memory story. A slow client can accumulate replies faster than it reads them. Pub/Sub clients can receive a flood of pushed messages. Replicas can lag behind the primary's write stream. If output buffers grow without limits, one connection can threaten the whole server.
Client Modes Matter
The same socket can move through different semantic modes.
In ordinary mode, the client sends a command and receives a reply. In transaction mode after MULTI, most commands are queued and return QUEUED until EXEC. In Pub/Sub mode, the client mostly receives pushed messages and can issue only a restricted set of commands. A replica connection receives a replication stream instead of acting like a normal application client.
These modes are why command dispatch must inspect client state. The meaning of an incoming command is not determined only by its name; it is also determined by the conversation around it.
Closing Cleanly
Closing a client is more than closing a file descriptor. The server must remove the client from the event loop, free its buffers, clear transaction state, remove Pub/Sub subscriptions, detach replication state, and release any other references.
Leaking a client from one of those side structures creates strange behavior: messages sent to dead subscribers, memory held by abandoned buffers, or stale state influencing future operations.
The connection lifecycle is the server's nervous system. It turns unreliable timing into predictable command order, and it gives higher-level features a place to attach their state.
See what actually stuck.
Take the practice scenarios now.