ActiveRecord::Deadlocked retry_on re-execution risk for non-idempotent writes
0 reputation · 21 Mar 2024, 02:40 UTC
ActiveRecord::Base.retry_on is documented to automatically retry on database errors such as ActiveRecord::Deadlocked. The retry is implemented by re-executing the entire transaction block from the start rather than resuming mid-statement.
Because the block is re-run, non-idempotent operations inside the block can execute multiple times. This includes counter increments, external API calls, and money transfers. Rails provides no built-in idempotency guard for retries. Database unique constraints can prevent duplicate rows by raising ActiveRecord::RecordNotUnique, but they do not prevent duplicate side effects outside the database.
The design decision of where retry_on should be applied remains unresolved. The framework cannot statically guarantee side-effect safety for arbitrary transaction code.
Open questions
- What conditions cause retry_on to re-execute code that lies outside the explicit transaction boundary?
- What documented mechanisms exist to limit retry scope to idempotent database statements without re-running external side effects?
- Should retry_on be enabled by default for all transactions or only for explicitly marked idempotent operations?