Connection reliability

Limit connections and work

Bound clients, input size, queued output, and per-message work to keep one peer from exhausting the service.

10 minute lesson

~~~

Timeouts solve only one resource problem. A server also needs limits on simultaneous clients, buffered bytes, message rate, and expensive operations. Without them, one aggressive peer — or one buggy legitimate one — takes the service down for everyone.

Start with the connection count. Track active sockets:

const clients = new Set()

server.on('connection', socket => {
  if (clients.size >= 100) return socket.destroy()
  clients.add(socket)
  socket.on('close', () => clients.delete(socket))
})

The Set gives you an exact live count. Above the cap, socket.destroy() drops the newcomer immediately: no protocol reply, no allocated state. Rejecting cheaply matters — an overload defense that does expensive work per rejection is itself an attack surface.

The close handler keeps the count honest. Every accepted socket must remove itself on every exit path, which is why the previous lesson insisted on cleaning up in close.

Verify it: open several lab clients (a shell loop of nc calls works), watch the count rise, close them, and confirm the set returns to zero after closure. If it doesn’t, you have a leak that will silently hit the cap in production days later.

The other budgets

Connection count is one axis. A single client inside its one allowed connection can still hurt you:

// bound the outgoing queue per client
if (socket.writableLength > 1_000_000) socket.destroy()

Three budgets to set deliberately. Input size: the maximum line or frame length from the framing module. Output size: socket.writableLength tells you how many bytes are queued for a client that reads slowly or not at all. Work rate: count messages per connection per second and throttle or disconnect past a ceiling.

Each limit needs a number, and the number should come from measurement — typical real usage times a safety factor — not from guessing.

Add metrics for rejected connections. Even one log line per rejection tells you whether the cap is doing routine protection, you’re under attack, or legitimate users are being turned away and the limit needs raising.

A process limit complements operating-system, firewall, and upstream controls; kernel backlog and file-descriptor limits sit underneath yours either way. Test failure behavior before production: hit the cap on purpose and confirm existing clients keep working while newcomers are refused.

Lesson completed

Take this course offline

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

Get the download library →