riverqueue-activerecord 0.13.0 → 0.14.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/driver.rb +45 -13
  3. metadata +9 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09139d53f5cb109997ed71a4d9a0353ee52760f950a5acedb27ac0c0f8b9e21e'
4
- data.tar.gz: 768b524dbe9f4b959100dda627137862ec1c53c91709792f02105e63be428bcb
3
+ metadata.gz: 671bf59d2aaa2199d30f02a1e69466502501577dffec80055c6c2adaadd245b1
4
+ data.tar.gz: e21b1403bc41a42fd85bb0b38cd9e8df434e0e20bce395a4d67d99e612829d7c
5
5
  SHA512:
6
- metadata.gz: d78a3de101d1942aa0adf46053072c5c9c93f9a0832b5469671a57530f4860cd95f967ed014e7399f6aa23bd787d715b491f95b9d618749a609b3223a9c693cf
7
- data.tar.gz: c3e0b21846b883f82f8980bd8926744009f810670b1eceb038144500084184e2b5fee6147e1bdd849f4ba71e56f091f387d14e8404cc47c3d7fc6363c3fed730
6
+ metadata.gz: cba4e6c350a89ea5bcc4f524870fba3f49e99c79c7681f6578d2b69af3bd082b68d4668558987a61437150f71c410f5d6a31ee4486f18b9b3645276ad5caf9a2
7
+ data.tar.gz: 24832d891872a83a27a6871df9d604ab20531749f866a082656decbb5b6ff21a969ced6af7f08b0889ca36aade5474b1d08487885ca37ddf79c2064daacf918d
data/lib/driver.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "securerandom"
4
4
 
5
5
  module River::Driver
6
- # Provides an ActiveRecord driver for River supporting PostgreSQL, YugabyteDB,
6
+ # Provides an ActiveRecord driver for River supporting Postgres, YugabyteDB,
7
7
  # and SQLite.
8
8
  #
9
9
  # Used in conjunction with a River client like:
@@ -17,7 +17,7 @@ module River::Driver
17
17
  # Connection class whose pool and transaction context this driver uses.
18
18
  attr_reader :connection_class
19
19
 
20
- # Uses an established PostgreSQL or SQLite connection. The class must be
20
+ # Uses an established Postgres or SQLite connection. The class must be
21
21
  # ActiveRecord::Base or an abstract Active Record class. Routing follows its
22
22
  # current role/shard; configure consumers explicitly for each database.
23
23
  def initialize(connection_class: ::ActiveRecord::Base)
@@ -48,6 +48,16 @@ module River::Driver
48
48
  end
49
49
  end
50
50
 
51
+ # Runs the callback only after this connection's outer transaction commits.
52
+ def after_commit(&block)
53
+ @connection_class.current_transaction.after_commit(&block)
54
+ end
55
+
56
+ # Reports whether this thread has a transaction on the configured connection.
57
+ def in_transaction?
58
+ @connection_class.connection_pool.with_connection(&:transaction_open?)
59
+ end
60
+
51
61
  def job_get_by_id(id)
52
62
  if @is_sqlite
53
63
  row = sqlite_job_rows("WHERE id = ? LIMIT 1", [id]).first
@@ -100,10 +110,17 @@ module River::Driver
100
110
  end
101
111
  end
102
112
 
103
- # Runs the block in a new Active Record transaction or savepoint and returns
104
- # the block's result.
113
+ # Runs the block in an Active Record transaction and returns the block's
114
+ # result. Joins an open, joinable transaction instead of opening a
115
+ # savepoint.
105
116
  def transaction(&)
106
- @connection_class.transaction(requires_new: true, &)
117
+ legacy = runtime_legacy_subtransactions?
118
+ # A joined Active Record block swallows ActiveRecord::Rollback without
119
+ # rolling anything back. Yield directly so that the transaction's owner
120
+ # receives it and rolls back, as Sequel does with Sequel::Rollback.
121
+ return yield if !legacy && @connection_class.connection.current_transaction.joinable?
122
+
123
+ @connection_class.transaction(requires_new: legacy, &)
107
124
  end
108
125
 
109
126
  SQLITE_CONFLICT_WHERE = <<~SQL.chomp.freeze
@@ -154,14 +171,21 @@ module River::Driver
154
171
  private_constant :SQLITE_CONFLICT_WHERE, :SQLITE_JOB_COLUMNS, :UNIQUE_INSERT_METADATA_KEY
155
172
 
156
173
  private def format_time(time)
