queue_classic_plus 4.0.0.alpha16 → 4.0.0.alpha17

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: 1ca2467a415dbb231b97492a6b223945a08c79a704209c9f5cbdcc6317a51643
4
- data.tar.gz: 497545d4065d6cd1c03a39591b04174fbff95899871a1b0dcb1d33a4d5cc4bdf
3
+ metadata.gz: 3b17bbec023aa5f8fa91527f15c91db3ca6a9975da20c304188b5100757bab9b
4
+ data.tar.gz: 57ce60a1dfd1912b7a6b57c6b8c312f78fc3f903dc674739099dc27bb138d8aa
5
5
  SHA512:
6
- metadata.gz: 2c46e104255d291c0bee0e2c5517bb72c1ced81f860fb8f7ee1474973ed806f92c5352bfb060e9f538dcacd9e43dfd8ad2100539931bf21f9d567a4456e6e64c
7
- data.tar.gz: 6102b297532d4cc30c30825deb42a82121460d773f0881a250ea5280803aa0d959279cf4e1baee8e4719d41840afbb1e332a9b598b9ef9711ceed1b31da31cbe
6
+ metadata.gz: e45d48b488c7cc0bdea10fbea03397539a5533b1d98647aaf9f942150caaa121dc958bc57cc183782ffc7b74c98305a90ef14c9d07dfcb00b562372bf0c7048b
7
+ data.tar.gz: 23932d46b74e4f994b4e8a09d6b81e933f0798fa45608968e63f37b39476de0428c6315ebb925e18b043dabc4b10d0d8336e72d65407f61ba5e678c591d9918a
@@ -2,18 +2,10 @@ module QueueClassicPlus
2
2
  class Base
3
3
  extend QueueClassicPlus::InheritableAttribute
4
4
 
5
- # Max value for bigint calculated from
6
- # https://stackoverflow.com/questions/28960478/postgres-maximum-value-for-bigint
7
- PG_BIGINT_MAX = 9223372036854775807.freeze
8
-
9
5
  def self.queue
10
6
  QC::Queue.new(@queue)
11
7
  end
12
8
 
13
- def self.queue_name_digest
14
- @queue_name_digest ||= @queue.to_s.to_i(36) % PG_BIGINT_MAX
15
- end
16
-
17
9
  inheritable_attr :locked
18
10
  inheritable_attr :skip_transaction
19
11
  inheritable_attr :retries_on
@@ -60,42 +52,8 @@ module QueueClassicPlus
60
52
  QueueClassicPlus.logger
61
53
  end
62
54
 
63
- def self.can_enqueue?(method, *args)
64
- if locked?
65
- max_lock_time = ENV.fetch("QUEUE_CLASSIC_MAX_LOCK_TIME", 10 * 60).to_i
66
-
67
- q = "SELECT COUNT(1) AS count
68
- FROM
69
- (
70
- SELECT 1
71
- FROM queue_classic_jobs
72
- WHERE q_name = $1 AND method = $2 AND args::text = $3::text
73
- AND (locked_at IS NULL OR locked_at > current_timestamp - interval '#{max_lock_time} seconds')
74
- LIMIT 1
75
- )
76
- AS x"
77
-
78
- result = QC.default_conn_adapter.execute(q, @queue, method, JSON.dump(serialized(args)))
79
- result['count'].to_i == 0
80
- else
81
- true
82
- end
83
- end
84
-
85
55
  def self.enqueue(method, *args)
86
- conn = QC.default_conn_adapter.connection
87
- check_and_enqueue = proc do
88
- conn.exec("SELECT pg_advisory_xact_lock(#{queue_name_digest})")
89
- if can_enqueue?(method, *args)
90
- queue.enqueue(method, *serialized(args))
91
- end
92
- end
93
-
94
- if [PG::PQTRANS_ACTIVE, PG::PQTRANS_INTRANS].include?(conn.transaction_status)
95
- check_and_enqueue.call
96
- else
97
- conn.transaction &check_and_enqueue
98
- end
56
+ queue.enqueue(method, *serialized(args), lock: locked?)
99
57
  end
100
58
 
101
59
  def self.enqueue_perform(*args)
@@ -50,5 +50,25 @@ module QC
50
50
  end
51
51
  end
52
52
 
