sequel-activerecord_connection 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4078a41f54331cbb97fc266abdbe24cf8c297be14bb7f92b3bf605bf9b549bd
4
- data.tar.gz: af466cd9b062279a7781106136a05151f02339830469136cdf9b58d629397b98
3
+ metadata.gz: f8e137f0937e4f17511d40529df10648276b7dae834cef50f4bc6615206c486b
4
+ data.tar.gz: e25287b365a78b34c55afb084b0ebad4a24c8c00944323c1a2b455505fd4a251
5
5
  SHA512:
6
- metadata.gz: e28881de46188d6dc143952a473e00254b0742c497e7888e8d334ff33b627d3d90d9174675809cc3248b15cfcc800908bc83dda5a8da3fee93c4048c44282424
7
- data.tar.gz: 7c0a46d18a45e76ca4a7af11d467b957035dd4b62adbdcaf64d8e9cafe1bfb406fa40c3d4feadff10c823d2535cab978111203ade32ea519f991e828ed1df033
6
+ metadata.gz: '033781eeae71623aa992f2a882c04a1fd718109435b3aa428b0710df4322f1f267664abb4670a9909e3aefa570029a37c8a6a6947d54d2b38e119b80c938245b'
7
+ data.tar.gz: c013ca47766a5cfae058353094df8a88e74a992de924f7b2107c6e50a5f76af72c74fab0f957032666f7b3442b80af886ca56f5bd3635d67c141fa67ff7be5d8
@@ -1,3 +1,17 @@
1
+ ## 0.4.0 (2020-09-28)
2
+
3
+ * Return correct result of `Database#in_transaction?` after ActiveRecord transaction exited (@janko)
4
+
5
+ * Make ActiveRecord create a savepoint inside a Sequel transaction with `auto_savepoint: true` (@janko)
6
+
7
+ * Make Sequel create a savepoint inside ActiveRecord transaction with `joinable: false` (@janko)
8
+
9
+ * Improve reliability of nested transactions when combining Sequel and ActiveRecord (@janko)
10
+
11
+ * Raise error when attempting to add an `after_commit`/`after_rollback` hook on ActiveRecord transaction (@janko)
12
+
13
+ * Fix infinite loop that could happen with transactional Rails tests (@janko)
14
+
1
15
  ## 0.3.0 (2020-07-24)
2
16
 
3
17
  * Fully support Sequel transaction API (all transaction options, transaction/savepoint hooks etc.) (@janko)
data/README.md CHANGED
@@ -42,8 +42,7 @@ appropriate Sequel adapter and load the `activerecord_connection` extension:
42
42
  ```rb
43
43
  require "sequel"
44
44
 
45
- DB = Sequel.postgres(test: false) # avoid creating a connection
46
- DB.extension :activerecord_connection
45
+ DB = Sequel.postgres(extensions: :activerecord_connection)
47
46
  ```
48
47
 
49
48
  Now any Sequel operations that you make will internaly be done using the
@@ -75,13 +74,13 @@ ActiveRecord adapters, just make sure to initialize the corresponding Sequel
75
74
  adapter before loading the extension.
76
75
 
77
76
  ```rb
78
- DB = Sequel.postgres(test: false) # for "postgresql" adapter
77
+ DB = Sequel.postgres(extensions: :activerecord_connection) # for "postgresql" adapter
79
78
  # or
80
- DB = Sequel.mysql2(test: false) # for "mysql2" adapter
79
+ DB = Sequel.mysql2(extensions: :activerecord_connection) # for "mysql2" adapter
81
80
  # or
82
- DB = Sequel.sqlite(test: false) # for "sqlite3" adapter
81
+ DB = Sequel.sqlite(extensions: :activerecord_connection) # for "sqlite3" adapter
83
82
  # or
84
- DB = Sequel.jdbc(test: false) # for JDBC adapter
83
+ DB = Sequel.jdbc(extensions: :activerecord_connection) # for JDBC adapter
85
84
  ```
86
85
 
87
86
  ### Transactions
@@ -101,39 +100,36 @@ Sequel's transaction API is fully supported:
101
100
 
102
101
  ```rb
103
102
  DB.transaction(isolation: :serializable) do
104
- DB.after_commit { ... } # call block after transaction commits
103
+ DB.after_commit { ... } # executed after transaction commits
105
104
  DB.transaction(savepoint: true) do # creates a savepoint
106
- # ...
105
+ DB.after_commit(savepoint: true) { ... } # executed if all enclosing savepoints have been released
107
106
  end
108
107
  end
109
108
  ```
110
109
 
111
- One caveat to keep in mind is that Sequel's transaction hooks
112
- (`after_commit`, `after_rollback`) will *not* run if ActiveRecord holds the
113
- outer transaction:
110
+ One caveat to keep in mind is that using Sequel's transaction/savepoint hooks
111
+ currently don't work if ActiveRecord holds the corresponding
112
+ transaction/savepoint. This is because it's difficult to be notified when
113
+ ActiveRecord commits or rolls back the transaction/savepoint.
114
114
 
115
115
  ```rb
116
116
  DB.transaction do
117
117
  DB.after_commit { ... } # will get executed
118
118
  end
119
119
 
120
- ActiveRecord::Base.transaction do
121
- DB.after_commit { ... } # won't get executed
120
+ DB.transaction do
121
+ DB.transaction(savepoint: true) do
122
+ DB.after_commit(savepoint: true) { ... } # will get executed
123
+ end
122
124
  end
123
125
 
124
126
  ActiveRecord::Base.transaction do
125
- DB.transaction do
126
- DB.after_commit { ... } # won't get executed
127
- end
127
+ DB.after_commit { ... } # not allowed (will raise Sequel::ActiveRecordConnection::Error)
128
128
  end
129
- ```
130
129
 
