isolator 1.1.2 → 1.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a37693d4626a6bb72e14c00562274a1002c557aca0122182f2422be08e225ad8
4
- data.tar.gz: ed49eb36314577217281389ea07d3dcb8c72c30373fd03f6b6609e2d43ea4d99
3
+ metadata.gz: 05b72beb85efc1ddfeb64209365e3b2326e346cce686a614349088a05406b2b7
4
+ data.tar.gz: 8679de3ba5f24b833054ee47a0515a8ce340fbb57880d6bf00dfa9d47ad57718
5
5
  SHA512:
6
- metadata.gz: e3c11b5f2da9fa95dc4f167b757e5a0dee3466ead28cb458839b1651b8e6db0631f72b9d1bcc0de239f41adb27b3dbd99aab46cf2d3872bcd462c75aa61aa471
7
- data.tar.gz: c9ea41ae4b214da35c3e5c8c53c75c5f8f3b74b620ff5e2f3d1dbfcbb52312f3620ba811829c7a46b82de87cb00420d74b55d1efc7b5a1cd47295ac4eca60afe
6
+ metadata.gz: 20351d43476587b537082c2e0f83a01813ec9f8f57971eedd5251bc27723b276cfe8b6c5e4c930169e309fe0144f117684393b1faabae29a890af6f482cd2224
7
+ data.tar.gz: 8f6ed452926f6f5e955537a5de95b2a67569153ab54a79ccb20b98fb37a775e0398f7e8be285dd39ab31750548271921047b4afd417b254a8bbfa0f600be84a4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.2.1 (2026-07-16)
6
+
7
+ - Fix handling "finish" events for untracked (lazy) transactions. ([@sawirricardo][])
8
+
9
+ ## 1.2.0 (2025-11-07)
10
+
11
+ - Use `ActiveRecord::Base.lease_connection` instead of `ActiveRecord::Base.connection`, if available. ([@viralpraxis][])
12
+
13
+ - Add ability to limit subtransaction amount. ([@dnamsons][])
14
+
15
+ This feature is disabled by default, opt-in via: `Isolator.config.max_subtransactions_depth = :desired_depth`.
16
+
5
17
  ## 1.1.2 (2025-10-16)
6
18
 
7
19
  - Fix `database_cleaner` support: require `base.rb` explicitly. ([@viralpraxis][])
@@ -161,3 +173,5 @@ This, for example, makes Isolator compatible with Rails multi-database apps.
161
173
  [@tagirahmad]: https://github.com/tagirahmad
162
174
  [@arthurWD]: https://github.com/arthurWD
163
175
  [@joshuay03]: https://github.com/joshuay03
176
+ [@viralpraxis]: https://github.com/viralpraxis
177
+ [@sawirricardo]: https://github.com/sawirricardo
data/README.md CHANGED
@@ -109,6 +109,9 @@ Isolator.configure do |config|
109
109
 
110
110
  # Turn on/off raising exceptions for simultaneous transactions to different databases
111
111
  config.disallow_per_thread_concurrent_transactions = false
112
+
113
+ # Limit the amount of allowed nested transactions
114
+ config.max_subtransactions_depth = 5
112
115
  end
