delayed 0.5.5 → 0.6.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: ae36c9e652d4fb6ce28f8897b0e7b03b7777ce9907a74973924111b735bca3ae
4
- data.tar.gz: 0d4c27d0bf1f7e256fa85914086a0711c6406b4be5e88f211eccbd921c001718
3
+ metadata.gz: 6e71f18ed659d830765786daba4513f7cedfef9fb645055cd01e95af8be066b3
4
+ data.tar.gz: c3b0624ab16f81e2aa7e4296a56a4967e88e75659eca20078ad662c0cc7d37ec
5
5
  SHA512:
6
- metadata.gz: cbae79e450983c1dd968b5f9959b4497758c32cc2c21a45ecea5b41dc1d7829fac2699d0a8cea635369d276eaec5b07ce8d7fa292674578ff62690823bc163fc
7
- data.tar.gz: df37746eee87f40792499e02421f82a2fc4afa4317e168bdb440f3bed1ba737c761469092098f99e2aa3fb163f50981e6b19a83a4859e91871ac8f046363f630
6
+ metadata.gz: b94b8a054612a71dc72078a0b62abf5db8a9bb305515ac75e2baeb39ab12d188eea4c168f3e774b3beebb608c821e808e2c24164a68fbdaebf894c7a566ec69e
7
+ data.tar.gz: f46b3769bc15ac507442548b1b9e810d0e70ae68a4a89eb29732c1c1b325d307eed593ab7ee49a78d6296bf69440ceb221ddb1e25e4b3237e5a723595c2903ed
data/README.md CHANGED
@@ -432,6 +432,10 @@ Delayed::Worker.read_ahead = 5
432
432
 
433
433
  # If a worker finds no jobs, it will sleep this number of seconds in between attempts:
434
434
  Delayed::Worker.sleep_delay = 5
