Rails 7.2 Prevents Job Scheduling Within Transactions.

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:

User.transaction do
  user.update(confirmed: true)
  UserNotificationJob.perform_later(user)
end

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.

class UserNotificationJob < ApplicationJob
  self.enqueue_after_transaction_commit = :never # or `:always` or `:default`
end
  • :always forces the job to be deferred.
  • :never forces the job to be queued immediately.
  • :default let the queue adapter define the behavior .

Need help on your Ruby on Rails or React project?

Join Our Newsletter