riverqueue-activerecord 0.15.0 → 0.16.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 +93 -86
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c5976ccf932cc15d270eb8275e80c9371e36f1f429dedaa7c168ff991e367fc
4
- data.tar.gz: e21b1403bc41a42fd85bb0b38cd9e8df434e0e20bce395a4d67d99e612829d7c
3
+ metadata.gz: 52855b59287d8ff3cfac156f94aef4bf887344c52461de4935c90e8e173eebdf
4
+ data.tar.gz: f0433acb86e87f358fdde11c125f595dc6a8a09ff1dd56b7cd7b34c516dabe1f
5
5
  SHA512:
6
- metadata.gz: 27ecc84fd9732b6e461e81d1639226d755ccd50f2cbb2d411aaa77a3acf68dcd48451c8ce118d15aff4e4220c263d01174bc633e45cbeed35a370c06fa0f55f3
7
- data.tar.gz: 24832d891872a83a27a6871df9d604ab20531749f866a082656decbb5b6ff21a969ced6af7f08b0889ca36aade5474b1d08487885ca37ddf79c2064daacf918d
6
+ metadata.gz: c6dcc04e8179b62aaedd0630a7b222f048578a02a089e087b10f38f089c0e14b87358ba589188c89aff167a48d419998f57e599ded18381c1ff8c44ca328a800
7
+ data.tar.gz: dfe8e481b770a80fb8e779536b60779f95dbe40f3a9001e36a3e1be44ce4ce63e8531bc411e49dc291b4a7e196fb4ca66da50d9c86e5443e76b7fcac0d01d051
data/lib/driver.rb CHANGED
@@ -31,7 +31,7 @@ module River::Driver
31
31
 
32
32
  @connection_class = connection_class
33
33
  @postgres_capabilities_cache = PostgresCapabilities::Cache.new
34
- @is_sqlite = connection_class.connection.adapter_name.downcase.include?("sqlite")
34
+ @is_sqlite = connection_class.connection_pool.with_connection { |connection| connection.adapter_name.downcase.include?("sqlite") }
35
35
  # Do not inherit application scopes, callbacks, or schema caches. Each
36
36
  # driver owns its model, but all operations use the selected class's pool.
37
37
  @job_model = Class.new(::ActiveRecord::Base) do
@@ -59,13 +59,7 @@ module River::Driver
59
59
  end
60
60
 
61
61
  def job_get_by_id(id)
62
- if @is_sqlite
63
- row = sqlite_job_rows("WHERE id = ? LIMIT 1", [id]).first
64
- row ? sqlite_to_job_row_from_raw(row) : nil
65
- else
66
- row = @job_model.find_by(id: id)
67
- row ? to_job_row_from_model(row) : nil
68
- end
62
+ runtime_query_jobs("SELECT #{runtime_job_columns} FROM river_job WHERE id = #{Integer(id)} LIMIT 1").first
69
63
  end
70
64
 
71
65
  def job_insert(insert_params)
@@ -75,12 +69,14 @@ module River::Driver
75
69
  def job_insert_many(insert_params_many)
76
70
  return [] if insert_params_many.empty?
77
71
 
78
- return sqlite_job_insert_many(insert_params_many) if @is_sqlite
79
-
80
- transaction do
81
- results = postgres_job_insert_many(insert_params_many)
82
- postgres_notify_insert(insert_params_many)
83
- results
72
+ runtime_insert_transaction(insert_params_many) do
73
+ if @is_sqlite
74
+ sqlite_job_insert_many(insert_params_many)
75
+ else
76
+ results = postgres_job_insert_many(insert_params_many)
77
+ postgres_notify_insert(insert_params_many)
78
+ results
79
+ end
84
80
  end
85
81
  end
86
82
 
@@ -114,13 +110,14 @@ module River::Driver
114
110
  # result. Joins an open, joinable transaction instead of opening a
115
111
  # savepoint.
116
112
  def transaction(&)
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?
113
+ @connection_class.connection_pool.with_connection do |connection|
114
+ legacy = runtime_legacy_subtransactions?
115
+ # A joined Active Record block swallows ActiveRecord::Rollback without
116
+ # rolling anything back. Let the transaction's owner receive it.
117
+ next yield if !legacy && connection.current_transaction.joinable?
122
118
 
123
- @connection_class.transaction(requires_new: legacy, &)
119
+ connection.transaction(requires_new: legacy, &)
120
+ end
124
121
  end
125
122
 
126
123
  SQLITE_CONFLICT_WHERE = <<~SQL.chomp.freeze
@@ -245,7 +242,11 @@ module River::Driver
245
242
  end
