jiggler 0.1.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE +4 -0
- data/README.md +423 -0
- data/bin/jiggler +31 -0
- data/lib/jiggler/cleaner.rb +130 -0
- data/lib/jiggler/cli.rb +263 -0
- data/lib/jiggler/config.rb +165 -0
- data/lib/jiggler/core.rb +22 -0
- data/lib/jiggler/errors.rb +5 -0
- data/lib/jiggler/job.rb +116 -0
- data/lib/jiggler/launcher.rb +69 -0
- data/lib/jiggler/manager.rb +73 -0
- data/lib/jiggler/redis_store.rb +55 -0
- data/lib/jiggler/retrier.rb +122 -0
- data/lib/jiggler/scheduled/enqueuer.rb +78 -0
- data/lib/jiggler/scheduled/poller.rb +97 -0
- data/lib/jiggler/stats/collection.rb +26 -0
- data/lib/jiggler/stats/monitor.rb +103 -0
- data/lib/jiggler/summary.rb +101 -0
- data/lib/jiggler/support/helper.rb +35 -0
- data/lib/jiggler/version.rb +5 -0
- data/lib/jiggler/web/assets/stylesheets/application.css +64 -0
- data/lib/jiggler/web/views/application.erb +329 -0
- data/lib/jiggler/web.rb +80 -0
- data/lib/jiggler/worker.rb +179 -0
- data/lib/jiggler.rb +10 -0
- data/spec/examples.txt +79 -0
- data/spec/fixtures/config/jiggler.yml +4 -0
- data/spec/fixtures/jobs.rb +5 -0
- data/spec/fixtures/my_failed_job.rb +10 -0
- data/spec/fixtures/my_job.rb +9 -0
- data/spec/fixtures/my_job_with_args.rb +18 -0
- data/spec/jiggler/cleaner_spec.rb +171 -0
- data/spec/jiggler/cli_spec.rb +87 -0
- data/spec/jiggler/config_spec.rb +56 -0
- data/spec/jiggler/core_spec.rb +34 -0
- data/spec/jiggler/job_spec.rb +99 -0
- data/spec/jiggler/launcher_spec.rb +66 -0
- data/spec/jiggler/manager_spec.rb +52 -0
- data/spec/jiggler/redis_store_spec.rb +20 -0
- data/spec/jiggler/retrier_spec.rb +55 -0
- data/spec/jiggler/scheduled/enqueuer_spec.rb +81 -0
- data/spec/jiggler/scheduled/poller_spec.rb +40 -0
- data/spec/jiggler/stats/monitor_spec.rb +40 -0
- data/spec/jiggler/summary_spec.rb +168 -0
- data/spec/jiggler/web_spec.rb +37 -0
- data/spec/jiggler/worker_spec.rb +110 -0
- data/spec/spec_helper.rb +54 -0
- metadata +230 -0
@@ -0,0 +1,329 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Jiggler</title>
|
5
|
+
<style>
|
6
|
+
<%= styles %>
|
7
|
+
</style>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<h1>Jiggler</h1>
|
11
|
+
<div id='content'>
|
12
|
+
|
13
|
+
<div class='stats-block'>
|
14
|
+
<div class='stats-entry' title='Jobs to be retried'>
|
15
|
+
<span><%= "To Retry: #{@summary['retry_jobs_count']}" %></span>
|
16
|
+
</div>
|
17
|
+
<div class='stats-entry' title='Jobs with delayed enqueuing'>
|
18
|
+
<span><%= "Scheduled: #{@summary['scheduled_jobs_count']}" %></span>
|
19
|
+
</div>
|
20
|
+
<div class='stats-entry' title='Completely failed jobs'>
|
21
|
+
<span><%= "Dead: #{@summary['dead_jobs_count']}" %></span>
|
22
|
+
</div>
|
23
|
+
<div class='stats-entry' title='Unsuccessful perform attempts'>
|
24
|
+
<span><%= "Failures: #{@summary['failures_count']}" %></span>
|
25
|
+
</div>
|
26
|
+
<div class='stats-entry' title='Successfully completed jobs'>
|
27
|
+
<span><%= "Processed: #{@summary['processed_count']}" %></span>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<table class='main-table processes'>
|
32
|
+
<thead>
|
33
|
+
<tr>
|
34
|
+
<th colspan='5' title='Process'>
|
35
|
+
<span>Process</span>
|
36
|
+
</th>
|
37
|
+
<th colspan='1' title='PID'>
|
38
|
+
<span>PID</span>
|
39
|
+
</th>
|
40
|
+
<th colspan='2' title='Concurrency'>
|
41
|
+
<span>Concurrency</span>
|
42
|
+
</th>
|
43
|
+
<th colspan='2' title='RSS'>
|
44
|
+
<span>RSS</span>
|
45
|
+
</th>
|
46
|
+
<th colspan='1' title='Last Heartbeat'>
|
47
|
+
<span>Heartbeat</span>
|
48
|
+
</th>
|
49
|
+
<th colspan='1' title='Started'>
|
50
|
+
<span>Started</span>
|
51
|
+
</th>
|
52
|
+
</tr>
|
53
|
+
</thead>
|
54
|
+
<tbody>
|
55
|
+
<% @summary['processes'].each do |uuid, data| %>
|
56
|
+
<tr class=<%= heartbeat_class(data['heartbeat']) %>>
|
57
|
+
<td colspan='5' title='Process'>
|
58
|
+
<span><%= "#{data['name']} - #{data['hostname']}" %></span>
|
59
|
+
<%= poller_badge(data['poller_enabled']) %>
|
60
|
+
</br>
|
61
|
+
<span><i><%= data['queues'] %></i></span>
|
62
|
+
</td>
|
63
|
+
<td colspan='1' title='PID' class='right'>
|
64
|
+
<span><%= data['pid'] %></span>
|
65
|
+
</td>
|
66
|
+
<td colspan='2' title='Concurrency' class='right'>
|
67
|
+
<span><%= data['concurrency'] %></span>
|
68
|
+
</td>
|
69
|
+
<td colspan='2' title=<%= data['rss'] %> class='right'>
|
70
|
+
<span><%= format_memory(data['rss']) %></span>
|
71
|
+
</td>
|
72
|
+
<td colspan='1' title=<%= format_datetime(data['heartbeat']) %> class='right'>
|
73
|
+
<span><%= time_ago_in_words(data['heartbeat']) %></span>
|
74
|
+
</td>
|
75
|
+
<td colspan='1' title=<%= format_datetime(data['started_at']) %> class='right'>
|
76
|
+
<span><%= time_ago_in_words(data['started_at']) %></span>
|
77
|
+
</td>
|
78
|
+
</tr>
|
79
|
+
<% end %>
|
80
|
+
</tbody>
|
81
|
+
</table>
|
82
|
+
|
83
|
+
<table class='main-table queues'>
|
84
|
+
<thead>
|
85
|
+
<tr>
|
86
|
+
<th colspan='6' title='Queue'>
|
87
|
+
<span>Queues</span>
|
88
|
+
</th>
|
89
|
+
<th colspan='6' title='Size'>
|
90
|
+
<span>Size</span>
|
91
|
+
</th>
|
92
|
+
</tr>
|
93
|
+
</thead>
|
94
|
+
<tbody>
|
95
|
+
<% @summary['queues'].each do |queue| %>
|
96
|
+
<tr>
|
97
|
+
<td colspan='6' title='Queue'>
|
98
|
+
<span><i><%= queue[0] %></i></span>
|
99
|
+
</td>
|
100
|
+
<td colspan='6' title='Size' class='right'>
|
101
|
+
<span><%= queue[1] %></span>
|
102
|
+
</td>
|
103
|
+
</tr>
|
104
|
+
<% end %>
|
105
|
+
</tbody>
|
106
|
+
</table>
|
107
|
+
|
108
|
+
<h3>Busy Jobs</h3>
|
109
|
+
<table class='main-table jobs'>
|
110
|
+
<thead>
|
111
|
+
<tr>
|
112
|
+
<th colspan='2' title='Process'>
|
113
|
+
<span>Process</span>
|
114
|
+
</th>
|
115
|
+
<th colspan='2' title='Job'>
|
116
|
+
<span>Job</span>
|
117
|
+
</th>
|
118
|
+
<th colspan='1' title='JID'>
|
119
|
+
<span>JID</span>
|
120
|
+
</th>
|
121
|
+
<th colspan='1' title='TID'>
|
122
|
+
<span>TID</span>
|
123
|
+
</th>
|
124
|
+
<th colspan='2' title='Queue'>
|
125
|
+
<span>Queue</span>
|
126
|
+
</th>
|
127
|
+
<th colspan='2' title='Args'>
|
128
|
+
<span>Args</span>
|
129
|
+
</th>
|
130
|
+
<th colspan='2' title='Started'>
|
131
|
+
<span>Started</span>
|
132
|
+
</th>
|
133
|
+
</tr>
|
134
|
+
</thead>
|
135
|
+
<tbody>
|
136
|
+
<% @summary['processes'].each do |uuid, process_data| %>
|
137
|
+
<% process_data['current_jobs'].each do |tid, job_data| %>
|
138
|
+
<tr>
|
139
|
+
<td colspan='2' title='Process'>
|
140
|
+
<span><%= uuid %></span></br>
|
141
|
+
</td>
|
142
|
+
<td colspan='2' title='Job'>
|
143
|
+
<span><%= job_data['job_args']['name'] %></span>
|
144
|
+
</td>
|
145
|
+
<td colspan='1' title='JID'>
|
146
|
+
<span><%= job_data['jid'] %></span>
|
147
|
+
</td>
|
148
|
+
<td colspan='1' title='TID'>
|
149
|
+
<span><%= tid %></span>
|
150
|
+
</td>
|
151
|
+
<td colspan='2' title='Queue'>
|
152
|
+
<span><%= job_data['queue'] %></span>
|
153
|
+
</td>
|
154
|
+
<td colspan='2' title='Arguments'>
|
155
|
+
<span><%= format_args(job_data['job_args']['args']) %></span>
|
156
|
+
</td>
|
157
|
+
<td colspan='2' title=<%= format_datetime(data['started_at']) %>>
|
158
|
+
<span><%= time_ago_in_words(data['started_at']) %></span>
|
159
|
+
</td>
|
160
|
+
</tr>
|
161
|
+
<% end %>
|
162
|
+
<% end %>
|
163
|
+
</tbody>
|
164
|
+
</table>
|
165
|
+
|
166
|
+
<% if @summary['retry_jobs_count'] > 0 %>
|
167
|
+
<h3>Last 5 Jobs to Retry</h3>
|
168
|
+
<table class='main-table retry_jobs'>
|
169
|
+
<thead>
|
170
|
+
<tr>
|
171
|
+
<th colspan='1' title='Job'>
|
172
|
+
<span>Job</span>
|
173
|
+
</th>
|
174
|
+
<th colspan='1' title='JID'>
|
175
|
+
<span>JID</span>
|
176
|
+
</th>
|
177
|
+
<th colspan='1' title='Queue'>
|
178
|
+
<span>Queue</span>
|
179
|
+
</th>
|
180
|
+
<th colspan='1' title='Error'>
|
181
|
+
<span>Error</span>
|
182
|
+
</th>
|
183
|
+
<th colspan='2' title='Args'>
|
184
|
+
<span>Args</span>
|
185
|
+
</th>
|
186
|
+
<th colspan='1' title='Attempt'>
|
187
|
+
<span>Attempt</span>
|
188
|
+
</th>
|
189
|
+
<th colspan='1' title='Started'>
|
190
|
+
<span>Started</span>
|
191
|
+
</th>
|
192
|
+
<th colspan='1' title='Retried'>
|
193
|
+
<span>Retried</span>
|
194
|
+
</th>
|
195
|
+
<th colspan='1' title='To Retry At'>
|
196
|
+
<span>To Retry At</span>
|
197
|
+
</th>
|
198
|
+
</tr>
|
199
|
+
</thead>
|
200
|
+
<tbody>
|
201
|
+
<% last_5_retry_jobs.each do |data| %>
|
202
|
+
<tr>
|
203
|
+
<td colspan='1' title='Job'>
|
204
|
+
<span><%= data['name'] %></span>
|
205
|
+
</td>
|
206
|
+
<td colspan='1' title='JID'>
|
207
|
+
<span><%= data['jid'] %></span>
|
208
|
+
</td>
|
209
|
+
<td colspan='1' title='Queue'>
|
210
|
+
<span><%= data['queue'] %></span>
|
211
|
+
</td>
|
212
|
+
<td colspan='1' title='Error'>
|
213
|
+
<span><%= "#{data['error_class']}: #{data['error_message']}" %></span>
|
214
|
+
</td>
|
215
|
+
<td colspan='2' title='Args'>
|
216
|
+
<span><%= data['args'] %></span>
|
217
|
+
</td>
|
218
|
+
<td colspan='1' title='Attempt' class='right'>
|
219
|
+
<span><%= data['attempt'] %></span>
|
220
|
+
</td>
|
221
|
+
<td colspan='1' title=<%= format_datetime(data['started_at']) %> class='right'>
|
222
|
+
<span><%= time_ago_in_words(data['started_at']) %></span>
|
223
|
+
</td>
|
224
|
+
<td colspan='1' title=<%= format_datetime(data['retried_at']) %> class='right'>
|
225
|
+
<span><%= time_ago_in_words(data['retried_at']) %></span>
|
226
|
+
</td>
|
227
|
+
<td colspan='1' title=<%= format_datetime(data['retry_at']) %> class='right'>
|
228
|
+
<span><%= format_datetime(data['retry_at']) %></span>
|
229
|
+
</td>
|
230
|
+
</tr>
|
231
|
+
<% end %>
|
232
|
+
</tbody>
|
233
|
+
</table>
|
234
|
+
<% end %>
|
235
|
+
|
236
|
+
<% if @summary['scheduled_jobs_count'] > 0 %>
|
237
|
+
<h3>Last 5 Scheduled Jobs</h3>
|
238
|
+
<table class='main-table retry_jobs'>
|
239
|
+
<thead>
|
240
|
+
<tr>
|
241
|
+
<th colspan='4' title='Job'>
|
242
|
+
<span>Job</span>
|
243
|
+
</th>
|
244
|
+
<th colspan='6' title='Args'>
|
245
|
+
<span>Args</span>
|
246
|
+
</th>
|
247
|
+
<th colspan='2' title='Scheduled At'>
|
248
|
+
<span>To be enqueued at</span>
|
249
|
+
</th>
|
250
|
+
</tr>
|
251
|
+
</thead>
|
252
|
+
<tbody>
|
253
|
+
<% last_5_scheduled_jobs.each do |data| %>
|
254
|
+
<tr>
|
255
|
+
<td colspan='4' title='Job'>
|
256
|
+
<span><%= data['name'] %></span>
|
257
|
+
</td>
|
258
|
+
<td colspan='6' title='Args'>
|
259
|
+
<span><%= format_args(data['args']) %></span>
|
260
|
+
</td>
|
261
|
+
<td colspan='2' title=<%= format_datetime(data['scheduled_at']) %> class='right'>
|
262
|
+
<span><%= format_datetime(data['scheduled_at']) %></span>
|
263
|
+
</td>
|
264
|
+
</tr>
|
265
|
+
<% end %>
|
266
|
+
</tbody>
|
267
|
+
</table>
|
268
|
+
<% end %>
|
269
|
+
|
270
|
+
<% if @summary['dead_jobs_count'] > 0 %>
|
271
|
+
<h3>Last 5 Dead Jobs</h3>
|
272
|
+
<table class='main-table dead_jobs'>
|
273
|
+
<thead>
|
274
|
+
<tr>
|
275
|
+
<th colspan='2' title='Job'>
|
276
|
+
<span>Job</span>
|
277
|
+
</th>
|
278
|
+
<th colspan='1' title='JID'>
|
279
|
+
<span>JID</span>
|
280
|
+
</th>
|
281
|
+
<th colspan='1' title='Queue'>
|
282
|
+
<span>Queue</span>
|
283
|
+
</th>
|
284
|
+
<th colspan='2' title='Error'>
|
285
|
+
<span>Error</span>
|
286
|
+
</th>
|
287
|
+
<th colspan='2' title='Args'>
|
288
|
+
<span>Args</span>
|
289
|
+
</th>
|
290
|
+
<th colspan='1' title='Started'>
|
291
|
+
<span>Started</span>
|
292
|
+
</th>
|
293
|
+
<th colspan='1' title='Retried'>
|
294
|
+
<span>Retried</span>
|
295
|
+
</th>
|
296
|
+
</tr>
|
297
|
+
</thead>
|
298
|
+
<tbody>
|
299
|
+
<% last_5_dead_jobs.each do |data| %>
|
300
|
+
<tr>
|
301
|
+
<td colspan='2' title='Job'>
|
302
|
+
<span><%= data['name'] %></span>
|
303
|
+
</td>
|
304
|
+
<td colspan='1' title='JID'>
|
305
|
+
<span><%= data['jid'] %></span>
|
306
|
+
</td>
|
307
|
+
<td colspan='1' title='Queue'>
|
308
|
+
<span><%= data['queue'] %></span>
|
309
|
+
</td>
|
310
|
+
<td colspan='2' title='Error'>
|
311
|
+
<span><%= "#{data['error_class']}: #{data['error_message']}" %></span>
|
312
|
+
</td>
|
313
|
+
<td colspan='2' title='Args'>
|
314
|
+
<span><%= format_args(data['args']) %></span>
|
315
|
+
</td>
|
316
|
+
<td colspan='1' title=<%= format_datetime(data['started_at']) %> class='right'>
|
317
|
+
<span><%= time_ago_in_words(data['started_at']) %></span>
|
318
|
+
</td>
|
319
|
+
<td colspan='1' title=<%= format_datetime(data['retried_at']) %> class='right'>
|
320
|
+
<span><%= time_ago_in_words(data['retried_at']) %></span>
|
321
|
+
</td>
|
322
|
+
</tr>
|
323
|
+
<% end %>
|
324
|
+
</tbody>
|
325
|
+
</table>
|
326
|
+
<% end %>
|
327
|
+
</div>
|
328
|
+
</body>
|
329
|
+
</html>
|
data/lib/jiggler/web.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Jiggler
|
6
|
+
class Web
|
7
|
+
WEB_PATH = File.expand_path("#{File.dirname(__FILE__)}/web")
|
8
|
+
LAYOUT = "#{WEB_PATH}/views/application.erb"
|
9
|
+
STYLESHEET = "#{WEB_PATH}/assets/stylesheets/application.css"
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@summary_instance = Jiggler::Summary.new(Jiggler.config)
|
13
|
+
@summary = @summary_instance.all
|
14
|
+
compiled_template = ERB.new(File.read(LAYOUT)).result(binding)
|
15
|
+
[200, {}, [compiled_template]]
|
16
|
+
end
|
17
|
+
|
18
|
+
def last_5_dead_jobs
|
19
|
+
@summary_instance.last_dead_jobs(5)
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_5_retry_jobs
|
23
|
+
@summary_instance.last_retry_jobs(5)
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_5_scheduled_jobs
|
27
|
+
@summary_instance.last_scheduled_jobs(5)
|
28
|
+
end
|
29
|
+
|
30
|
+
def format_datetime(timestamp)
|
31
|
+
return if timestamp.nil?
|
32
|
+
Time.at(timestamp.to_f).to_datetime
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_memory(kb)
|
36
|
+
return '?' if kb.nil?
|
37
|
+
"#{(kb/1024.0).round(2)} MB"
|
38
|
+
end
|
39
|
+
|
40
|
+
def time_ago_in_words(timestamp)
|
41
|
+
return if timestamp.nil?
|
42
|
+
seconds = Time.now.to_i - timestamp.to_i
|
43
|
+
case seconds
|
44
|
+
when 0..59
|
45
|
+
"#{seconds} seconds ago"
|
46
|
+
when 60..3599
|
47
|
+
"#{(seconds/60).round} minutes ago"
|
48
|
+
when 3600..86399
|
49
|
+
"#{(seconds/3600).round} hours ago"
|
50
|
+
when 86400..604799
|
51
|
+
"#{(seconds/86400).round} days ago"
|
52
|
+
else
|
53
|
+
"#{(seconds/604800).round} weeks ago"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def heartbeat_class(timestamp)
|
58
|
+
return 'outdated' if outdated_heartbeat?(timestamp)
|
59
|
+
end
|
60
|
+
|
61
|
+
def outdated_heartbeat?(timestamp)
|
62
|
+
return true if timestamp.nil?
|
63
|
+
seconds = Time.now.to_i - timestamp.to_i
|
64
|
+
seconds > Jiggler.config[:stats_interval] * 2
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_args(args)
|
68
|
+
return if args.nil?
|
69
|
+
args.map { |arg| Oj.dump(arg, mode: :compat) }.join(', ')
|
70
|
+
end
|
71
|
+
|
72
|
+
def poller_badge(poller_enabled)
|
73
|
+
poller_enabled ? '<span class=\'badge badge-success\'>Polling</span>' : '<span class=\'badge\'>Polling Disabled</span>'
|
74
|
+
end
|
75
|
+
|
76
|
+
def styles
|
77
|
+
@styles ||= File.read(STYLESHEET)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiggler
|
4
|
+
class Worker
|
5
|
+
include Support::Helper
|
6
|
+
TIMEOUT = 2 # timeout for brpop
|
7
|
+
|
8
|
+
CurrentJob = Struct.new(:queue, :args, keyword_init: true)
|
9
|
+
|
10
|
+
attr_reader :current_job, :config, :done, :collection
|
11
|
+
|
12
|
+
def initialize(config, collection, &callback)
|
13
|
+
@done = false
|
14
|
+
@current_job = nil
|
15
|
+
@callback = callback
|
16
|
+
@config = config
|
17
|
+
@collection = collection
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
reason = nil
|
22
|
+
@runner = safe_async('Worker') do
|
23
|
+
@tid = tid
|
24
|
+
loop do
|
25
|
+
if @done
|
26
|
+
@runner = nil
|
27
|
+
break @callback.call(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
process_job
|
31
|
+
|
32
|
+
# pass control to other fibers
|
33
|
+
Async::Task.current.yield
|
34
|
+
rescue Async::Stop
|
35
|
+
@runner = nil
|
36
|
+
break @callback.call(self)
|
37
|
+
rescue => err
|
38
|
+
collection.incr_failures
|
39
|
+
@runner = nil
|
40
|
+
break @callback.call(self, err)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def terminate
|
46
|
+
@done = true
|
47
|
+
@runner&.stop
|
48
|
+
end
|
49
|
+
|
50
|
+
def suspend
|
51
|
+
@done = true
|
52
|
+
end
|
53
|
+
|
54
|
+
def wait
|
55
|
+
@runner&.wait
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def process_job
|
61
|
+
@current_job = fetch_one
|
62
|
+
return if current_job.nil? # timed out brpop or done
|
63
|
+
execute_job
|
64
|
+
@current_job = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def fetch_one
|
68
|
+
queue, args = config.with_sync_redis { |conn| conn.blocking_call(false, 'BRPOP', *queues, TIMEOUT) }
|
69
|
+
return nil unless queue
|
70
|
+
|
71
|
+
if @done
|
72
|
+
requeue(queue, args)
|
73
|
+
nil
|
74
|
+
else
|
75
|
+
CurrentJob.new(queue: queue, args: args)
|
76
|
+
end
|
77
|
+
rescue Async::Stop => err
|
78
|
+
raise err
|
79
|
+
rescue => err
|
80
|
+
# error_message='undefined method `zero?' for nil:NilClass' context='Fetch error' tid=1tpj
|
81
|
+
# sometimes happens in async-pool-0.3.12/lib/async/pool/controller.rb:213:in `reuse'
|
82
|
+
handle_fetch_error(err)
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def execute_job
|
87
|
+
parsed_args = Oj.load(current_job.args, mode: :compat)
|
88
|
+
execute(parsed_args, current_job.queue)
|
89
|
+
rescue Async::Stop => err
|
90
|
+
raise err
|
91
|
+
rescue UnknownJobError => err
|
92
|
+
collection.incr_failures
|
93
|
+
log_error_short(
|
94
|
+
err,
|
95
|
+
{
|
96
|
+
error_class: err.class.name,
|
97
|
+
job: parsed_args,
|
98
|
+
tid: @tid
|
99
|
+
}
|
100
|
+
)
|
101
|
+
rescue JSON::ParserError => err
|
102
|
+
collection.incr_failures
|
103
|
+
logger.error('Worker') { "Failed to parse job: #{current_job.args}" }
|
104
|
+
rescue Exception => ex
|
105
|
+
log_error(
|
106
|
+
ex,
|
107
|
+
{
|
108
|
+
context: '\'Internal exception\'',
|
109
|
+
tid: @tid,
|
110
|
+
jid: parsed_args['jid']
|
111
|
+
}
|
112
|
+
)
|
113
|
+
# raise ex
|
114
|
+
end
|
115
|
+
|
116
|
+
def execute(parsed_job, queue)
|
117
|
+
instance = constantize(parsed_job['name']).new
|
118
|
+
add_current_job_to_collection(parsed_job, parsed_job['queue'])
|
119
|
+
|
120
|
+
retrier.wrapped(instance, parsed_job, queue) do
|
121
|
+
instance.perform(*parsed_job['args'])
|
122
|
+
end
|
123
|
+
|
124
|
+
collection.incr_processed
|
125
|
+
ensure
|
126
|
+
remove_current_job_from_collection
|
127
|
+
end
|
128
|
+
|
129
|
+
def retrier
|
130
|
+
@retrier ||= Jiggler::Retrier.new(config, collection)
|
131
|
+
end
|
132
|
+
|
133
|
+
def requeue(queue, args)
|
134
|
+
config.with_async_redis do |conn|
|
135
|
+
conn.call('RPUSH', queue, args)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def handle_fetch_error(ex)
|
140
|
+
log_error_short(
|
141
|
+
ex,
|
142
|
+
{
|
143
|
+
context: '\'Fetch error\'',
|
144
|
+
tid: @tid
|
145
|
+
}
|
146
|
+
)
|
147
|
+
sleep(TIMEOUT + rand(5) * config[:concurrency]) # sleep for a while before retrying
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_current_job_to_collection(parsed_job, queue)
|
151
|
+
collection.data[:current_jobs][@tid] = {
|
152
|
+
job_args: parsed_job,
|
153
|
+
queue: queue,
|
154
|
+
started_at: Time.now.to_f
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def remove_current_job_from_collection
|
159
|
+
collection.data[:current_jobs].delete(@tid)
|
160
|
+
end
|
161
|
+
|
162
|
+
def queues
|
163
|
+
@queues ||= config.prefixed_queues
|
164
|
+
end
|
165
|
+
|
166
|
+
def constantize(str)
|
167
|
+
return Object.const_get(str) unless str.include?('::')
|
168
|
+
|
169
|
+
names = str.split('::')
|
170
|
+
names.shift if names.empty? || names.first.empty?
|
171
|
+
|
172
|
+
names.inject(Object) do |constant, name|
|
173
|
+
constant.const_get(name, false)
|
174
|
+
end
|
175
|
+
rescue => err
|
176
|
+
raise UnknownJobError, 'Cannot initialize job'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/lib/jiggler.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require only classes which are going to be used on the client side
|
4
|
+
require 'jiggler/redis_store'
|
5
|
+
require 'jiggler/config'
|
6
|
+
require 'jiggler/cleaner'
|
7
|
+
require 'jiggler/summary'
|
8
|
+
require 'jiggler/version'
|
9
|
+
require 'jiggler/core'
|
10
|
+
require 'jiggler/job'
|
data/spec/examples.txt
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
------------------------------------------------ | ------- | --------------- |
|
3
|
+
./spec/jiggler/cleaner_spec.rb[1:1:1] | passed | 0.00079 seconds |
|
4
|
+
./spec/jiggler/cleaner_spec.rb[1:2:1] | passed | 0.00104 seconds |
|
5
|
+
./spec/jiggler/cleaner_spec.rb[1:3:1] | passed | 0.00427 seconds |
|
6
|
+
./spec/jiggler/cleaner_spec.rb[1:4:1] | passed | 0.00071 seconds |
|
7
|
+
./spec/jiggler/cleaner_spec.rb[1:5:1] | passed | 0.00079 seconds |
|
8
|
+
./spec/jiggler/cleaner_spec.rb[1:6:1] | passed | 0.00071 seconds |
|
9
|
+
./spec/jiggler/cleaner_spec.rb[1:7:1] | passed | 0.00063 seconds |
|
10
|
+
./spec/jiggler/cleaner_spec.rb[1:8:1] | passed | 0.003 seconds |
|
11
|
+
./spec/jiggler/cleaner_spec.rb[1:9:1] | passed | 0.00077 seconds |
|
12
|
+
./spec/jiggler/cleaner_spec.rb[1:10:1] | passed | 0.00122 seconds |
|
13
|
+
./spec/jiggler/cleaner_spec.rb[1:11:1] | passed | 0.00286 seconds |
|
14
|
+
./spec/jiggler/cli_spec.rb[1:1:1:1] | passed | 0.00403 seconds |
|
15
|
+
./spec/jiggler/cli_spec.rb[1:1:2:1] | passed | 0.76231 seconds |
|
16
|
+
./spec/jiggler/cli_spec.rb[1:1:2:2] | passed | 0.00644 seconds |
|
17
|
+
./spec/jiggler/cli_spec.rb[1:1:2:3] | passed | 0.00535 seconds |
|
18
|
+
./spec/jiggler/cli_spec.rb[1:1:2:4] | passed | 0.0202 seconds |
|
19
|
+
./spec/jiggler/cli_spec.rb[1:1:2:5] | passed | 0.004 seconds |
|
20
|
+
./spec/jiggler/cli_spec.rb[1:1:2:6] | passed | 0.00955 seconds |
|
21
|
+
./spec/jiggler/cli_spec.rb[1:1:2:7] | passed | 0.02139 seconds |
|
22
|
+
./spec/jiggler/cli_spec.rb[1:1:2:8] | passed | 0.00429 seconds |
|
23
|
+
./spec/jiggler/cli_spec.rb[1:1:2:9] | passed | 0.00746 seconds |
|
24
|
+
./spec/jiggler/cli_spec.rb[1:1:3:1] | passed | 0.00336 seconds |
|
25
|
+
./spec/jiggler/cli_spec.rb[1:1:3:2] | passed | 0.00497 seconds |
|
26
|
+
./spec/jiggler/cli_spec.rb[1:1:3:3] | passed | 0.00312 seconds |
|
27
|
+
./spec/jiggler/cli_spec.rb[1:1:3:4] | passed | 0.0086 seconds |
|
28
|
+
./spec/jiggler/cli_spec.rb[1:1:3:5] | unknown | |
|
29
|
+
./spec/jiggler/config_spec.rb[1:1:1] | passed | 0.00055 seconds |
|
30
|
+
./spec/jiggler/config_spec.rb[1:1:2] | passed | 0.00057 seconds |
|
31
|
+
./spec/jiggler/config_spec.rb[1:1:3] | passed | 0.00004 seconds |
|
32
|
+
./spec/jiggler/config_spec.rb[1:1:4] | passed | 0.00004 seconds |
|
33
|
+
./spec/jiggler/core_spec.rb[1:1:1] | passed | 0.00103 seconds |
|
34
|
+
./spec/jiggler/job_spec.rb[1:1:1:1] | passed | 0.00007 seconds |
|
35
|
+
./spec/jiggler/job_spec.rb[1:1:2:1] | passed | 0.0009 seconds |
|
36
|
+
./spec/jiggler/job_spec.rb[1:2:1] | passed | 0.00064 seconds |
|
37
|
+
./spec/jiggler/job_spec.rb[1:3:1] | passed | 0.00341 seconds |
|
38
|
+
./spec/jiggler/job_spec.rb[1:4:1] | passed | 0.00051 seconds |
|
39
|
+
./spec/jiggler/job_spec.rb[1:4:2] | passed | 0.00073 seconds |
|
40
|
+
./spec/jiggler/job_spec.rb[1:5:1] | passed | 0.00051 seconds |
|
41
|
+
./spec/jiggler/launcher_spec.rb[1:1:1] | passed | 0.00598 seconds |
|
42
|
+
./spec/jiggler/launcher_spec.rb[1:2:1] | passed | 2.02 seconds |
|
43
|
+
./spec/jiggler/launcher_spec.rb[1:3:1] | passed | 2.1 seconds |
|
44
|
+
./spec/jiggler/launcher_spec.rb[1:4:1] | passed | 2.02 seconds |
|
45
|
+
./spec/jiggler/manager_spec.rb[1:1] | passed | 0.00094 seconds |
|
46
|
+
./spec/jiggler/manager_spec.rb[1:2:1] | passed | 1.53 seconds |
|
47
|
+
./spec/jiggler/manager_spec.rb[1:3:1] | passed | 2.14 seconds |
|
48
|
+
./spec/jiggler/manager_spec.rb[1:4:1] | passed | 3.56 seconds |
|
49
|
+
./spec/jiggler/redis_store_spec.rb[1:1:1] | passed | 0.00073 seconds |
|
50
|
+
./spec/jiggler/redis_store_spec.rb[1:1:2:1] | passed | 0.00071 seconds |
|
51
|
+
./spec/jiggler/retrier_spec.rb[1:1:1:1] | passed | 0.02616 seconds |
|
52
|
+
./spec/jiggler/retrier_spec.rb[1:1:1:2] | passed | 0.0143 seconds |
|
53
|
+
./spec/jiggler/retrier_spec.rb[1:1:2:1] | passed | 0.01566 seconds |
|
54
|
+
./spec/jiggler/scheduled/enqueuer_spec.rb[1:1:1] | passed | 0.01203 seconds |
|
55
|
+
./spec/jiggler/scheduled/enqueuer_spec.rb[1:1:2] | passed | 0.02395 seconds |
|
56
|
+
./spec/jiggler/scheduled/enqueuer_spec.rb[1:1:3] | passed | 0.0107 seconds |
|
57
|
+
./spec/jiggler/scheduled/enqueuer_spec.rb[1:2:1] | passed | 0.03692 seconds |
|
58
|
+
./spec/jiggler/scheduled/poller_spec.rb[1:1:1] | passed | 1.02 seconds |
|
59
|
+
./spec/jiggler/scheduled/poller_spec.rb[1:2:1] | passed | 1.02 seconds |
|
60
|
+
./spec/jiggler/stats/monitor_spec.rb[1:1:1] | passed | 2.02 seconds |
|
61
|
+
./spec/jiggler/stats/monitor_spec.rb[1:2:1] | passed | 1.03 seconds |
|
62
|
+
./spec/jiggler/summary_spec.rb[1:1:1] | passed | 0.00208 seconds |
|
63
|
+
./spec/jiggler/summary_spec.rb[1:1:2] | passed | 3.04 seconds |
|
64
|
+
./spec/jiggler/summary_spec.rb[1:1:3:1] | passed | 0.0076 seconds |
|
65
|
+
./spec/jiggler/summary_spec.rb[1:2:1] | passed | 1.01 seconds |
|
66
|
+
./spec/jiggler/summary_spec.rb[1:3:1] | passed | 1.01 seconds |
|
67
|
+
./spec/jiggler/summary_spec.rb[1:4:1] | passed | 0.00461 seconds |
|
68
|
+
./spec/jiggler/web_spec.rb[1:1:1] | passed | 0.00004 seconds |
|
69
|
+
./spec/jiggler/web_spec.rb[1:1:2] | passed | 0.0005 seconds |
|
70
|
+
./spec/jiggler/web_spec.rb[1:2:1] | passed | 0.00005 seconds |
|
71
|
+
./spec/jiggler/web_spec.rb[1:2:2] | passed | 0.00044 seconds |
|
72
|
+
./spec/jiggler/web_spec.rb[1:3:1] | passed | 0.00037 seconds |
|
73
|
+
./spec/jiggler/web_spec.rb[1:3:2] | passed | 0.00004 seconds |
|
74
|
+
./spec/jiggler/worker_spec.rb[1:1:1] | passed | 1.01 seconds |
|
75
|
+
./spec/jiggler/worker_spec.rb[1:1:2] | passed | 1.01 seconds |
|
76
|
+
./spec/jiggler/worker_spec.rb[1:1:3] | passed | 1.01 seconds |
|
77
|
+
./spec/jiggler/worker_spec.rb[1:2:1] | passed | 0.00527 seconds |
|
78
|
+
./spec/jiggler/worker_spec.rb[1:3:1:1] | passed | 2.05 seconds |
|
79
|
+
./spec/jiggler/worker_spec.rb[1:3:2:1] | passed | 0.00371 seconds |
|