53
+ def enqueue(method, *args, lock: false)
54
+ QC.log_yield(:measure => 'queue.enqueue') do
55
+ insert_sql = <<-EOF
56
+ INSERT INTO #{QC.table_name} (q_name, method, args, lock)
57
+ VALUES ($1, $2, $3, $4)
58
+ ON CONFLICT (q_name, method, args) WHERE lock IS TRUE DO NOTHING
59
+ RETURNING id
60
+ EOF
61
+ begin
62
+ retries ||= 0
63
+ conn_adapter.execute(insert_sql, name, method, JSON.dump(args), lock)
64
+ rescue PG::Error => error
65
+ if (retries += 1) < 2
66
+ retry
67
+ else
68
+ raise
69
+ end
70
+ end
71
+ end
72
+ end
53
73
  end
54
74
  end
@@ -1,3 +1,3 @@
1
1
  module QueueClassicPlus
2
- VERSION = '4.0.0.alpha16'.freeze
2
+ VERSION = '4.0.0.alpha17'.freeze
3
3
  end
@@ -15,14 +15,18 @@ module QueueClassicPlus
15
15
 
16
16
  def self.migrate(c = QC::default_conn_adapter.connection)
17
17
  conn = QC::ConnAdapter.new(connection: c)
18
- conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN last_error TEXT")
19
- conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN remaining_retries INTEGER")
18
+ conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN IF NOT EXISTS last_error TEXT")
19
+ conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN IF NOT EXISTS remaining_retries INTEGER")
20
+ conn.execute("ALTER TABLE queue_classic_jobs ADD COLUMN IF NOT EXISTS lock BOOLEAN NOT NULL DEFAULT FALSE")
21
+ conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS index_queue_classic_jobs_enqueue_lock on queue_classic_jobs(q_name, method, args) WHERE lock IS TRUE")
20
22
  end
21
23
 
22
24
  def self.demigrate(c = QC::default_conn_adapter.connection)
23
25
  conn = QC::ConnAdapter.new(connection: c)
24
- conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN last_error")
25
- conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN remaining_retries")
26
+ conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN IF EXISTS last_error")
27
+ conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN IF EXISTS remaining_retries")
28
+ conn.execute("DROP INDEX IF EXISTS index_queue_classic_jobs_enqueue_lock")
29
+ conn.execute("ALTER TABLE queue_classic_jobs DROP COLUMN IF EXISTS lock")
26
30
  end
27
31
 
28
32
  def self.exception_handler
data/spec/base_spec.rb CHANGED
@@ -3,6 +3,24 @@ require 'active_record'
3
3
 
4
4
  describe QueueClassicPlus::Base do
5
5
  context "A child of QueueClassicPlus::Base" do
6
+ subject do
7
+ Class.new(QueueClassicPlus::Base) do
8
+ @queue = :test
9
+ end
10
+ end
11
+
12
+ it "allows multiple enqueues" do
13
+ threads = []
14
+ 10.times do
15
+ threads << Thread.new do
16
+ subject.do
17
+ end
18
+ end
19
+ threads.each(&:join)
20
+
21
+ expect(subject).to have_queue_size_of(10)
22
+ end
23
+
6
24
  context "that is locked" do
7
25
  subject do
8
26
  Class.new(QueueClassicPlus::Base) do
@@ -13,14 +31,27 @@ describe QueueClassicPlus::Base do
13
31
 
14
32
  it "does not allow multiple enqueues" do
15
33
  threads = []
16
- 50.times do
34
+ 10.times do
17
35
  threads << Thread.new do
18
36
  subject.do
19
37
  expect(subject).to have_queue_size_of(1)
20
38
  end
21
39
  end
40
+ threads.each(&:join)
41
+ end
22
42
 
43
+ it "allows enqueueing same job with different arguments" do
44
+ threads = []
45
+ (1..3).each do |arg|
46
+ 10.times do
47
+ threads << Thread.new do
48
+ subject.do(arg)
49
+ end
50
+ end
51
+ end
23
52
  threads.each(&:join)
53
+
54
+ expect(subject).to have_queue_size_of(3)
24
55
  end
25
56
 
26
57
  it "checks for an existing job using the same serializing as job enqueuing" do
@@ -34,14 +65,6 @@ describe QueueClassicPlus::Base do
34
65
  subject.do(date)
35
66
  expect(subject).to have_queue_size_of(1)
36
67
  end
37
-
38
- it "does allow multiple enqueues if something got locked for too long" do
39
- subject.do
40
- one_day_ago = Time.now - 60*60*24
41
- execute "UPDATE queue_classic_jobs SET locked_at = '#{one_day_ago}' WHERE q_name = 'test'"
42
- subject.do
43
- expect(subject).to have_queue_size_of(2)
44
- end
45
68
  end
46
69
 
47
70
  context "when in a transaction" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: queue_classic_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.alpha16
4
+ version: 4.0.0.alpha17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Mathieu
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-08-11 00:00:00.000000000 Z
13
+ date: 2023-08-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: queue_classic