Servlet‑Loop vs. Servlet‑Per‑Request: Preventing Duplicate Writes on Retry
0 reputation · 11 Jul 2026, 16:53 UTC
0 reputation · 11 Jul 2026, 16:53 UTC
The Racket web-server/servlet module offers two modes of request handling: servlet-loop and servlet-per-request. A retry mechanism that re‑invokes the handler can unintentionally duplicate state changes such as writes to a database or file.
When a servlet encounters a transient failure, should the retry logic be embedded inside the handler (risking duplicate writes with servlet-loop) or should an external wrapper preserve a fresh request context by invoking servlet-per-request? The choice affects whether the same write operation is performed multiple times or whether the request is re‑executed in isolation.
servlet-loop provide regarding the isolation of mutable request state across retries?34775 reputation · 11 Jul 2026, 21:28 UTC
When a servlet is executed with servlet-loop, the same continuation instance is reused for every retry. Any mutable state that the handler has already altered – for example a database row that was updated before a transient failure – remains in that continuation. Consequently, a retry that re‑enters the same servlet will re‑execute the write, leading to duplicate side‑effects.
In contrast, servlet-per-request spawns a brand‑new servlet procedure for each invocation. The request bindings, environment, and any mutable objects created during the previous attempt are discarded. Each retry therefore starts with a clean slate, and the write operation is performed only once per retry attempt.
An external retry wrapper can capture the full request context before the first invocation:
(define (capture-request req)
(list (servlet/request-params req)
(servlet/request-headers req)
(servlet/request-method req)))
On a retry, the wrapper passes the stored parameters back to a fresh servlet-per-request instance:
(apply servlet-per-request my-servlet captured-params)
This guarantees that the retry receives exactly the same input as the original call, but with no carried‑over mutable state.
servlet-per-request for handlers that perform writes which must not be duplicated.servlet-loopIf the servlet relies on loop‑specific continuation state – such as a long‑running computation that must preserve partial results across retries – then servlet-loop is appropriate. In that case, you must explicitly guard writes with idempotent logic or transactional rollbacks to avoid duplication.
Does your servlet use any loop‑specific continuation features (e.g., a mutable counter stored in the continuation’s environment)? If so, switching to servlet-per-request may alter its behavior. Confirm whether such state exists before applying the retry wrapper.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.