Skip to content

Commit 087e27d

Browse files
authored
Merge pull request rails#55407 from kirs/current-transaction-isolation
Provide a method to check current transaction's isolation level
2 parents a87cc46 + 3a2142e commit 087e27d

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

activerecord/CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
1+
* Add `connection.current_transaction.isolation` API to check current transaction's isolation level.
2+
3+
Returns the isolation level if it was explicitly set via the `isolation:` parameter
4+
or through `ActiveRecord.with_transaction_isolation_level`, otherwise returns `nil`.
5+
Nested transactions return the parent transaction's isolation level.
6+
7+
```ruby
8+
# Returns nil when no transaction
9+
User.connection.current_transaction.isolation # => nil
10+
11+
# Returns explicitly set isolation level
12+
User.transaction(isolation: :serializable) do
13+
User.connection.current_transaction.isolation # => :serializable
14+
end
15+
16+
# Returns nil when isolation not explicitly set
17+
User.transaction do
18+
User.connection.current_transaction.isolation # => nil
19+
end
20+
21+
# Nested transactions inherit parent's isolation
22+
User.transaction(isolation: :read_committed) do
23+
User.transaction do
24+
User.connection.current_transaction.isolation # => :read_committed
25+
end
26+
end
27+
```
28+
29+
*Kir Shatrov*
30+
131
* Emit a warning for pg gem < 1.6.0 when using PostgreSQL 18+
232

333
*Yasuo Honda*

activerecord/lib/active_record/connection_adapters/abstract/transaction.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def state; end
112112
def closed?; true; end
113113
def open?; false; end
114114
def joinable?; false; end
115+
def isolation; nil; end
115116
def add_record(record, _ = true); end
116117
def restartable?; false; end
117118
def dirty?; false; end
@@ -150,6 +151,11 @@ def after_rollback
150151

151152
delegate :invalidate!, :invalidated?, to: :@state
152153

154+
# Returns the isolation level if it was explicitly set, nil otherwise
155+
def isolation
156+
@isolation_level
157+
end
158+
153159
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
154160
super()
155161
@connection = connection
@@ -386,7 +392,7 @@ def initialize(connection, parent_transaction, **options)
386392
@parent.state.add_child(@state)
387393
end
388394

389-
delegate :materialize!, :materialized?, :restart, to: :@parent
395+
delegate :materialize!, :materialized?, :restart, :isolation, to: :@parent
390396

391397
def rollback
392398
@state.rollback!
@@ -405,6 +411,7 @@ class SavepointTransaction < Transaction
405411
def initialize(connection, savepoint_name, parent_transaction, **options)
406412
super(connection, **options)
407413

414+
@parent_transaction = parent_transaction
408415
parent_transaction.state.add_child(@state)
409416

410417
if isolation_level
@@ -414,6 +421,11 @@ def initialize(connection, savepoint_name, parent_transaction, **options)
414421
@savepoint_name = savepoint_name
415422
end
416423

424+
# Delegates to parent transaction's isolation level
425+
def isolation
426+
@parent_transaction.isolation
427+
end
428+
417429
def materialize!
418430
connection.create_savepoint(savepoint_name)
419431
super

activerecord/test/cases/transaction_isolation_test.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class Tag < ActiveRecord::Base
1414
Tag.transaction(isolation: :serializable) { Tag.lease_connection.materialize_transactions }
1515
end
1616
end
17+
18+
test "current_transaction.isolation returns nil when no transaction" do
19+
assert_nil Tag.lease_connection.current_transaction.isolation
20+
end
1721
end
1822
end
1923

@@ -252,6 +256,30 @@ class Dog < ARUnit2Model
252256
end
253257
end
254258

259+
test "current_transaction.isolation returns nil when no transaction" do
260+
assert_nil Tag.lease_connection.current_transaction.isolation
261+
end
262+
263+
test "current_transaction.isolation returns explicitly set isolation level" do
264+
Tag.transaction(isolation: :read_committed) do
265+
assert_equal :read_committed, Tag.lease_connection.current_transaction.isolation
266+
end
267+
end
268+
269+
test "current_transaction.isolation returns parent isolation for nested transactions" do
270+
Tag.transaction(isolation: :read_committed) do
271+
Tag.transaction do
272+
assert_equal :read_committed, Tag.lease_connection.current_transaction.isolation
273+
end
274+
end
275+
end
276+
277+
test "current_transaction.isolation returns nil for transactions without explicit isolation" do
278+
Tag.transaction do
279+
assert_nil Tag.lease_connection.current_transaction.isolation
280+
end
281+
end
282+
255283
private
256284
def assert_begin_isolation_level_event(events, isolation: "READ COMMITTED", count: 1)
257285
if current_adapter?(:PostgreSQLAdapter)

0 commit comments

Comments
 (0)