UDP datagrams
Add request identifiers
Give UDP requests unique IDs so replies, retries, and duplicates can be matched safely.
10 minute lesson
Once a UDP client retries, a new problem appears: duplicates. A retry may cause the receiver to process the same logical request more than once. The original datagram might not be lost — just slow. Now both copies arrive, and if the request was “add 10 credits”, you added twenty.
A request ID lets both sides recognize the operation. The client generates a unique ID per logical request and reuses the same ID on every retry of it. Replies carry the ID back, so the client can match replies to requests. The server remembers recently handled IDs so it can deduplicate.
Send a small JSON datagram:
const request = { id: crypto.randomUUID(), type: 'status' }
socket.send(JSON.stringify(request), 41234, '127.0.0.1')
crypto.randomUUID() gives 122 random bits, so collisions are not a practical concern. Sequence numbers work too, but need care to stay unique across client restarts.
On the server, return the same ID in the reply, and cache what you already handled:
const seen = new Map() // id -> { reply, at }
socket.on('message', (message, remote) => {
const request = JSON.parse(message) // wrap in try/catch as in the framing module
const cached = seen.get(request.id)
if (cached) {
return socket.send(cached.reply, remote.port, remote.address)
}
const reply = JSON.stringify({ id: request.id, ok: true })
seen.set(request.id, { reply, at: Date.now() })
socket.send(reply, remote.port, remote.address)
})
A duplicate now receives the cached reply instead of a second execution. This makes retries safe even when the operation itself isn’t naturally repeatable.
Have the server cache recent completed IDs for a bounded time — a few retry windows is enough. Sweep entries older than, say, 60 seconds. An unbounded map is a slow memory leak that an attacker can accelerate by flooding you with fresh IDs.
Verify
Retry after a deadline: make the client resend the identical request object after 500 ms, then read both logs. The server should show one “executed” and one “answered from cache”. The client should tolerate receiving two identical replies and treat the second as noise — match by ID, act once.
An ID supports deduplication but does not authenticate the sender. A forged datagram can carry any ID it likes. Validate and authorize separately.
Lesson completed