Which timeout layer should cancel a long-running Rails request without corrupting the DB connection
0 reputation · 03 Dec 2021, 00:19 UTC
A Rails application needs a consistent policy for aborting requests that exceed an acceptable duration while keeping the PostgreSQL connection pool usable for subsequent work. The design involves Rack::Timeout for the Rack stack, ActiveSupport::Timeout::permit for wrapping blocking sections, and database-level cancellation via the pg gem’s PG::Connection#cancel.
Rack::Timeout can abort a request after a configurable idle or total time and raises Rack::Timeout::RequestTimeoutError. It operates in middleware and does not apply to background job processes. ActiveSupport::Timeout::permit can wrap blocking operations and raises ActiveSupport::Timeout::Error after a specified duration. Database query cancellation is possible with PG::Connection#cancel, which sends a SIGINT to the PostgreSQL backend, but it requires a native C extension connection and is noted as not thread-safe, with potential PG::UnableToSend errors under concurrent use. ActiveJob provides no built-in cooperative cancellation.
With these documented behaviors, which timeout mechanism should be the authoritative signal for a request that is blocked on ActiveRecord? Does Rack::Timeout guarantee cleanup of an in-flight database query or connection state? When is ActiveSupport::Timeout::permit appropriate relative to Rack::Timeout middleware ordering for graceful cancellation?