246
243
 
247
244
  private def runtime_execute(sql)
248
- @connection_class.connection.execute(sql)
245
+ @connection_class.connection_pool.with_connection { |connection| connection.execute(sql) }
246
+ end
247
+
248
+ private def runtime_job_columns
249
+ @is_sqlite ? SQLITE_JOB_COLUMNS : "*"
249
250
  end
250
251
 
251
252
  private def runtime_job_list_without_params
@@ -253,11 +254,7 @@ module River::Driver
253
254
  end
254
255
 
255
256
  private def runtime_job_rows(suffix)
256
- if @is_sqlite
257
- sqlite_job_rows(suffix).map { |row| sqlite_to_job_row_from_raw(row) }
258
- else
259
- @job_model.find_by_sql("SELECT * FROM river_job #{suffix}").map { |row| to_job_row_from_model(row) }
260
- end
257
+ runtime_query_jobs("SELECT #{runtime_job_columns} FROM river_job #{suffix}")
261
258
  end
262
259
 
263
260
  private def runtime_notification_connection
@@ -274,15 +271,25 @@ module River::Driver
274
271
  end
275
272
 
276
273
  private def runtime_query_rows(sql)
277
- if @is_sqlite
278
- @connection_class.connection.raw_connection.execute(sql).to_a
279
- else
280
- @connection_class.connection.select_all(sql).to_a
274
+ @connection_class.connection_pool.with_connection do |connection|
275
+ if @is_sqlite
276
+ connection.raw_connection.execute(sql).to_a
277
+ else
278
+ # exec_query invalidates the query cache for writes with RETURNING.
279
+ # Deserialize without allocating an Active Record model per job.
280
+ result = connection.exec_query(sql)
281
+ types = result.column_types
282
+ result.rows.map do |row|
283
+ result.columns.each_with_index.to_h do |column, index|
284
+ [column, types.fetch(index).deserialize(row[index])]
285
+ end
286
+ end
287
+ end
281
288
  end
282
289
  end
283
290
 
284
291
  private def runtime_quote(value)
285
- @connection_class.connection.quote(value)
292
+ @connection_class.connection_pool.with_connection { |connection| connection.quote(value) }
286
293
  end
287
294
 
288
295
  private def runtime_value(row, key)
@@ -313,61 +320,59 @@ module River::Driver
313
320
  # matching migration 007 and newer River databases.
314
321
  private def sqlite_job_insert_many(insert_params_many)
315
322
  nonces = sqlite_insert_nonces(insert_params_many)
316
- transaction do
317
- jobs = insert_params_many.zip(nonces).map { |param, nonce| sqlite_insert_params_to_hash(param, nonce) }
318
- inserted_nonces = nonces.to_h { |nonce| [nonce, true] }
319
-
320
- sql = <<~SQL
321
- INSERT INTO river_job (
322
- args,
323
- created_at,
324
- kind,
325
- max_attempts,
326
- metadata,
327
- priority,
328
- queue,
329
- scheduled_at,
330
- state,
331
- tags,
332
- unique_key,
333
- unique_states
334
- )
335
- SELECT
336
- jsonb(json_extract(value, '$.args')),
337
- datetime('now', 'subsec'),
338
- cast(json_extract(value, '$.kind') AS text),
339
- cast(json_extract(value, '$.max_attempts') AS integer),
340
- jsonb(json_extract(value, '$.metadata')),
341
- cast(json_extract(value, '$.priority') AS integer),
342
- cast(json_extract(value, '$.queue') AS text),
343
- coalesce(cast(json_extract(value, '$.scheduled_at') AS text), datetime('now', 'subsec')),
344
- cast(json_extract(value, '$.state') AS text),
345
- jsonb(json_extract(value, '$.tags')),
346
- CASE
347
- WHEN length(cast(json_extract(value, '$.unique_key') AS text)) = 0 THEN NULL
348
- ELSE unhex(cast(json_extract(value, '$.unique_key') AS text))
349
- END,
350
- nullif(cast(json_extract(value, '$.unique_states') AS integer), 0)
351
- FROM json_each(cast(? AS blob))
352
- WHERE true
353
- ON CONFLICT (unique_key) WHERE #{SQLITE_CONFLICT_WHERE}
354
- DO UPDATE SET kind = river_job.kind
355
- RETURNING #{SQLITE_JOB_COLUMNS}
356
- SQL
323
+ jobs = insert_params_many.zip(nonces).map { |param, nonce| sqlite_insert_params_to_hash(param, nonce) }
324
+ inserted_nonces = nonces.to_h { |nonce| [nonce, true] }
325
+
326
+ sql = <<~SQL
327
+ INSERT INTO river_job (
328
+ args,
329
+ created_at,
330
+ kind,
331
+ max_attempts,
332
+ metadata,
333
+ priority,
334
+ queue,
335
+ scheduled_at,
336
+ state,
337
+ tags,
338
+ unique_key,
339
+ unique_states
340
+ )
341
+ SELECT
342
+ jsonb(json_extract(value, '$.args')),
343
+ datetime('now', 'subsec'),
344
+ cast(json_extract(value, '$.kind') AS text),
345
+ cast(json_extract(value, '$.max_attempts') AS integer),
346
+ jsonb(json_extract(value, '$.metadata')),
347
+ cast(json_extract(value, '$.priority') AS integer),
348
+ cast(json_extract(value, '$.queue') AS text),
349
+ coalesce(cast(json_extract(value, '$.scheduled_at') AS text), datetime('now', 'subsec')),
350
+ cast(json_extract(value, '$.state') AS text),
351
+ jsonb(json_extract(value, '$.tags')),
352
+ CASE
353
+ WHEN length(cast(json_extract(value, '$.unique_key') AS text)) = 0 THEN NULL
354
+ ELSE unhex(cast(json_extract(value, '$.unique_key') AS text))
355
+ END,
356
+ nullif(cast(json_extract(value, '$.unique_states') AS integer), 0)
357
+ FROM json_each(cast(? AS blob))
358
+ WHERE true
359
+ ON CONFLICT (unique_key) WHERE #{SQLITE_CONFLICT_WHERE}
360
+ DO UPDATE SET kind = river_job.kind
361
+ RETURNING #{SQLITE_JOB_COLUMNS}
362
+ SQL
357
363
 
