Build and test a protocol

Support concurrent clients

Keep connection state isolated and decide how shared data changes are serialized.

10 minute lesson

~~~

Node handles concurrent connections without threads: the event loop interleaves socket events, and each callback runs to completion. Node can maintain many sockets, but shared state can still create ordering races. The bugs aren’t corrupted memory — they’re logical: two clients touching shared data in an order you didn’t anticipate.

Two disciplines keep this manageable.

Isolate per-connection state

Everything belonging to one client — line buffer, auth state, timers — lives in the connection callback’s scope. Per-socket buffers and authentication must never leak between clients:

const server = net.createServer(socket => {
  let pending = ''            // this client's partial line
  let authenticated = false   // this client's auth state
  // handlers close over these, one set per connection
})

Name and observe each connection:

const id = crypto.randomUUID()
console.log({ event: 'connected', id, remote: socket.remoteAddress })
socket.on('close', () => console.log({ event: 'closed', id }))

Tag every log line with the connection id and interleaved logs from many clients become readable per client. Without it, debugging three simultaneous conversations from one log stream is guesswork.

Decide how shared writes are ordered

The key-value store is shared by design. A synchronous handler is safe: it runs to completion before the next event. The race appears the moment a handler awaits between reading and writing:

case 'increment': {
  const current = store.get(message.key) ?? 0
  await audit(message)                 // another client's handler can run here
  store.set(message.key, current + 1)  // writes a stale value
}

Two concurrent increments read the same current, and one update is lost. The fixes are ordinary: read after the await instead of before, or add a sequence or queue for shared writes if order matters — a per-key promise chain is a compact queue.

Verify with interleaving

Open three clients, interleave commands, and verify replies return to the correct socket. Set a key on client A, read it from B and C, and issue overlapping writes. Check that no reply lands on the wrong connection and that the logs show three distinct IDs, each with a clean connect-to-close lifecycle.

Connection IDs belong in logs, not as proof of user identity. The ID says “same TCP connection”, nothing more. Identity comes from authentication inside the protocol.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →