Search code examples
ruby-on-railsrubysidekiq

Why does my Sidekiq job fail from a model's after_commit code?


When I run this Sidekiq job, it successfully creates the Foo record. But because Foo's after_commit raises an error, the entire job fails and ends up in the dead job queue. If I fix the after_commit error and rerun the job, it will create another Foo record (or will do so on retry 😬), which is NOT desirable.

I expect PerformAndCreateFooJob to complete successfully after the Foo record is successfully created. The after_commit logic should not cause the job to fail.

  • Why is the after_commit considered part of this job?
  • Is there a way to decouple the after_commit code from the job?
  • If no way to decouple, is there a better pattern to employ?
class PerformAndCreateFooJob
  include Sidekiq::Job
  sidekiq_options retry: 0

  def perform = Foo.create
end
class Foo < ApplicationRecord
  after_commit -> () { raise }
end

dead job queue showing Perform and create foo job failed


Solution

  • Why is the after_commit considered part of this job?

    Because it's part of create (actually part of save), which you're calling.

    Is there a way to decouple the after_commit code from the job?

    By using insert to skip the callbacks as explained in the guides

    If no way to decouple, is there a better pattern to employ?

    Worth adding... it's best practice to ensure jobs are idempotent. There are many reasons a job can be retried, including your own app logic, but also other system conditions. In normal circumstances, this could be as simple as using a find_by first (assuming your job queuing logic is unlikely to create many jobs for the same record/data), or maybe needs a unique constraint in the DB and logic to rescue a failure there.