113
116
  ```
114
117
 
@@ -332,6 +335,22 @@ The reason is that Rails started using a [separate connection pool for advisory
332
335
 
333
336
  To fix this disable migrations advisory locks by adding `advisory_locks: false` to your database configuration in `(spec|test)/internal/config/database.yml`.
334
337
 
338
+ ## ActionMailer jobs
339
+
340
+ Rails 7.2 added support for the `enqueue_after_transaction_commit` configuration option, which ensures that jobs are enqueued only after all transactions have been committed. One gotcha that may lead to Isolator offenses is that `ActionMailer::MailDeliveryJob` inherits directly from `ActiveJob::Base`, rather than from your application’s top-level `ApplicationJob`.
341
+
342
+ To fix this, make sure `enqueue_after_transaction_commit` is set to true on `ActiveJob::Base`, or use the global `config.active_job.enqueue_after_transaction_commit setting` if your Rails version supports it.
343
+
344
+ See https://github.com/palkan/isolator/issues/93 for details.
345
+
346
+ ## Occasional test failures related to connection reaping
347
+
348
+ In some cases, connections may be reaped and replaced mid-test, which will most likely lead to test failures because Isolator currently does not track this situation.
349
+
350
+ One way to mitigate this issue is to disable ActiveRecord's connection pool reaping by setting `reaping_frequency` to `nil`.
351
+
352
+ See https://github.com/palkan/isolator/issues/83 for details.
353
+
335
354
  ## Contributing
336
355
 
337
356
  Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/isolator.
@@ -6,7 +6,9 @@ require "database_cleaner/active_record/transaction"
6
6
  Module.new do
7
7
  def start
8
8
  super
9
- connection_id = connection_class.connection.object_id
9
+
10
+ connection_id = connection.object_id
11
+
10
12
  Isolator.set_connection_threshold(
11
13
  Isolator.transactions_threshold(connection_id) + 1,
12
14
  connection_id
@@ -14,12 +16,24 @@ require "database_cleaner/active_record/transaction"
14
16
  end
15
17
 
16
18
  def clean
17
- connection_id = connection_class.connection.object_id
19
+ connection_id = connection.object_id
20
+
18
21
  Isolator.set_connection_threshold(
19
22
  Isolator.transactions_threshold(connection_id) - 1,
20
23
  connection_id
21
24
  )
25
+
22
26
  super
23
27
  end
28
+
29
+ private
30
+
31
+ def connection
32
+ if Gem::Version.new("7.2") <= Gem::Version.new(Rails::VERSION::STRING)
33
+ connection_class.lease_connection
34
+ else
35
+ connection_class.connection
36
+ end
37
+ end
24
38
  end
25
39
  )
@@ -13,16 +13,17 @@ module Isolator
13
13
  def start(event, id, payload)
14
14
  if event.start_with?("transaction.")
15
15
  connection_id = extract_transaction_connection_id(payload)
16
+ stack = stacks[connection_id]
16
17
 
17
18
  # transaction.active_record can be issued without a query (when we restart the transaction),
18
19
  # so we should add a new one on the stack.
19
20
  # Example: https://github.com/rails/rails/blob/ce49fa9b31cd4a21d43db39d0cea364bce28b51d/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L337
20
- if stacks[connection_id].last == :raw
21
+ if stack.last == :raw
21
22
  # Update the type of the last transaction event
22
- stacks[connection_id].pop
23
- stacks[connection_id] << :transaction
23
+ stack.pop
24
+ stack << :transaction
24
25
  else
25
- stacks[connection_id] << :transaction
26
+ stack << :transaction
26
27
  Isolator.incr_transactions!(connection_id)
27
28
  end
28
29
  end
@@ -32,19 +33,23 @@ module Isolator
32
33
  if event.start_with?("sql.")
33
34
  if start_event?(payload[:sql])
34
35
  connection_id = extract_connection_id(payload)
36
+ stack = stacks[connection_id]
35
37
 
36
- stacks[connection_id] << :raw
38
+ stack << :raw
37
39
 
38
40
  Isolator.incr_transactions!(connection_id)
39
41
  end
40
42
 
41
43
  if finish_event?(payload[:sql])
42
44
  connection_id = extract_connection_id(payload)
45
+ stack = stacks[connection_id]
43
46
 
44
47
  # Decrement only if the transaction was started in the raw mode,
45
48
  # otherwise we should wait for the "transaction" event
46
- if stacks[connection_id].last == :raw
47
- stacks[connection_id].pop
49
+ if stack.last == :raw
50
+ stack.pop
51
+ stacks.delete(connection_id) if stack.empty?
52
+
48
53
  Isolator.decr_transactions!(connection_id)
49
54
  end
50
55
  end
@@ -52,9 +57,14 @@ module Isolator
52
57
 
53
58
  if event.start_with?("transaction.")
54
59
  connection_id = extract_transaction_connection_id(payload)
55
- stacks[connection_id].pop
60
+ stack = stacks[connection_id]
61
+
62
+ if stack.last == :transaction
63
+ stack.pop
64
+ stacks.delete(connection_id) if stack.empty?
56
65
 
57
- Isolator.decr_transactions!(connection_id)
66
+ Isolator.decr_transactions!(connection_id)
67
+ end
58
68
  end
59
69
  end
60
70
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Isolator
4
+ class Configuration
5
+ attr_accessor :max_subtransactions_depth
6
+ end
7
+
8
+ class MaxSubtransactionsExceededError < UnsafeOperationError
9
+ MESSAGE = "Allowed subtransaction amount exceeded"
10
+ end
11
+
12
+ Isolator.on_transaction_begin do |event|
13
+ next if Isolator.config.max_subtransactions_depth.nil?
14
+
15
+ depth = event[:depth]
16
+ next unless (depth - 1) > Isolator.config.max_subtransactions_depth
17
+
18
+ Isolator.notify(exception: MaxSubtransactionsExceededError.new, backtrace: caller)
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Isolator
4
- VERSION = "1.1.2"
4
+ VERSION = "1.2.1"
5
5
  end
data/lib/isolator.rb CHANGED
@@ -120,7 +120,7 @@ module Isolator
120
120
 
121
121
  def decr_thresholds!
122
122
  self.default_threshold -= 1
123
- debug!("Thresholds were incremented")
123
+ debug!("Thresholds were decremented")
124
124
 
125
125
  return unless state[:thresholds]
126
126
 
@@ -263,3 +263,4 @@ require "isolator/database_cleaner_support" if defined?(DatabaseCleaner)
263
263
 
264
264
  # Built-in extensions
265
265
  require "isolator/plugins/concurrent_database_transactions"
266
+ require "isolator/plugins/database_subtransactions"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isolator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-16 00:00:00.000000000 Z
11
+ date: 2026-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sniffer
@@ -318,6 +318,7 @@ files:
318
318
  - lib/isolator/orm_adapters/active_support_transaction_subscriber.rb
319
319
  - lib/isolator/orm_adapters/rom_active_support.rb
320
320
  - lib/isolator/plugins/concurrent_database_transactions.rb
321
+ - lib/isolator/plugins/database_subtransactions.rb
321
322
  - lib/isolator/railtie.rb
322
323
  - lib/isolator/simple_hashie.rb
323
324
  - lib/isolator/version.rb