358
- rows = @connection_class.connection.raw_connection.execute(sql, [JSON.generate(jobs)])
359
- sqlite_notify_insert(insert_params_many)
364
+ rows = @connection_class.connection_pool.with_connection { |connection| connection.raw_connection.execute(sql, [JSON.generate(jobs)]) }
365
+ sqlite_notify_insert(insert_params_many)
360
366
 
361
- rows.map do |row|
362
- metadata = JSON.parse(row["metadata"])
363
- [sqlite_to_job_row_from_raw(row), !inserted_nonces.key?(metadata[UNIQUE_INSERT_METADATA_KEY])]
364
- end
367
+ rows.map do |row|
368
+ job = sqlite_to_job_row_from_raw(row)
369
+ [job, !inserted_nonces.key?(job.metadata[UNIQUE_INSERT_METADATA_KEY])]
365
370
  end
366
371
  end
367
372
 
368
373
  private def sqlite_job_rows(suffix, binds = [])
369
374
  sql = "SELECT #{SQLITE_JOB_COLUMNS} FROM river_job #{suffix}"
370
- @connection_class.connection.raw_connection.execute(sql, binds)
375
+ @connection_class.connection_pool.with_connection { |connection| connection.raw_connection.execute(sql, binds) }
371
376
  end
372
377
 
373
378
  private def sqlite_notify_insert(insert_params_many)
@@ -381,13 +386,15 @@ module River::Driver
381
386
  {payload: JSON.generate({queue: queue}), topic: "river_insert"}
382
387
  end
383
388
 
384
- @connection_class.connection.raw_connection.execute(<<~SQL, [JSON.generate(notifications)])
385
- INSERT INTO river_notification (payload, topic)
386
- SELECT
387
- json_extract(value, '$.payload'),
388
- json_extract(value, '$.topic')
389
- FROM json_each(cast(? AS blob))
390
- SQL
389
+ @connection_class.connection_pool.with_connection do |connection|
390
+ connection.raw_connection.execute(<<~SQL, [JSON.generate(notifications)])
391
+ INSERT INTO river_notification (payload, topic)
392
+ SELECT
393
+ json_extract(value, '$.payload'),
394
+ json_extract(value, '$.topic')
395
+ FROM json_each(cast(? AS blob))
396
+ SQL
397
+ end
391
398
  end
392
399
 
393
400
  private def sqlite_to_job_row_from_model(river_job)
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.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Gentry
@@ -56,14 +56,14 @@ dependencies:
56
56
  requirements:
57
57
  - - '='
58
58
  - !ruby/object:Gem::Version
59
- version: 0.15.0
59
+ version: 0.16.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.15.0
66
+ version: 0.16.0
67
67
  description: ActiveRecord Postgres and SQLite driver for inserting and working River
68
68
  jobs in Ruby.
69
69
  email: brandur@brandur.org