131
- Savepoint hooks should still work, though:
132
-
133
- ```rb
134
- ActiveRecord::Base.transaction do
135
- DB.transaction(savepoint: true) do
136
- DB.after_commit { ... } # will get executed after savepoint is released
130
+ DB.transaction do
131
+ ActiveRecord::Base.transaction(requires_new: true) do
132
+ DB.after_commit(savepoint: true) { ... } # not allowed (will raise Sequel::ActiveRecordConnection::Error)
137
133
  end
138
134
  end
139
135
  ```
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sequel
2
4
  module ActiveRecordConnection
3
5
  Error = Class.new(Sequel::Error)
@@ -41,38 +43,43 @@ module Sequel
41
43
 
42
44
  private
43
45
 
44
- # Backfills any ActiveRecord transactions/savepoints that have been opened
45
- # directly via ActiveRecord::Base.transaction. Sequel uses this information
46
- # to know whether we're in a transaction, whether to create a savepoint,
47
- # when to run transaction/savepoint hooks etc.
46
+ # Synchronizes transaction state with ActiveRecord. Sequel uses this
47
+ # information to know whether we're in a transaction, whether to create a
48
+ # savepoint, when to run transaction/savepoint hooks etc.
48
49
  def _trans(conn)
49
- Sequel.synchronize do
50
- result = @transactions[conn]
51
-
52
- if activerecord_connection.transaction_open?
53
- result ||= { savepoints: [] }
54
- while result[:savepoints].length < activerecord_connection.open_transactions
55
- result[:savepoints].unshift({ activerecord: true })
56
- end
57
- end
50
+ hash = super || { savepoints: [], activerecord: true }
58
51
 
59
- @transactions[conn] = result if result
60
- result
52
+ # add any ActiveRecord transactions/savepoints that have been opened
53
+ # directly via ActiveRecord::Base.transaction
54
+ while hash[:savepoints].length < activerecord_connection.open_transactions
55
+ hash[:savepoints] << { activerecord: true }
56
+ end
57
+
58
+ # remove any ActiveRecord transactions/savepoints that have been closed
59
+ # directly via ActiveRecord::Base.transaction
60
+ while hash[:savepoints].length > activerecord_connection.open_transactions && hash[:savepoints].last[:activerecord]
61
+ hash[:savepoints].pop
62
+ end
63
+
64
+ # sync knowledge about joinability of current ActiveRecord transaction/savepoint
65
+ if activerecord_connection.transaction_open? && !activerecord_connection.current_transaction.joinable?
66
+ hash[:savepoints].last[:auto_savepoint] = true
67
+ end
68
+
69
+ if hash[:savepoints].empty? && hash[:activerecord]
70
+ Sequel.synchronize { @transactions.delete(conn) }
71
+ else
72
+ Sequel.synchronize { @transactions[conn] = hash }
61
73
  end
62
- end
63
74
 
64
- # First delete any transactions/savepoints opened directly via
65
- # ActiveRecord::Base.transaction, so that Sequel can detect when the last
66
- # Sequel transaction has been closed and clear transaction information.
67
- def transaction_finished?(conn)
68
- _trans(conn)[:savepoints].shift while _trans(conn)[:savepoints].first[:activerecord]
69
75
  super
70
76
  end
71
77
 
72
78
  def begin_transaction(conn, opts = {})
73
79
  isolation = TRANSACTION_ISOLATION_MAP.fetch(opts[:isolation]) if opts[:isolation]
80
+ joinable = !opts[:auto_savepoint]
74
81
 
75
- activerecord_connection.begin_transaction(isolation: isolation)
82
+ activerecord_connection.begin_transaction(isolation: isolation, joinable: joinable)
76
83
  end
77
84
 
78
85
  def commit_transaction(conn, opts = {})
@@ -84,8 +91,20 @@ module Sequel
84
91
  activerecord_connection.transaction_manager.send(:after_failure_actions, activerecord_connection.current_transaction, $!) if activerecord_connection.transaction_manager.respond_to?(:after_failure_actions)
85
92
  end
86
93
 
87
- def savepoint_level(conn)
88
- activerecord_connection.open_transactions
94
+ def add_transaction_hook(conn, type, block)
95
+ if _trans(conn)[:activerecord]
96
+ fail Error, "cannot add transaction hook when ActiveRecord holds the outer transaction"
97
+ end
98
+
99
+ super
100
+ end
101
+
102
+ def add_savepoint_hook(conn, type, block)
103
+ if _trans(conn)[:savepoints].last[:activerecord]
104
+ fail Error, "cannot add savepoint hook when ActiveRecord holds the current savepoint"
105
+ end
106
+
107
+ super
89
108
  end
90
109
 
91
110
  def activerecord_raw_connection
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "sequel-activerecord_connection"
3
- spec.version = "0.3.0"
3
+ spec.version = "0.4.0"
4
4
  spec.authors = ["Janko Marohnić"]
5
5
  spec.email = ["janko.marohnic@gmail.com"]
6
6
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-activerecord_connection
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-24 00:00:00.000000000 Z
11
+ date: 2020-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel