sidekiq-transaction_guard 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/VERSION +1 -1
- data/lib/sidekiq/transaction_guard.rb +42 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c0d2035a276a481304f3b833e10119704d05583e63eda918a34da4a83823ab1
|
|
4
|
+
data.tar.gz: beb93e6793e86312f448d536eb4ba51b4b0b5290c5b45ebfe2a5916e37a00b5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1de9763227c618c2ecd3e4c7af601f20db01b388eac1632678ac3521f4b1d7baa93669de29e00d8cfe718a4ef9502967c593e17e4b8fec47f46804b2b9453de2
|
|
7
|
+
data.tar.gz: 97732ce309894eafc91fa5e4e2f1ff89cee2d1aeb8ed33da0e6e9496e3c635fe724b28c449c7dff54e3d3d16c1568d8b8c64f961466782b5d2643139a98d8594
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 1.1.3
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Transactions opened by the connection pool pinning machinery used by transactional tests in Rails 7.2+ are no longer counted as application transactions. Rails 7.2 pins a single connection wrapped in a non-joinable transaction and shares it with every thread, but the transaction level baseline captured by the test integrations is stored per thread. Threads other than the test runner thread (e.g. Capybara server threads, ActionCable workers, or inline Sidekiq jobs in system tests) had no baseline, so every job they enqueued was falsely flagged as inside a transaction. The allowed transaction level is now derived from the pool's pinned connection state as well, which is visible from all threads. Transactions opened by application code, including non-joinable ones, are still counted. The pinned connection state is only consulted once a test integration has started a testing context, so pinning a connection outside of tests does not bypass the guard.
|
|
12
|
+
|
|
7
13
|
## 1.1.2
|
|
8
14
|
|
|
9
15
|
### Fixed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.1.
|
|
1
|
+
1.1.3
|
|
@@ -15,6 +15,7 @@ module Sidekiq
|
|
|
15
15
|
@connection_classes = Set.new
|
|
16
16
|
@notify = nil
|
|
17
17
|
@mode = :warn
|
|
18
|
+
@testing_context = false
|
|
18
19
|
|
|
19
20
|
class << self
|
|
20
21
|
VALID_MODES = [:warn, :stderr, :error, :disabled].freeze
|
|
@@ -134,11 +135,10 @@ module Sidekiq
|
|
|
134
135
|
def in_transaction?
|
|
135
136
|
connection_classes.any? do |connection_class|
|
|
136
137
|
connection = active_connection(connection_class)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
end
|
|
138
|
+
next false unless connection
|
|
139
|
+
|
|
140
|
+
connection.open_transactions > allowed_transaction_level(connection_class) &&
|
|
141
|
+
application_transaction_open?(connection)
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
144
|
|
|
@@ -194,6 +194,7 @@ module Sidekiq
|
|
|
194
194
|
var = :sidekiq_rails_transaction_guard
|
|
195
195
|
saved_state = Thread.current[var]
|
|
196
196
|
Thread.current[var] = (saved_state ? saved_state.dup : {})
|
|
197
|
+
@testing_context = true
|
|
197
198
|
saved_state
|
|
198
199
|
end
|
|
199
200
|
|
|
@@ -242,6 +243,42 @@ module Sidekiq
|
|
|
242
243
|
(connection_counts && connection_counts[connection_class.name]) || 0
|
|
243
244
|
end
|
|
244
245
|
|
|
246
|
+
# Return true if the connection has any open transaction that was not opened by
|
|
247
|
+
# the connection pool pinning machinery (Rails 7.2+). Transactional tests pin a
|
|
248
|
+
# connection and wrap it in a non-joinable transaction that the pool then shares
|
|
249
|
+
# with every thread. Those wrapper transactions are test scaffolding, not
|
|
250
|
+
# application transactions, so they never count against the guard. The pin state
|
|
251
|
+
# is read from the pool rather than from a thread local so that threads without a
|
|
252
|
+
# baseline (e.g. web server threads in system tests) get the correct level.
|
|
253
|
+
def application_transaction_open?(connection)
|
|
254
|
+
return true unless @testing_context
|
|
255
|
+
return true unless connection.respond_to?(:pinned)
|
|
256
|
+
|
|
257
|
+
# The pool changes the pin state under the connection lock. Hold it so the
|
|
258
|
+
# transaction count and the pin state cannot be read from different states.
|
|
259
|
+
connection.lock.synchronize do
|
|
260
|
+
next true unless connection.pinned
|
|
261
|
+
|
|
262
|
+
open_transactions = connection.open_transactions
|
|
263
|
+
open_transactions > pinned_transaction_count(connection, open_transactions)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Return the number of open transactions on a pinned connection that belong to
|
|
268
|
+
# the pinning machinery. The pool counts pins rather than transactions, so the
|
|
269
|
+
# count is only trusted as far as the connection state supports it: it can never
|
|
270
|
+
# exceed the open transactions, and if it accounts for all of them then the
|
|
271
|
+
# innermost one must be non-joinable, since the wrappers always are. Anything
|
|
272
|
+
# else means the wrappers were closed outside of the pinning machinery and
|
|
273
|
+
# nothing should be discounted.
|
|
274
|
+
def pinned_transaction_count(connection, open_transactions)
|
|
275
|
+
depth = connection.pool.instance_variable_get(:@pinned_connections_depth).to_i
|
|
276
|
+
depth = open_transactions if depth > open_transactions
|
|
277
|
+
return 0 if depth == open_transactions && connection.current_transaction.joinable?
|
|
278
|
+
|
|
279
|
+
depth
|
|
280
|
+
end
|
|
281
|
+
|
|
245
282
|
def add_client_middleware(config)
|
|
246
283
|
config.client_middleware do |chain|
|
|
247
284
|
unless chain.exists?(Sidekiq::TransactionGuard::Middleware)
|