delayed 0.5.3 → 0.5.4

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: 1a6428c0614b28cf06d2921310bea48cc8e0bcc891a9772b7aed44238dbf1533
4
- data.tar.gz: fa61f990945d382d21dd856170ce10dc52ecf44f2360a5be8ac9ec3463f9b5ac
3
+ metadata.gz: e76902c89ee215794e6c6b987b69a9c00371fd70658a3ef1accd7751587b17a5
4
+ data.tar.gz: d4e2ddc68375700e839e60aadb487bbeacf55f603d392ec0a11099429685ae77
5
5
  SHA512:
6
- metadata.gz: 46152e349a8b725978c3ef8a281789e288301e1d605b67fe4a8562733dac1742d5a9783a329c1569dce69910e73249b1134f8a5a1b19956ccb2b3a3a355dddd9
7
- data.tar.gz: f6bd7af40fd73a4d4d9fe5c2af5eb307241d627a43ecf972cc7fb6510ba1a0d2ad0b2e065fdf935bb2bc3f03ffa912f91d2cead31cd01a2ab5aa6e70d446203a
6
+ metadata.gz: 6240ae94bee15f2f434e6a73941ad3123add526ac29b56a13127aa7797fbf006dd8e1f73b6a53b533b9494d6d99b4f30550fd3d632eadc5b4cbb2c2a4dbd796c
7
+ data.tar.gz: d0fef6960b1e32f400f53779886e0465c4c7fcf8d66b30de17c588c17a2661c66391dc3ba978a67890d21799980fff22195881eb30eaef7bdfabd4049c8a21ee
data/README.md CHANGED
@@ -491,6 +491,14 @@ Delayed::Worker.min_priority = nil
491
491
  Delayed::Worker.max_priority = nil
