sequel-activerecord_connection 0.3.0 → 0.4.0

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: 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