In Rails applications, it is common to perform actions that depend on the successful completion of database transactions. For instance, sending a notification after a record is updated or triggering a background job.
Before
When jobs are enqued within a transaction there’s a risk they might be executed before the transaction is fully committed.
If the transaction is rolled back due to an error or other reasons, the job might still execute, even though the data it relies on was never committed to the database.
This could lead to errors such as ActiveJob::DeserializationError
or RecordNotFound
.
Consider a scenario where we confirm a user and want to send a notification afterwards:
This code might work in development but can fail in production due to timing issues with the database and job queue.
If UserNotificationJob
job runs before the transaction commits, it might fail because the user’s state hasn’t been persisted yet.
After
In Rails7.2, ActiveJob now delays job enqueuing until the transaction is committed and drops the job if the transaction is rolled back.
This prevents common errors such as ActiveJob::DeserializationError
or RecordNotFound
caused by jobs executing before the transaction commits.
Queue systems like Sidekiq
or Resque
can override the default job enqueuing behavior to fit their needs.
We also have the option to disable or enforce this behavior on a per-job basis.
:always
forces the job to be deferred.:never
forces the job to be queued immediately.:default
let the queue adapter define the behavior .