Skip to content

Instantly share code, notes, and snippets.

@sj26
Created January 24, 2018 02:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sj26/fca6e25ea902f36c87bc57bbc8a3c6e2 to your computer and use it in GitHub Desktop.
Save sj26/fca6e25ea902f36c87bc57bbc8a3c6e2 to your computer and use it in GitHub Desktop.
# Weak ACID semantics for Sidekiq jobs
#
# Include this module in a Sidekiq::Worker class and it will defer enqueuing
# jobs for that worker within an open ActiveRecord transaction until it is
# committed. Rollbacks will prevent any jobs being enqueued at all.
#
# ActiveRecord keeps track of all records which are being utilized within a
# transaction so it can fulfil commit and rollback callbacks. We create a fake
# record that quacks the same way for the same hooks.
#
# Inspired by: http://blog.arkency.com/2015/10/run-it-in-background-job-after-commit/
#
module Sidekiq::AfterTransaction
extend ActiveSupport::Concern
class Error < RuntimeError; end
class PushError < Error; end
module ClassMethods
def client_push(*args)
if ActiveRecord::Base.connection.transaction_open?
ActiveRecord::Base.connection.current_transaction.add_record(Record.new(self, *args))
else
super(*args)
end
end
end
class Record
def initialize(klass, *args)
@klass = klass
@args = args
end
delegate :logger, to: :Sidekiq
def has_transaction_callbacks?
true
end
def before_committed!(*)
end
def committed!(*)
logger.debug("[Sidekiq::AfterTransaction]: Commit, pushing Sidekiq job: #{@klass.name}.client_push(#{@args.inspect[1...-1]})")
@klass.client_push(*@args)
rescue
raise PushError.new("Transaction committed but Sidekiq push failed")
end
def rolledback!(*)
logger.debug "[Sidekiq::AfterTransaction]: Rollback, discarding Sidekiq job: #{@klass.name}.client_push(#{@args.inspect[1...-1]})"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment