delayed 0.5.3 → 0.5.4

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