queue_classic_plus 4.0.0.alpha16 → 4.0.0.alpha17

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