435
+
436
+ # Until version 1.0, the worker will not sleep at all between attemps if it finds jobs.
437
+ # This can be configured by setting the minimum reserve interval:
438
+ Delayed::Worker.min_reserve_interval = 0.5 # seconds
435
439
  ```
436
440
 
437
441
  If a job fails, it will be rerun up to 25 times (with an exponential back-off). Jobs will also
@@ -21,7 +21,7 @@ module Delayed
21
21
  def on_exit!; end
22
22
 
23
23
  def interruptable_sleep(seconds)
24
- pipe[0].wait_readable(seconds)
24
+ pipe[0].wait_readable(seconds) if seconds.positive?
25
25
  end
26
26
 
27
27
  def stop
@@ -12,6 +12,7 @@ module Delayed
12
12
  include Runnable
13
13
 
14
14
  cattr_accessor :sleep_delay, instance_writer: false, default: 5
15
+ cattr_accessor :min_reserve_interval, instance_writer: false, default: 0
15
16
  cattr_accessor :max_attempts, instance_writer: false, default: 25
16
17
  cattr_accessor :max_claims, instance_writer: false, default: 5
17
18
  cattr_accessor :max_run_time, instance_writer: false, default: 20.minutes
@@ -92,6 +93,7 @@ module Delayed
92
93
  total = 0
93
94
 
94
95
  while total < num
96
+ start = clock_time
95
97
  jobs = reserve_jobs
96
98
  break if jobs.empty?
97
99
 
@@ -107,6 +109,9 @@ module Delayed
107
109
  pool.wait_for_termination
108
110
 
109
111
  break if stop? # leave if we're exiting
112
+
113
+ elapsed = clock_time - start
114
+ interruptable_sleep(self.class.min_reserve_interval - elapsed)
110
115
  end
111
116
 
112
117
  [success.value, total - success.value]
@@ -227,5 +232,9 @@ module Delayed
227
232
  def reload!
228
233
  Rails.application.reloader.reload! if defined?(Rails.application.reloader) && Rails.application.reloader.check!
229
234
  end
235
+
236
+ def clock_time
237
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
238
+ end
230
239
  end
231
240
  end
@@ -64,7 +64,7 @@ describe 'rake' do
64
64
  .to change { Delayed::Worker.min_priority }.from(nil).to(6)
65
65
  .and change { Delayed::Worker.max_priority }.from(nil).to(8)
66
66
  .and change { Delayed::Worker.queues }.from([]).to(%w(foo bar))
67
- .and change { Delayed::Worker.sleep_delay }.from(5).to(1)
67
+ .and change { Delayed::Worker.sleep_delay }.from(TEST_SLEEP_DELAY).to(1)
68
68
  .and change { Delayed::Worker.read_ahead }.from(5).to(3)
69
69
  .and change { Delayed::Worker.max_claims }.from(5).to(3)
70
70
  end
@@ -96,7 +96,7 @@ describe 'rake' do
96
96
  .to change { Delayed::Worker.min_priority }.from(nil).to(6)
97
97
  .and change { Delayed::Worker.max_priority }.from(nil).to(8)
98
98
  .and change { Delayed::Worker.queues }.from([]).to(%w(foo))
99
- .and change { Delayed::Worker.sleep_delay }.from(5).to(1)
99
+ .and change { Delayed::Worker.sleep_delay }.from(TEST_SLEEP_DELAY).to(1)
100
100
  .and change { Delayed::Worker.read_ahead }.from(5).to(3)
101
101
  .and change { Delayed::Worker.max_claims }.from(5).to(3)
102
102
  end
data/spec/helper.rb CHANGED
@@ -97,6 +97,11 @@ class SingletonClass
97
97
  include Singleton
98
98
  end
99
99
 
100
+ # Negative values are treated as sleep(0),
101
+ # so we can use different values to test the sleep behavior:
102
+ TEST_MIN_RESERVE_INTERVAL = -10
103
+ TEST_SLEEP_DELAY = -100
104
+
100
105
  RSpec.configure do |config|
101
106
  config.around(:each) do |example|
102
107
  aj_priority_was = ActiveJob::Base.priority
@@ -113,6 +118,14 @@ RSpec.configure do |config|
113
118
  queues_was = Delayed::Worker.queues
114
119
  read_ahead_was = Delayed::Worker.read_ahead
115
120
  sleep_delay_was = Delayed::Worker.sleep_delay
121
+ min_reserve_interval_was = Delayed::Worker.min_reserve_interval
122
+
123
+ if Gem.loaded_specs['delayed'].version >= Gem::Version.new('1.0') && min_reserve_interval_was.zero?
124
+ raise "Min reserve interval should be nonzero in v1.0 release"
125
+ end
126
+
127
+ Delayed::Worker.sleep_delay = TEST_SLEEP_DELAY
128
+ Delayed::Worker.min_reserve_interval = TEST_MIN_RESERVE_INTERVAL
116
129
 
117
130
  example.run
118
131
  ensure
@@ -130,6 +143,7 @@ RSpec.configure do |config|
130
143
  Delayed::Worker.queues = queues_was
131
144
  Delayed::Worker.read_ahead = read_ahead_was
132
145
  Delayed::Worker.sleep_delay = sleep_delay_was
146
+ Delayed::Worker.min_reserve_interval = min_reserve_interval_was
133
147
 
134
148
  Delayed::Job.delete_all
135
149
  end
data/spec/worker_spec.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Delayed::Worker do
4
- before do
5
- described_class.sleep_delay = 0
6
- end
7
-
8
4
  describe 'start' do
9
5
  it 'runs the :execute lifecycle hook' do
10
6
  performances = []
@@ -32,62 +28,74 @@ describe Delayed::Worker do
32
28
  allow(subject).to receive(:interruptable_sleep).and_call_original
33
29
  end
34
30
 
35
- context 'when there are no jobs' do
36
- before do
37
- allow(Delayed::Job).to receive(:reserve).and_return([])
38
- end
31
+ around do |example|
32
+ max_claims_was = described_class.max_claims
33
+ described_class.max_claims = max_claims
34
+ example.run
35
+ ensure
36
+ described_class.max_claims = max_claims_was
37
+ end
39
38
 
40
- it 'does not log and then sleeps' do
39
+ before do
40
+ allow(Delayed::Job).to receive(:reserve).and_return((0...jobs_returned).map { job }, [])
41
+ end
42
+
43
+ let(:max_claims) { 1 }
44
+ let(:jobs_returned) { 1 }
45
+ let(:job) do
46
+ instance_double(
47
+ Delayed::Job,
48
+ id: 123,
49
+ max_run_time: 10,
50
+ name: 'MyJob',
51
+ run_at: Delayed::Job.db_time_now,
52
+ created_at: Delayed::Job.db_time_now,
53
+ priority: Delayed::Priority.interactive,
54
+ queue: 'testqueue',
55
+ attempts: 0,
56
+ invoke_job: true,
57
+ destroy: true,
58
+ )
59
+ end
60
+
61
+ it 'logs the count and sleeps only within the loop' do
62
+ subject.run!
63
+ expect(Delayed.logger).to have_received(:info).with(/1 jobs processed/)
64
+ expect(subject).to have_received(:interruptable_sleep).once.with(a_value_within(1).of(TEST_MIN_RESERVE_INTERVAL))
65
+ expect(subject).not_to have_received(:interruptable_sleep).with(TEST_SLEEP_DELAY)
66
+ end
67
+
68
+ context 'when no jobs are returned' do
69
+ let(:jobs_returned) { 0 }
70
+
71
+ it 'does not log and then sleeps only outside of the loop' do
41
72
  subject.run!
42
73
  expect(Delayed.logger).not_to have_received(:info)
43
- expect(subject).to have_received(:interruptable_sleep)
74
+ expect(subject).to have_received(:interruptable_sleep).with(TEST_SLEEP_DELAY)
44
75
  end
45
76
  end
46
77
 
47
- context 'when there is a job worked off' do
48
- around do |example|
49
- max_claims_was = described_class.max_claims
50
- described_class.max_claims = max_claims
51
- example.run
52
- ensure
53
- described_class.max_claims = max_claims_was
54
- end
55
-
56
- before do
57
- allow(Delayed::Job).to receive(:reserve).and_return([job], [])
58
- end
59
-
60
- let(:max_claims) { 1 }
61
- let(:job) do
62
- instance_double(
63
- Delayed::Job,
64
- id: 123,
65
- max_run_time: 10,
66
- name: 'MyJob',
67
- run_at: Delayed::Job.db_time_now,
68
- created_at: Delayed::Job.db_time_now,
69
- priority: Delayed::Priority.interactive,
70
- queue: 'testqueue',
71
- attempts: 0,
72
- invoke_job: true,
73
- destroy: true,
74
- )
75
- end
78
+ context 'when max_claims is 3 and 3 jobs are returned' do
79
+ let(:max_claims) { 3 }
80
+ let(:jobs_returned) { 3 }
76
81
 
77
- it 'logs the count and does not sleep' do
82
+ it 'logs the count and sleeps only in the loop' do
78
83
  subject.run!
79
- expect(Delayed.logger).to have_received(:info).with(/1 jobs processed/)
80
- expect(subject).not_to have_received(:interruptable_sleep)
84
+ expect(Delayed.logger).to have_received(:info).with(/3 jobs processed/)
85
+ expect(subject).to have_received(:interruptable_sleep).once.with(a_value_within(1).of(TEST_MIN_RESERVE_INTERVAL))
86
+ expect(subject).not_to have_received(:interruptable_sleep).with(TEST_SLEEP_DELAY)
81
87
  end
88
+ end
82
89
 
83
- context 'when max_claims is 2' do
84
- let(:max_claims) { 2 }
90
+ context 'when max_claims is 3 and 2 jobs are returned' do
91
+ let(:max_claims) { 3 }
92
+ let(:jobs_returned) { 2 }
85
93
 
86
- it 'logs the count and sleeps' do
87
- subject.run!
88
- expect(Delayed.logger).to have_received(:info).with(/1 jobs processed/)
89
- expect(subject).to have_received(:interruptable_sleep)
90
- end
94
+ it 'logs the count and sleeps both in the loop and outside of the loop' do
95
+ subject.run!
96
+ expect(Delayed.logger).to have_received(:info).with(/2 jobs processed/)
97
+ expect(subject).to have_received(:interruptable_sleep).once.with(a_value_within(1).of(TEST_MIN_RESERVE_INTERVAL))
98
+ expect(subject).to have_received(:interruptable_sleep).once.with(TEST_SLEEP_DELAY)
91
99
  end
92
100
  end
93
101
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delayed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Griffith
@@ -19,7 +19,7 @@ authors:
19
19
  autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
- date: 2024-08-13 00:00:00.000000000 Z
22
+ date: 2024-12-18 00:00:00.000000000 Z
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
25
25
  name: activerecord