157
- time.getutc.round(3).strftime("%Y-%m-%d %H:%M:%S.%3N")
174
+ normalize_time(time).strftime("%Y-%m-%d %H:%M:%S.%3N")
175
+ end
176
+
177
+ private def notification_query(sql)
178
+ @connection_class.connection_pool.with_connection { runtime_query_rows(sql) }
158
179
  end
159
180
 
160
181
  private def postgres_insert_params_to_hash(insert_params, nonce)
161
182
  metadata = insert_params.metadata || {}
162
183
  metadata = metadata.merge(UNIQUE_INSERT_METADATA_KEY => nonce) if nonce
163
184
  {
164
- args: JSON.parse(insert_params.encoded_args),
185
+ # Bypass the jsonb attribute type, which would decode and re-encode
186
+ # the args, losing number spellings like `1.10` and digits beyond
187
+ # Float precision. Postgres parses the encoded bytes itself instead.
188
+ args: Arel.sql("CAST(#{runtime_quote(insert_params.encoded_args)} AS jsonb)"),
165
189
  kind: insert_params.kind,
166
190
  max_attempts: insert_params.max_attempts,
167
191
  metadata: metadata,
@@ -236,6 +260,15 @@ module River::Driver
236
260
  end
237
261
  end
238
262
 
263
+ private def runtime_notification_connection
264
+ params, schema = @connection_class.connection_pool.with_connection do |connection|
265
+ [connection.raw_connection.conninfo_hash, connection.select_value("SELECT current_schema()")]
266
+ end
267
+ params.compact!
268
+ params[:connect_timeout] = "5" unless params[:connect_timeout].to_i.positive?
269
+ [::PG.connect(params), schema]
270
+ end
271
+
239
272
  private def runtime_postgres?
240
273
  !@is_sqlite
241
274
  end
@@ -252,17 +285,16 @@ module River::Driver
252
285
  @connection_class.connection.quote(value)
253
286
  end
254
287
 
255
- private def runtime_unique_violation_class
256
- ::ActiveRecord::RecordNotUnique
257
- end
258
-
259
288
  private def runtime_value(row, key)
260
289
  row[key.to_s] || row[key]
261
290
  end
262
291
 
263
292
  private def sqlite_insert_params_to_hash(insert_params, nonce)
264
293
  {
265
- args: JSON.parse(insert_params.encoded_args),
294
+ # Embed the encoded args as a JSON string so they reach SQLite
295
+ # byte-for-byte. `json_extract` unwraps the string back to the
296
+ # encoded JSON text before `jsonb` parses it.
297
+ args: insert_params.encoded_args,
266
298
  kind: insert_params.kind,
267
299
  max_attempts: insert_params.max_attempts,
268
300
  metadata: {UNIQUE_INSERT_METADATA_KEY => nonce},
@@ -281,7 +313,7 @@ module River::Driver
281
313
  # matching migration 007 and newer River databases.
282
314
  private def sqlite_job_insert_many(insert_params_many)
283
315
  nonces = sqlite_insert_nonces(insert_params_many)
284
- @connection_class.transaction(requires_new: true) do
316
+ transaction do
285
317
  jobs = insert_params_many.zip(nonces).map { |param, nonce| sqlite_insert_params_to_hash(param, nonce) }
286
318
  inserted_nonces = nonces.to_h { |nonce| [nonce, true] }
287
319
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riverqueue-activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Gentry
@@ -14,9 +14,9 @@ dependencies:
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '7.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '1000'
@@ -24,9 +24,9 @@ dependencies:
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ">"
27
+ - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: '7.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '1000'
@@ -56,15 +56,15 @@ dependencies:
56
56
  requirements:
57
57
  - - '='
58
58
  - !ruby/object:Gem::Version
59
- version: 0.13.0
59
+ version: 0.14.0
60
60
  type: :runtime
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - '='
65
65
  - !ruby/object:Gem::Version
66
- version: 0.13.0
67
- description: ActiveRecord PostgreSQL and SQLite driver for inserting and working River
66
+ version: 0.14.0
67
+ description: ActiveRecord Postgres and SQLite driver for inserting and working River
68
68
  jobs in Ruby.
69
69
  email: brandur@brandur.org
70
70
  executables: []
@@ -93,5 +93,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  requirements: []
94
94
  rubygems_version: 4.0.9
95
95
  specification_version: 4
96
- summary: ActiveRecord PostgreSQL and SQLite driver for the River Ruby gem.
96
+ summary: ActiveRecord Postgres and SQLite driver for the River Ruby gem.
97
97
  test_files: []