delayed 0.1.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 +7 -0
- data/LICENSE +20 -0
- data/README.md +560 -0
- data/Rakefile +35 -0
- data/lib/delayed.rb +72 -0
- data/lib/delayed/active_job_adapter.rb +65 -0
- data/lib/delayed/backend/base.rb +166 -0
- data/lib/delayed/backend/job_preparer.rb +43 -0
- data/lib/delayed/exceptions.rb +14 -0
- data/lib/delayed/job.rb +250 -0
- data/lib/delayed/lifecycle.rb +85 -0
- data/lib/delayed/message_sending.rb +65 -0
- data/lib/delayed/monitor.rb +134 -0
- data/lib/delayed/performable_mailer.rb +22 -0
- data/lib/delayed/performable_method.rb +47 -0
- data/lib/delayed/plugin.rb +15 -0
- data/lib/delayed/plugins/connection.rb +13 -0
- data/lib/delayed/plugins/instrumentation.rb +39 -0
- data/lib/delayed/priority.rb +164 -0
- data/lib/delayed/psych_ext.rb +135 -0
- data/lib/delayed/railtie.rb +7 -0
- data/lib/delayed/runnable.rb +46 -0
- data/lib/delayed/serialization/active_record.rb +18 -0
- data/lib/delayed/syck_ext.rb +42 -0
- data/lib/delayed/tasks.rb +40 -0
- data/lib/delayed/worker.rb +233 -0
- data/lib/delayed/yaml_ext.rb +10 -0
- data/lib/delayed_job.rb +1 -0
- data/lib/delayed_job_active_record.rb +1 -0
- data/lib/generators/delayed/generator.rb +7 -0
- data/lib/generators/delayed/migration_generator.rb +28 -0
- data/lib/generators/delayed/next_migration_version.rb +14 -0
- data/lib/generators/delayed/templates/migration.rb +22 -0
- data/spec/autoloaded/clazz.rb +6 -0
- data/spec/autoloaded/instance_clazz.rb +5 -0
- data/spec/autoloaded/instance_struct.rb +6 -0
- data/spec/autoloaded/struct.rb +7 -0
- data/spec/database.yml +25 -0
- data/spec/delayed/active_job_adapter_spec.rb +267 -0
- data/spec/delayed/job_spec.rb +953 -0
- data/spec/delayed/monitor_spec.rb +276 -0
- data/spec/delayed/plugins/instrumentation_spec.rb +49 -0
- data/spec/delayed/priority_spec.rb +154 -0
- data/spec/delayed/serialization/active_record_spec.rb +15 -0
- data/spec/delayed/tasks_spec.rb +116 -0
- data/spec/helper.rb +196 -0
- data/spec/lifecycle_spec.rb +77 -0
- data/spec/message_sending_spec.rb +149 -0
- data/spec/performable_mailer_spec.rb +68 -0
- data/spec/performable_method_spec.rb +123 -0
- data/spec/psych_ext_spec.rb +94 -0
- data/spec/sample_jobs.rb +117 -0
- data/spec/worker_spec.rb +235 -0
- data/spec/yaml_ext_spec.rb +48 -0
- metadata +326 -0
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Delayed::Monitor do
|
4
|
+
before do
|
5
|
+
described_class.sleep_delay = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:default_payload) do
|
9
|
+
{
|
10
|
+
table: 'delayed_jobs',
|
11
|
+
database: current_database,
|
12
|
+
database_adapter: current_adapter,
|
13
|
+
queue: 'default',
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'emits empty metrics for all default priorities' do
|
18
|
+
expect { subject.run! }
|
19
|
+
.to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
|
20
|
+
.and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
21
|
+
.and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
22
|
+
.and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
23
|
+
.and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
24
|
+
.and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
25
|
+
.and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
26
|
+
.and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
27
|
+
.and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
28
|
+
.and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
29
|
+
.and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
30
|
+
.and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
31
|
+
.and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
32
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
33
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
34
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
35
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
36
|
+
.and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
37
|
+
.and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
38
|
+
.and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
39
|
+
.and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
40
|
+
.and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
41
|
+
.and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
42
|
+
.and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
43
|
+
.and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
44
|
+
.and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
45
|
+
.and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
46
|
+
.and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
47
|
+
.and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
48
|
+
.and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
49
|
+
.and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
50
|
+
.and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
51
|
+
.and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
52
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
53
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
54
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
55
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
56
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
|
57
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
|
58
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
|
59
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when named priorities are customized' do
|
63
|
+
around do |example|
|
64
|
+
Delayed::Priority.names = { high: 0, low: 7 }
|
65
|
+
example.run
|
66
|
+
ensure
|
67
|
+
Delayed::Priority.names = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'emits empty metrics for all custom priorities' do
|
71
|
+
expect { subject.run! }
|
72
|
+
.to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
|
73
|
+
.and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
74
|
+
.and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
75
|
+
.and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
76
|
+
.and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
77
|
+
.and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
78
|
+
.and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
79
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
80
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
81
|
+
.and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
82
|
+
.and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
83
|
+
.and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
84
|
+
.and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
85
|
+
.and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
86
|
+
.and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
87
|
+
.and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
88
|
+
.and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
89
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
90
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
91
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'high')).with_value(0)
|
92
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'low')).with_value(0)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when there are jobs in the queue' do
|
97
|
+
let(:now) { Time.now.change(nsec: 0) } # rubocop:disable Rails/TimeZone
|
98
|
+
let(:job_attributes) do
|
99
|
+
{
|
100
|
+
run_at: now,
|
101
|
+
queue: 'default',
|
102
|
+
handler: "--- !ruby/object:SimpleJob\n",
|
103
|
+
attempts: 0,
|
104
|
+
}
|
105
|
+
end
|
106
|
+
let(:failed_attributes) { { run_at: now - 1.week, last_error: '123', failed_at: now - 1.day, attempts: 4, locked_at: now - 1.day } }
|
107
|
+
let(:p0_attributes) { job_attributes.merge(priority: 1) }
|
108
|
+
let(:p10_attributes) { job_attributes.merge(priority: 13) }
|
109
|
+
let(:p20_attributes) { job_attributes.merge(priority: 23) }
|
110
|
+
let(:p30_attributes) { job_attributes.merge(priority: 999) }
|
111
|
+
let(:p0_payload) { default_payload.merge(priority: 'interactive') }
|
112
|
+
let(:p10_payload) { default_payload.merge(priority: 'user_visible') }
|
113
|
+
let(:p20_payload) { default_payload.merge(priority: 'eventual') }
|
114
|
+
let(:p30_payload) { default_payload.merge(priority: 'reporting') }
|
115
|
+
let!(:p0_workable_job) { Delayed::Job.create! p0_attributes.merge(run_at: now - 30.seconds) }
|
116
|
+
let!(:p0_failed_job) { Delayed::Job.create! p0_attributes.merge(failed_attributes) }
|
117
|
+
let!(:p0_future_job) { Delayed::Job.create! p0_attributes.merge(run_at: now + 1.hour) }
|
118
|
+
let!(:p0_working_job) { Delayed::Job.create! p0_attributes.merge(locked_at: now - 3.minutes) }
|
119
|
+
let!(:p10_workable_job) { Delayed::Job.create! p10_attributes.merge(run_at: now - 2.minutes) }
|
120
|
+
let!(:p10_failed_job) { Delayed::Job.create! p10_attributes.merge(failed_attributes) }
|
121
|
+
let!(:p10_future_job) { Delayed::Job.create! p10_attributes.merge(run_at: now + 1.hour) }
|
122
|
+
let!(:p10_working_job) { Delayed::Job.create! p10_attributes.merge(locked_at: now - 7.minutes) }
|
123
|
+
let!(:p20_workable_job) { Delayed::Job.create! p20_attributes.merge(run_at: now - 1.hour) }
|
124
|
+
let!(:p20_failed_job) { Delayed::Job.create! p20_attributes.merge(failed_attributes) }
|
125
|
+
let!(:p20_future_job) { Delayed::Job.create! p20_attributes.merge(run_at: now + 1.hour) }
|
126
|
+
let!(:p20_working_job) { Delayed::Job.create! p20_attributes.merge(locked_at: now - 9.minutes) }
|
127
|
+
let!(:p30_workable_job) { Delayed::Job.create! p30_attributes.merge(run_at: now - 6.hours) }
|
128
|
+
let!(:p30_failed_job) { Delayed::Job.create! p30_attributes.merge(failed_attributes) }
|
129
|
+
let!(:p30_future_job) { Delayed::Job.create! p30_attributes.merge(run_at: now + 1.hour) }
|
130
|
+
let!(:p30_working_job) { Delayed::Job.create! p30_attributes.merge(locked_at: now - 11.minutes) }
|
131
|
+
let!(:p30_workable_job_in_other_queue) { Delayed::Job.create! p30_attributes.merge(run_at: now - 4.hours, queue: 'banana') }
|
132
|
+
|
133
|
+
around do |example|
|
134
|
+
Timecop.freeze(now) { example.run }
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'emits the expected results for each metric' do
|
138
|
+
expect { subject.run! }
|
139
|
+
.to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
|
140
|
+
.and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(4)
|
141
|
+
.and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(1)
|
142
|
+
.and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(2)
|
143
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(p0_payload).with_value(1)
|
144
|
+
.and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(1)
|
145
|
+
.and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(1)
|
146
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(1)
|
147
|
+
.and emit_notification("delayed.job.max_age").with_payload(p0_payload).with_value(30.seconds)
|
148
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).with_value(3.minutes)
|
149
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).with_value(30.0.seconds / 1.minute * 100)
|
150
|
+
.and emit_notification("delayed.job.count").with_payload(p10_payload).with_value(4)
|
151
|
+
.and emit_notification("delayed.job.future_count").with_payload(p10_payload).with_value(1)
|
152
|
+
.and emit_notification("delayed.job.locked_count").with_payload(p10_payload).with_value(2)
|
153
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(p10_payload).with_value(1)
|
154
|
+
.and emit_notification("delayed.job.failed_count").with_payload(p10_payload).with_value(1)
|
155
|
+
.and emit_notification("delayed.job.working_count").with_payload(p10_payload).with_value(1)
|
156
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p10_payload).with_value(1)
|
157
|
+
.and emit_notification("delayed.job.max_age").with_payload(p10_payload).with_value(2.minutes)
|
158
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(p10_payload).with_value(7.minutes)
|
159
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p10_payload).with_value(2.0.minutes / 3.minutes * 100)
|
160
|
+
.and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(4)
|
161
|
+
.and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(1)
|
162
|
+
.and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(2)
|
163
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(p20_payload).with_value(1)
|
164
|
+
.and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(1)
|
165
|
+
.and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(1)
|
166
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(1)
|
167
|
+
.and emit_notification("delayed.job.max_age").with_payload(p20_payload).with_value(1.hour)
|
168
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).with_value(9.minutes)
|
169
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).with_value(1.hour / 1.5.hours * 100)
|
170
|
+
.and emit_notification("delayed.job.count").with_payload(p30_payload).with_value(4)
|
171
|
+
.and emit_notification("delayed.job.future_count").with_payload(p30_payload).with_value(1)
|
172
|
+
.and emit_notification("delayed.job.locked_count").with_payload(p30_payload).with_value(2)
|
173
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(p30_payload).with_value(1)
|
174
|
+
.and emit_notification("delayed.job.failed_count").with_payload(p30_payload).with_value(1)
|
175
|
+
.and emit_notification("delayed.job.working_count").with_payload(p30_payload).with_value(1)
|
176
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p30_payload).with_value(1)
|
177
|
+
.and emit_notification("delayed.job.max_age").with_payload(p30_payload).with_value(6.hours)
|
178
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(p30_payload).with_value(11.minutes)
|
179
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p30_payload).with_value(100) # 6 hours / 4 hours (overflow)
|
180
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p30_payload.merge(queue: 'banana')).with_value(1)
|
181
|
+
.and emit_notification("delayed.job.max_age").with_payload(p30_payload.merge(queue: 'banana')).with_value(4.hours)
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'when named priorities are customized' do
|
185
|
+
around do |example|
|
186
|
+
Delayed::Priority.names = { high: 0, low: 20 }
|
187
|
+
example.run
|
188
|
+
ensure
|
189
|
+
Delayed::Priority.names = nil
|
190
|
+
end
|
191
|
+
let(:p0_payload) { default_payload.merge(priority: 'high') }
|
192
|
+
let(:p20_payload) { default_payload.merge(priority: 'low') }
|
193
|
+
|
194
|
+
it 'emits the expected results for each metric' do
|
195
|
+
expect { subject.run! }
|
196
|
+
.to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
|
197
|
+
.and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(8)
|
198
|
+
.and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(2)
|
199
|
+
.and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(4)
|
200
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(p0_payload).with_value(2)
|
201
|
+
.and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(2)
|
202
|
+
.and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(2)
|
203
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(2)
|
204
|
+
.and emit_notification("delayed.job.max_age").with_payload(p0_payload).with_value(2.minutes)
|
205
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).with_value(7.minutes)
|
206
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).with_value(0)
|
207
|
+
.and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(8)
|
208
|
+
.and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(2)
|
209
|
+
.and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(4)
|
210
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(p20_payload).with_value(2)
|
211
|
+
.and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(2)
|
212
|
+
.and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(2)
|
213
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(2)
|
214
|
+
.and emit_notification("delayed.job.max_age").with_payload(p20_payload).with_value(6.hours)
|
215
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).with_value(11.minutes)
|
216
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).with_value(0)
|
217
|
+
.and emit_notification("delayed.job.workable_count").with_payload(p20_payload.merge(queue: 'banana')).with_value(1)
|
218
|
+
.and emit_notification("delayed.job.max_age").with_payload(p20_payload.merge(queue: 'banana')).with_value(4.hours)
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when alert thresholds are specified' do
|
222
|
+
around do |example|
|
223
|
+
Delayed::Priority.alerts = { high: { age: 3.hours }, low: { age: 1.year } }
|
224
|
+
example.run
|
225
|
+
ensure
|
226
|
+
Delayed::Priority.alerts = nil
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'emits the expected alert_age_percent results' do
|
230
|
+
expect { subject.run! }
|
231
|
+
.to emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).with_value(2.0.minutes / 3.hours * 100)
|
232
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).with_value(6.0.hours / 1.year * 100)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'when worker queues are specified' do
|
238
|
+
around do |example|
|
239
|
+
Delayed::Worker.queues = %w(banana gram)
|
240
|
+
Delayed::Priority.names = { interactive: 0 } # avoid splitting by priority for simplicity
|
241
|
+
Delayed::Priority.alerts = { interactive: { age: 8.hours } }
|
242
|
+
example.run
|
243
|
+
ensure
|
244
|
+
Delayed::Priority.names = nil
|
245
|
+
Delayed::Worker.queues = []
|
246
|
+
end
|
247
|
+
let(:banana_payload) { default_payload.merge(queue: 'banana', priority: 'interactive') }
|
248
|
+
let(:gram_payload) { default_payload.merge(queue: 'gram', priority: 'interactive') }
|
249
|
+
|
250
|
+
it 'emits the expected results for each queue' do
|
251
|
+
expect { subject.run! }
|
252
|
+
.to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
|
253
|
+
.and emit_notification("delayed.job.count").with_payload(banana_payload).with_value(1)
|
254
|
+
.and emit_notification("delayed.job.future_count").with_payload(banana_payload).with_value(0)
|
255
|
+
.and emit_notification("delayed.job.locked_count").with_payload(banana_payload).with_value(0)
|
256
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(banana_payload).with_value(0)
|
257
|
+
.and emit_notification("delayed.job.failed_count").with_payload(banana_payload).with_value(0)
|
258
|
+
.and emit_notification("delayed.job.working_count").with_payload(banana_payload).with_value(0)
|
259
|
+
.and emit_notification("delayed.job.workable_count").with_payload(banana_payload).with_value(1)
|
260
|
+
.and emit_notification("delayed.job.max_age").with_payload(banana_payload).with_value(4.hours)
|
261
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(banana_payload).with_value(0)
|
262
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(banana_payload).with_value(4.0.hours / 8.hours * 100)
|
263
|
+
.and emit_notification("delayed.job.count").with_payload(gram_payload).with_value(0)
|
264
|
+
.and emit_notification("delayed.job.future_count").with_payload(gram_payload).with_value(0)
|
265
|
+
.and emit_notification("delayed.job.locked_count").with_payload(gram_payload).with_value(0)
|
266
|
+
.and emit_notification("delayed.job.erroring_count").with_payload(gram_payload).with_value(0)
|
267
|
+
.and emit_notification("delayed.job.failed_count").with_payload(gram_payload).with_value(0)
|
268
|
+
.and emit_notification("delayed.job.working_count").with_payload(gram_payload).with_value(0)
|
269
|
+
.and emit_notification("delayed.job.workable_count").with_payload(gram_payload).with_value(0)
|
270
|
+
.and emit_notification("delayed.job.max_age").with_payload(gram_payload).with_value(0)
|
271
|
+
.and emit_notification("delayed.job.max_lock_age").with_payload(gram_payload).with_value(0)
|
272
|
+
.and emit_notification("delayed.job.alert_age_percent").with_payload(gram_payload).with_value(0)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Delayed::Plugins::Instrumentation do
|
4
|
+
let!(:job) { Delayed::Job.enqueue SimpleJob.new, priority: 13, queue: 'test' }
|
5
|
+
|
6
|
+
it 'emits delayed.job.run' do
|
7
|
+
expect { Delayed::Worker.new.work_off }.to emit_notification('delayed.job.run').with_payload(
|
8
|
+
job_name: 'SimpleJob',
|
9
|
+
priority: 13,
|
10
|
+
queue: 'test',
|
11
|
+
table: 'delayed_jobs',
|
12
|
+
database: current_database,
|
13
|
+
database_adapter: current_adapter,
|
14
|
+
job: job,
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when the job errors' do
|
19
|
+
let!(:job) { Delayed::Job.enqueue ErrorJob.new, priority: 7, queue: 'foo' }
|
20
|
+
|
21
|
+
it 'emits delayed.job.error' do
|
22
|
+
expect { Delayed::Worker.new.work_off }.to emit_notification('delayed.job.error').with_payload(
|
23
|
+
job_name: 'ErrorJob',
|
24
|
+
priority: 7,
|
25
|
+
queue: 'foo',
|
26
|
+
table: 'delayed_jobs',
|
27
|
+
database: current_database,
|
28
|
+
database_adapter: current_adapter,
|
29
|
+
job: job,
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when the job fails' do
|
35
|
+
let!(:job) { Delayed::Job.enqueue FailureJob.new, priority: 3, queue: 'bar' }
|
36
|
+
|
37
|
+
it 'emits delayed.job.failure' do
|
38
|
+
expect { Delayed::Worker.new.work_off }.to emit_notification('delayed.job.failure').with_payload(
|
39
|
+
job_name: 'FailureJob',
|
40
|
+
priority: 3,
|
41
|
+
queue: 'bar',
|
42
|
+
table: 'delayed_jobs',
|
43
|
+
database: current_database,
|
44
|
+
database_adapter: current_adapter,
|
45
|
+
job: job,
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Delayed::Priority do
|
4
|
+
let(:custom_names) { nil }
|
5
|
+
let(:custom_alerts) { nil }
|
6
|
+
|
7
|
+
around do |example|
|
8
|
+
described_class.names = custom_names
|
9
|
+
described_class.alerts = custom_alerts
|
10
|
+
example.run
|
11
|
+
ensure
|
12
|
+
described_class.alerts = nil
|
13
|
+
described_class.names = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.names, .ranges, .alerts, method_missing' do
|
17
|
+
it 'defaults to interactive, user_visible, eventual, reporting' do
|
18
|
+
expect(described_class.names).to eq(
|
19
|
+
interactive: 0,
|
20
|
+
user_visible: 10,
|
21
|
+
eventual: 20,
|
22
|
+
reporting: 30,
|
23
|
+
)
|
24
|
+
expect(described_class.ranges).to eq(
|
25
|
+
interactive: (0...10),
|
26
|
+
user_visible: (10...20),
|
27
|
+
eventual: (20...30),
|
28
|
+
reporting: (30...Float::INFINITY),
|
29
|
+
)
|
30
|
+
expect(described_class.alerts).to eq(
|
31
|
+
interactive: { age: 1.minute, run_time: 30.seconds, attempts: 3 },
|
32
|
+
user_visible: { age: 3.minutes, run_time: 90.seconds, attempts: 5 },
|
33
|
+
eventual: { age: 1.5.hours, run_time: 5.minutes, attempts: 8 },
|
34
|
+
reporting: { age: 4.hours, run_time: 10.minutes, attempts: 8 },
|
35
|
+
)
|
36
|
+
expect(described_class).to respond_to(:interactive)
|
37
|
+
expect(described_class).to respond_to(:user_visible)
|
38
|
+
expect(described_class).to respond_to(:eventual)
|
39
|
+
expect(described_class).to respond_to(:reporting)
|
40
|
+
expect(described_class.interactive).to eq 0
|
41
|
+
expect(described_class.user_visible).to eq 10
|
42
|
+
expect(described_class.eventual).to eq 20
|
43
|
+
expect(described_class.reporting).to eq 30
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when customized to high, medium, low' do
|
47
|
+
let(:custom_names) { { high: 0, medium: 100, low: 500 } }
|
48
|
+
|
49
|
+
it 'returns the customized value' do
|
50
|
+
expect(described_class.names).to eq(
|
51
|
+
high: 0,
|
52
|
+
medium: 100,
|
53
|
+
low: 500,
|
54
|
+
)
|
55
|
+
expect(described_class.ranges).to eq(
|
56
|
+
high: (0...100),
|
57
|
+
medium: (100...500),
|
58
|
+
low: (500...Float::INFINITY),
|
59
|
+
)
|
60
|
+
expect(described_class.alerts).to eq({})
|
61
|
+
expect(described_class).not_to respond_to(:interactive)
|
62
|
+
expect(described_class).not_to respond_to(:user_visible)
|
63
|
+
expect(described_class).not_to respond_to(:eventual)
|
64
|
+
expect(described_class).not_to respond_to(:reporting)
|
65
|
+
expect(described_class).to respond_to(:high)
|
66
|
+
expect(described_class).to respond_to(:medium)
|
67
|
+
expect(described_class).to respond_to(:low)
|
68
|
+
expect(described_class.high).to eq 0
|
69
|
+
expect(described_class.medium).to eq 100
|
70
|
+
expect(described_class.low).to eq 500
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when custom alert thresholds are defined' do
|
74
|
+
let(:custom_alerts) { { high: { age: 1.minute }, medium: { run_time: 1.minute }, low: { attempts: 10 } } }
|
75
|
+
|
76
|
+
it 'returns the customized value' do
|
77
|
+
expect(described_class.alerts).to eq(
|
78
|
+
high: { age: 1.minute },
|
79
|
+
medium: { run_time: 1.minute },
|
80
|
+
low: { attempts: 10 },
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'provides the name of the priority range' do
|
88
|
+
expect(described_class.new(0).name).to eq :interactive
|
89
|
+
expect(described_class.new(3).name).to eq :interactive
|
90
|
+
expect(described_class.new(10).name).to eq :user_visible
|
91
|
+
expect(described_class.new(29).name).to eq :eventual
|
92
|
+
expect(described_class.new(999).name).to eq :reporting
|
93
|
+
expect(described_class.new(-123).name).to eq nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'supports initialization by symbol value' do
|
97
|
+
expect(described_class.new(:interactive)).to eq(0)
|
98
|
+
expect(described_class.new(:user_visible)).to eq(10)
|
99
|
+
expect(described_class.new(:eventual)).to eq(20)
|
100
|
+
expect(described_class.new(:reporting)).to eq(30)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "supports predicate ('?') methods" do
|
104
|
+
expect(described_class.new(0).interactive?).to eq true
|
105
|
+
expect(described_class.new(3)).to be_interactive
|
106
|
+
expect(described_class.new(3).user_visible?).to eq false
|
107
|
+
expect(described_class.new(10)).to be_user_visible
|
108
|
+
expect(described_class.new(29)).to be_eventual
|
109
|
+
expect(described_class.new(999)).to be_reporting
|
110
|
+
expect(described_class.new(-123).interactive?).to eq false
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'supports alert threshold methods' do
|
114
|
+
described_class.alerts = {
|
115
|
+
interactive: { age: 77.seconds },
|
116
|
+
user_visible: { run_time: 11.seconds },
|
117
|
+
eventual: { attempts: 7 },
|
118
|
+
}
|
119
|
+
expect(described_class.interactive.alert_age).to eq 77.seconds
|
120
|
+
expect(described_class.user_visible.alert_run_time).to eq 11.seconds
|
121
|
+
expect(described_class.eventual.alert_attempts).to eq 7
|
122
|
+
expect(described_class.reporting.alert_age).to be_nil
|
123
|
+
expect(described_class.reporting.alert_run_time).to be_nil
|
124
|
+
expect(described_class.reporting.alert_attempts).to be_nil
|
125
|
+
ensure
|
126
|
+
described_class.alerts = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'supports comparisons' do
|
130
|
+
expect(described_class.new(3)).to be < described_class.new(5)
|
131
|
+
expect(described_class.new(10)).to be >= described_class.new(10)
|
132
|
+
expect(described_class.new(101)).to eq described_class.new(101) # rubocop:disable RSpec/IdenticalEqualityAssertion
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'suports coercion' do
|
136
|
+
expect(described_class.new(0)).to eq 0
|
137
|
+
expect(described_class.new(8)).to be > 5
|
138
|
+
expect(described_class.new(5)).to be < 8
|
139
|
+
expect(0 == described_class.new(0)).to eq true
|
140
|
+
expect(8 > described_class.new(5)).to eq true
|
141
|
+
expect(5 < described_class.new(8)).to eq true
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'supports sorting' do
|
145
|
+
expect(
|
146
|
+
[
|
147
|
+
described_class.new(5),
|
148
|
+
described_class.new(40),
|
149
|
+
described_class.new(3),
|
150
|
+
described_class.new(-13),
|
151
|
+
].sort,
|
152
|
+
).to eq [-13, 3, 5, 40]
|
153
|
+
end
|
154
|
+
end
|