492
492
  ```
493
493
 
494
+ Job priorities can specified by using the name of the desired range (i.e. :user_visible).
495
+ By default, the value for a named priority will be the first value in that range.
496
+ To set each priority's default value to the middle of its range (i.e. 15 for :user_visible), Delayed::Priority can be configured with:
497
+
498
+ ```ruby
499
+ Delayed::Priority.assign_at_midpoint = true
500
+ ```
501
+
494
502
  Logging verbosity is also configurable. The gem will attempt to default to `Rails.logger` with an
495
503
  "info" log level.
496
504
 
@@ -1,5 +1,11 @@
1
1
  module Delayed
2
2
  class ActiveJobAdapter
3
+ class UnsafeEnqueueError < RuntimeError; end
4
+
5
+ def enqueue_after_transaction_commit?
6
+ false
7
+ end
8
+
3
9
  def enqueue(job)
4
10
  _enqueue(job)
5
11
  end
@@ -11,6 +17,10 @@ module Delayed
11
17
  private
12
18
 
13
19
  def _enqueue(job, opts = {})
20
+ if job.class.respond_to?(:enqueue_after_transaction_commit) && job.class.enqueue_after_transaction_commit
21
+ raise UnsafeEnqueueError, "The ':delayed' ActiveJob adapter is not compatible with enqueue_after_transaction_commit"
22
+ end
23
+
14
24
  opts.merge!({ queue: job.queue_name, priority: job.priority }.compact)
15
25
  .merge!(job.provider_attributes || {})
16
26
 
@@ -57,6 +57,8 @@ module Delayed
57
57
  }.freeze
58
58
 
59
59
  class << self
60
+ attr_writer :assign_at_midpoint
61
+
60
62
  def names
61
63
  @names || default_names
62
64
  end
@@ -70,6 +72,7 @@ module Delayed
70
72
 
71
73
  @ranges = nil
72
74
  @alerts = nil
75
+ @names_to_priority = nil
73
76
  @names = names&.sort_by(&:last)&.to_h&.transform_values { |v| new(v) }
74
77
  end
75
78
 
@@ -82,12 +85,25 @@ module Delayed
82
85
  @alerts = alerts&.sort_by { |k, _| names.keys.index(k) }&.to_h
83
86
  end
84
87
 
88
+ def assign_at_midpoint?
89
+ @assign_at_midpoint || false
90
+ end
91
+
85
92
  def ranges
86
93
  @ranges ||= names.zip(names.except(names.keys.first)).each_with_object({}) do |((name, lower), (_, upper)), obj|
87
94
  obj[name] = (lower...(upper || Float::INFINITY))
88
95
  end
89
96
  end
90
97
 
98
+ def names_to_priority
99
+ @names_to_priority ||=
100
+ if assign_at_midpoint?
101
+ names_to_midpoint_priority
102
+ else
103
+ names
104
+ end
105
+ end
106
+
91
107
  private
92
108
 
93
109
  def default_names
@@ -98,13 +114,23 @@ module Delayed
98
114
  @names ? {} : DEFAULT_ALERTS
99
115
  end
100
116
 
117
+ def names_to_midpoint_priority
118
+ names.each_cons(2).to_h { |(name, priority_value), (_, next_priority_value)|
119
+ [name, new(midpoint(priority_value, next_priority_value))]
120
+ }.merge(names.keys.last => new(names.values.last + 5))
121
+ end
122
+
123
+ def midpoint(low, high)
124
+ low + ((high - low).to_d / 2).ceil
125
+ end
126
+
101
127
  def respond_to_missing?(method_name, include_private = false)
102
- names.key?(method_name) || super
128
+ names_to_priority.key?(method_name) || super
103
129
  end
104
130
 
105
131
  def method_missing(method_name, *args)
106
- if names.key?(method_name) && args.none?
107
- names[method_name]
132
+ if names_to_priority.key?(method_name) && args.none?
133
+ names_to_priority[method_name]
108
134
  else
109
135
  super
110
136
  end
@@ -118,7 +144,7 @@ module Delayed
118
144
 
119
145
  def initialize(value)
120
146
  super()
121
- value = self.class.names[value] if value.is_a?(Symbol)
147
+ value = self.class.names_to_priority[value] if value.is_a?(Symbol)
122
148
  @value = value.to_i
123
149
  end
124
150
 
@@ -147,6 +173,20 @@ module Delayed
147
173
  to_i <=> other
148
174
  end
149
175
 
176
+ def -(other)
177
+ other = other.to_i if other.is_a?(self.class)
178
+ self.class.new(to_i - other)
179
+ end
180
+
181
+ def +(other)
182
+ other = other.to_i if other.is_a?(self.class)
183
+ self.class.new(to_i + other)
184
+ end
185
+
186
+ def to_d
187
+ to_i.to_d
188
+ end
189
+
150
190
  private
151
191
 
152
192
  def respond_to_missing?(method_name, include_private = false)
@@ -287,6 +287,19 @@ RSpec.describe Delayed::ActiveJobAdapter do
287
287
  end
288
288
  end
289
289
 
290
+ if ActiveJob.gem_version.release >= Gem::Version.new('7.2')
291
+ context 'when the given job sets enqueue_after_transaction_commit to true' do
292
+ before do
293
+ JobClass.include ActiveJob::EnqueueAfterTransactionCommit # normally run in an ActiveJob railtie
294
+ JobClass.enqueue_after_transaction_commit = true
295
+ end
296
+
297
+ it 'raises an exception on enqueue' do
298
+ expect { JobClass.perform_later }.to raise_error(Delayed::ActiveJobAdapter::UnsafeEnqueueError)
299
+ end
300
+ end
301
+ end
302
+
290
303
  context 'when using the ActiveJob test adapter' do
291
304
  let(:queue_adapter) { :test }
292
305
 
@@ -3,8 +3,10 @@ require 'helper'
3
3
  RSpec.describe Delayed::Priority do
4
4
  let(:custom_names) { nil }
5
5
  let(:custom_alerts) { nil }
6
+ let(:assign_at_midpoint) { nil }
6
7
 
7
8
  around do |example|
9
+ described_class.assign_at_midpoint = assign_at_midpoint
8
10
  described_class.names = custom_names
9
11
  described_class.alerts = custom_alerts
10
12
  example.run
@@ -13,7 +15,7 @@ RSpec.describe Delayed::Priority do
13
15
  described_class.names = nil
14
16
  end
15
17
 
16
- describe '.names, .ranges, .alerts, method_missing' do
18
+ describe '.names, .ranges, .alerts, .names_to_priority, method_missing' do
17
19
  it 'defaults to interactive, user_visible, eventual, reporting' do
18
20
  expect(described_class.names).to eq(
19
21
  interactive: 0,
@@ -33,6 +35,12 @@ RSpec.describe Delayed::Priority do
33
35
  eventual: { age: 1.5.hours, run_time: 5.minutes, attempts: 8 },
34
36
  reporting: { age: 4.hours, run_time: 10.minutes, attempts: 8 },
35
37
  )
38
+ expect(described_class.names_to_priority).to eq(
39
+ interactive: 0,
40
+ user_visible: 10,
41
+ eventual: 20,
42
+ reporting: 30,
43
+ )
36
44
  expect(described_class).to respond_to(:interactive)
37
45
  expect(described_class).to respond_to(:user_visible)
38
46
  expect(described_class).to respond_to(:eventual)
@@ -43,6 +51,23 @@ RSpec.describe Delayed::Priority do
43
51
  expect(described_class.reporting).to eq 30
44
52
  end
45
53
 
54
+ context 'when assign_at_midpoint is set to true' do
55
+ let(:assign_at_midpoint) { true }
56
+
57
+ it 'returns the midpoint value' do
58
+ expect(described_class.names_to_priority).to eq(
59
+ interactive: 5,
60
+ user_visible: 15,
61
+ eventual: 25,
62
+ reporting: 35,
63
+ )
64
+ expect(described_class.interactive).to eq 5
65
+ expect(described_class.user_visible).to eq 15
66
+ expect(described_class.eventual).to eq 25
67
+ expect(described_class.reporting).to eq 35
68
+ end
69
+ end
70
+
46
71
  context 'when customized to high, medium, low' do
47
72
  let(:custom_names) { { high: 0, medium: 100, low: 500 } }
48
73
 
@@ -57,6 +82,11 @@ RSpec.describe Delayed::Priority do
57
82
  medium: (100...500),
58
83
  low: (500...Float::INFINITY),
59
84
  )
85
+ expect(described_class.names_to_priority).to eq(
86
+ high: 0,
87
+ medium: 100,
88
+ low: 500,
89
+ )
60
90
  expect(described_class.alerts).to eq({})
61
91
  expect(described_class).not_to respond_to(:interactive)
62
92
  expect(described_class).not_to respond_to(:user_visible)
@@ -81,6 +111,21 @@ RSpec.describe Delayed::Priority do
81
111
  )
82
112
  end
83
113
  end
114
+
115
+ context 'when assign_at_midpoint is set to true' do
116
+ let(:assign_at_midpoint) { true }
117
+
118
+ it 'returns the midpoint value' do
119
+ expect(described_class.names_to_priority).to eq(
120
+ high: 50,
121
+ medium: 300,
122
+ low: 505,
123
+ )
124
+ expect(described_class.high).to eq 50
125
+ expect(described_class.medium).to eq 300
126
+ expect(described_class.low).to eq 505
127
+ end
128
+ end
84
129
  end
85
130
  end
86
131
 
@@ -110,6 +155,36 @@ RSpec.describe Delayed::Priority do
110
155
  expect(described_class.new(-123).interactive?).to eq false
111
156
  end
112
157
 
158
+ context 'when assign_at_midpoint is set to true' do
159
+ let(:assign_at_midpoint) { true }
160
+
161
+ it 'provides the name of the priority range' do
162
+ expect(described_class.new(0).name).to eq :interactive
163
+ expect(described_class.new(3).name).to eq :interactive
164
+ expect(described_class.new(10).name).to eq :user_visible
165
+ expect(described_class.new(29).name).to eq :eventual
166
+ expect(described_class.new(999).name).to eq :reporting
167
+ expect(described_class.new(-123).name).to eq nil
168
+ end
169
+
170
+ it 'supports initialization by symbol value' do
171
+ expect(described_class.new(:interactive)).to eq(5)
172
+ expect(described_class.new(:user_visible)).to eq(15)
173
+ expect(described_class.new(:eventual)).to eq(25)
174
+ expect(described_class.new(:reporting)).to eq(35)
175
+ end
176
+
177
+ it "supports predicate ('?') methods" do
178
+ expect(described_class.new(0).interactive?).to eq true
179
+ expect(described_class.new(3)).to be_interactive
180
+ expect(described_class.new(3).user_visible?).to eq false
181
+ expect(described_class.new(10)).to be_user_visible
182
+ expect(described_class.new(29)).to be_eventual
183
+ expect(described_class.new(999)).to be_reporting
184
+ expect(described_class.new(-123).interactive?).to eq false
185
+ end
186
+ end
187
+
113
188
  it 'supports alert threshold methods' do
114
189
  described_class.alerts = {
115
190
  interactive: { age: 77.seconds },
@@ -151,4 +226,13 @@ RSpec.describe Delayed::Priority do
151
226
  ].sort,
152
227
  ).to eq [-13, 3, 5, 40]
153
228
  end
229
+
230
+ it 'supports addition and subtraction' do
231
+ expect(described_class.new(0) + 10).to eq(10)
232
+ expect(10 + described_class.new(5)).to eq(15)
233
+ expect(described_class.new(0) + described_class.new(33)).to eq(33)
234
+ expect(described_class.new(10) - 5).to eq(5)
235
+ expect(15 - described_class.new(10)).to eq(5)
236
+ expect(described_class.new(5) - described_class.new(15)).to eq(-10)
237
+ end
154
238
  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.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Griffith
@@ -16,10 +16,10 @@ authors:
16
16
  - Matt Griffin
17
17
  - Steve Richert
18
18
  - Tobias Lütke
19
- autorequire:
19
+ autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
- date: 2024-01-31 00:00:00.000000000 Z
22
+ date: 2024-05-01 00:00:00.000000000 Z
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
25
25
  name: activerecord
@@ -124,7 +124,7 @@ metadata:
124
124
  bug_tracker_uri: https://github.com/betterment/delayed/issues
125
125
  source_code_uri: https://github.com/betterment/delayed
126
126
  rubygems_mfa_required: 'true'
127
- post_install_message:
127
+ post_install_message:
128
128
  rdoc_options: []
129
129
  require_paths:
130
130
  - lib
@@ -139,30 +139,30 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
139
  - !ruby/object:Gem::Version
140
140
  version: '0'
141
141
  requirements: []
142
- rubygems_version: 3.3.5
143
- signing_key:
142
+ rubygems_version: 3.3.26
143
+ signing_key:
144
144
  specification_version: 4
145
145
  summary: a multi-threaded, SQL-driven ActiveJob backend used at Betterment to process
146
146
  millions of background jobs per day
147
147
  test_files:
148
- - spec/sample_jobs.rb
149
- - spec/lifecycle_spec.rb
150
- - spec/performable_method_spec.rb
151
- - spec/helper.rb
152
- - spec/psych_ext_spec.rb
153
- - spec/worker_spec.rb
154
- - spec/autoloaded/struct.rb
155
148
  - spec/autoloaded/clazz.rb
156
149
  - spec/autoloaded/instance_clazz.rb
157
150
  - spec/autoloaded/instance_struct.rb
151
+ - spec/autoloaded/struct.rb
158
152
  - spec/database.yml
159
- - spec/delayed/priority_spec.rb
160
- - spec/delayed/plugins/instrumentation_spec.rb
153
+ - spec/delayed/active_job_adapter_spec.rb
154
+ - spec/delayed/job_spec.rb
161
155
  - spec/delayed/monitor_spec.rb
156
+ - spec/delayed/plugins/instrumentation_spec.rb
157
+ - spec/delayed/priority_spec.rb
162
158
  - spec/delayed/serialization/active_record_spec.rb
163
159
  - spec/delayed/tasks_spec.rb
164
- - spec/delayed/job_spec.rb
165
- - spec/delayed/active_job_adapter_spec.rb
160
+ - spec/helper.rb
161
+ - spec/lifecycle_spec.rb
166
162
  - spec/message_sending_spec.rb
167
163
  - spec/performable_mailer_spec.rb
164
+ - spec/performable_method_spec.rb
165
+ - spec/psych_ext_spec.rb
166
+ - spec/sample_jobs.rb
167
+ - spec/worker_spec.rb
168
168
  - spec/yaml_ext_spec.rb