Module: Sidekiq::TransactionGuard
- Defined in:
- lib/sidekiq/transaction_guard.rb,
lib/sidekiq/transaction_guard/rspec.rb,
lib/sidekiq/transaction_guard/railtie.rb,
lib/sidekiq/transaction_guard/version.rb,
lib/sidekiq/transaction_guard/minitest.rb,
lib/sidekiq/transaction_guard/middleware.rb,
lib/sidekiq/transaction_guard/database_cleaner.rb
Defined Under Namespace
Modules: DatabaseCleaner, MinitestHelper, RSpecIntegration Classes: InsideTransactionError, Middleware, Railtie
Constant Summary collapse
- VALID_MODES =
[:warn, :stderr, :error, :disabled].freeze
- VERSION =
File.read(File.("../../../../VERSION", __FILE__)).chomp.freeze
Class Method Summary collapse
-
.add_connection_class(connection_class) ⇒ void
Add a class that maintains its own connection pool to the connections being monitored for open transactions.
-
.begin_testing ⇒ Object
private
Start a testing context on the current thread without a block.
-
.connection_classes ⇒ Array<Class>
Return the classes that have been added via
add_connection_class. -
.default_mode ⇒ Symbol
Return the globally configured mode, ignoring any thread local override.
-
.disable { ... } ⇒ Object
Disable the transaction guard within the provided block.
-
.end_testing(saved_state) ⇒ void
private
End a testing context started with
begin_testing. -
.in_transaction? ⇒ Boolean
Return true if any connection is currently inside of a transaction.
-
.init(mode: nil) ⇒ void
Initialize Sidekiq::TransactionGuard by adding its client middleware to Sidekiq.
-
.mode ⇒ Symbol
Return the mode in effect for the current thread.
-
.mode=(symbol) ⇒ Symbol
Set the global mode to one of
[:warn, :stderr, :error, :disabled]. -
.notify {|Hash| ... } ⇒ void
Define the global notify block.
-
.notify_block ⇒ Proc?
Return the block set as the notify handler with a call to
notify. -
.set_allowed_transaction_level(connection_classes, base_transaction_level = 0) ⇒ void
This method needs to be called to set the allowed transaction level for a connection class (see
add_connection_classfor more info). -
.testing { ... } ⇒ Object
This method call needs to be wrapped around tests that use transactional fixtures.
-
.thread_local_mode ⇒ Symbol?
Return the mode override for the current thread, or nil if none is set.
-
.thread_local_mode=(symbol) ⇒ Symbol?
Set a mode override for the current thread only.
Class Method Details
.add_connection_class(connection_class) ⇒ void
This method returns an undefined value.
Add a class that maintains its own connection pool to the connections
being monitored for open transactions. You don't need to add ActiveRecord::Base
or subclasses. Only the base class that establishes a new connection pool
with a call to establish_connection needs to be added.
120 121 122 |
# File 'lib/sidekiq/transaction_guard.rb', line 120 def add_connection_class(connection_class) @lock.synchronize { @connection_classes << connection_class } end |
.begin_testing ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Start a testing context on the current thread without a block. This is used
by test framework integrations (like the Minitest helper) that cannot wrap
the entire test in a single block. Use testing instead whenever a block
can be used. The returned state must be passed to end_testing when the
test finishes.
This method only allocates the transaction tracking state; it does not
capture a transaction level baseline. The caller is responsible for calling
set_allowed_transaction_level once any setup that opens transactions
(e.g. transactional fixtures) has run.
193 194 195 196 197 198 |
# File 'lib/sidekiq/transaction_guard.rb', line 193 def begin_testing var = :sidekiq_rails_transaction_guard saved_state = Thread.current[var] Thread.current[var] = (saved_state ? saved_state.dup : {}) saved_state end |
.connection_classes ⇒ Array<Class>
Return the classes that have been added via add_connection_class.
127 128 129 |
# File 'lib/sidekiq/transaction_guard.rb', line 127 def connection_classes ([ActiveRecord::Base] + @lock.synchronize { @connection_classes.to_a }).uniq end |
.default_mode ⇒ Symbol
Return the globally configured mode, ignoring any thread local override.
70 71 72 |
# File 'lib/sidekiq/transaction_guard.rb', line 70 def default_mode @lock.synchronize { @mode } end |
.disable { ... } ⇒ Object
Disable the transaction guard within the provided block. This is useful in test environments when you want to setup data for your tests without worrying about transaction levels.
The guard is only disabled for the current thread so that jobs enqueued concurrently in other threads are still checked.
153 154 155 156 157 158 159 160 161 |
# File 'lib/sidekiq/transaction_guard.rb', line 153 def disable save_mode = thread_local_mode begin self.thread_local_mode = :disabled yield ensure self.thread_local_mode = save_mode end end |
.end_testing(saved_state) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
End a testing context started with begin_testing.
205 206 207 |
# File 'lib/sidekiq/transaction_guard.rb', line 205 def end_testing(saved_state) Thread.current[:sidekiq_rails_transaction_guard] = saved_state end |
.in_transaction? ⇒ Boolean
Return true if any connection is currently inside of a transaction.
134 135 136 137 138 139 140 141 142 143 |
# File 'lib/sidekiq/transaction_guard.rb', line 134 def in_transaction? connection_classes.any? do |connection_class| connection = active_connection(connection_class) if connection connection.open_transactions > allowed_transaction_level(connection_class) else false end end end |
.init(mode: nil) ⇒ void
This method returns an undefined value.
Initialize Sidekiq::TransactionGuard by adding its client middleware to Sidekiq. The middleware is added to both the client and server configurations since jobs can also be enqueued from within other jobs running in the Sidekiq server process.
29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/sidekiq/transaction_guard.rb', line 29 def init(mode: nil) self.mode = mode if mode Sidekiq.configure_client do |config| add_client_middleware(config) end Sidekiq.configure_server do |config| add_client_middleware(config) end end |
.mode ⇒ Symbol
Return the mode in effect for the current thread. This is the thread local mode if one has been set, otherwise the global mode.
63 64 65 |
# File 'lib/sidekiq/transaction_guard.rb', line 63 def mode thread_local_mode || default_mode end |
.mode=(symbol) ⇒ Symbol
Set the global mode to one of [:warn, :stderr, :error, :disabled]. The
default mode is :warn. This controls the behavior of workers enqueued
inside of transactions.
- :warn - Log to Sidekiq.logger
- :stderr - Log to STDERR
- :error - Raise a
Sidekiq::TransactionGuard::InsideTransactionError - :disabled - Allow workers inside of transactions
51 52 53 54 55 56 57 |
# File 'lib/sidekiq/transaction_guard.rb', line 51 def mode=(symbol) if VALID_MODES.include?(symbol) @lock.synchronize { @mode = symbol } else raise ArgumentError.new("mode must be one of #{VALID_MODES.inspect}") end end |
.notify {|Hash| ... } ⇒ void
This method returns an undefined value.
Define the global notify block. This block will be called with a Sidekiq
job hash for all jobs enqueued inside transactions if the mode is :warn
or :stderr.
102 103 104 |
# File 'lib/sidekiq/transaction_guard.rb', line 102 def notify(&block) @lock.synchronize { @notify = block } end |
.notify_block ⇒ Proc?
Return the block set as the notify handler with a call to notify.
109 110 111 |
# File 'lib/sidekiq/transaction_guard.rb', line 109 def notify_block @lock.synchronize { @notify } end |
.set_allowed_transaction_level(connection_classes, base_transaction_level = 0) ⇒ void
This method returns an undefined value.
This method needs to be called to set the allowed transaction level for a connection
class (see add_connection_class for more info). The current transaction level
for that class's connection will be set as the zero point. This method can only
be called inside a block wrapped with the testing method.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/sidekiq/transaction_guard.rb', line 221 def set_allowed_transaction_level(connection_classes, base_transaction_level = 0) connection_counts = Thread.current[:sidekiq_rails_transaction_guard] unless connection_counts raise("set_allowed_transaction_level is only allowed inside a testing block") end connection_classes = self.connection_classes if connection_classes == :all Array(connection_classes).each do |connection_class| class_count = begin lease_connection(connection_class).open_transactions + base_transaction_level rescue ActiveRecord::ConnectionNotEstablished base_transaction_level end connection_counts[connection_class.name] = class_count end end |
.testing { ... } ⇒ Object
This method call needs to be wrapped around tests that use transactional fixtures. It sets up data structures used to track the number of open transactions. The current transaction level is automatically captured as the baseline so that any transactions opened by test setup (e.g. transactional fixtures) are ignored.
170 171 172 173 174 175 176 177 178 |
# File 'lib/sidekiq/transaction_guard.rb', line 170 def testing saved_state = begin_testing begin set_allowed_transaction_level(:all) yield ensure end_testing(saved_state) end end |
.thread_local_mode ⇒ Symbol?
Return the mode override for the current thread, or nil if none is set.
92 93 94 |
# File 'lib/sidekiq/transaction_guard.rb', line 92 def thread_local_mode Thread.current[:sidekiq_transaction_guard_mode] end |
.thread_local_mode=(symbol) ⇒ Symbol?
Set a mode override for the current thread only. This is used by disable
and the test integrations so that changing the mode in one thread cannot
affect jobs being enqueued concurrently in other threads. Set to nil to
remove the override and fall back to the global mode.
81 82 83 84 85 86 87 |
# File 'lib/sidekiq/transaction_guard.rb', line 81 def thread_local_mode=(symbol) if symbol.nil? || VALID_MODES.include?(symbol) Thread.current[:sidekiq_transaction_guard_mode] = symbol else raise ArgumentError.new("mode must be one of #{VALID_MODES.inspect}") end end |