AsyncResponse timeout vs. manual watchdog for long-running requests
0 reputation · 01 Aug 2025, 05:06 UTC
0 reputation · 01 Aug 2025, 05:06 UTC
The native approach relies on setting a server-side deadline via the AsyncResponse object. However, the underlying Jetty container does not always reliably trigger AsyncResponse.cancel() when a remote client aborts the connection. This can lead to orphaned background tasks continuing to consume resources even after the client has disconnected.
The alternative involves wrapping the work in a managed ExecutorService and using a separate ScheduledExecutorService to poll the request state or force cancellation. This bypasses the container-managed timeout semantics to provide more granular control over the lifecycle of the background operation.
34775 reputation · 01 Aug 2025, 15:44 UTC
No. AsyncResponse.setTimeout() does not guarantee resource cleanup when a client socket closes early. It registers a server-side deadline and invokes the TimeoutHandler only after that deadline expires; it is not a disconnect detector and it does not interrupt the application thread running the long operation. A manual watchdog is not automatically more effective under high concurrency. It provides more deterministic cleanup only when the background task can be cooperatively cancelled—by polling isCancelled() or responding to Thread.interrupt()—and when per-request scheduled tasks are properly shut down.
setTimeout() is a server-side deadline, not a client-abort signal.AsyncResponse.cancel() immediately when the remote client drops the connection.isCancelled() inside the long-running task and release executor, database, or socket resources itself.A ScheduledExecutorService watchdog that calls cancel() and interrupts the worker is useful when you need finer-grained control than the container timeout provides, or when you cannot rely on Jetty to notify the application of a premature close. The trade-off is overhead: one scheduled task per request can pressure the scheduler under high concurrency, and an unguarded cancel() after the response already completed can throw or be ignored.
setTimeout() as the outer safety net so the container eventually reclaims the async context.ExecutorService and pass the AsyncResponse to the worker.asyncResponse.isCancelled() at each stage and return early if true, closing statements, streams, and HTTP clients.asyncResponse.cancel() and workerThread.interrupt(), then remove it in a finally block once the response completes.Can the background task cooperatively check isCancelled() and respond to Thread.interrupt()? This determines whether a watchdog is worth the added overhead or whether native timeout plus listener instrumentation is sufficient.
Run the request, close the client early, and check jstack <pid> to confirm the worker thread leaves RUNNABLE or WAITING and returns to baseline. Log isCancelled() at task start, at each cancellation check, and inside the TimeoutHandler. Add a Jetty AsyncListener to record onComplete, onTimeout, and onError so you can see whether premature client close reaches the container. Repeat with the exact production Dropwizard, Jersey, and Jetty versions, with and without the watchdog, and compare orphaned tasks against thread count. This behavior is version-sensitive; verify against your exact release before relying on it in production.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.