postjob 0.5.11 → 0.5.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/postjob/cli/cron.rb +24 -0
- data/lib/postjob/cli/db.rb +1 -2
- data/lib/postjob/cli/events.rb +2 -2
- data/lib/postjob/cli/heartbeat.rb +2 -2
- data/lib/postjob/cli/helpers.rb +28 -0
- data/lib/postjob/cli/hosts.rb +32 -15
- data/lib/postjob/cli/job.rb +2 -0
- data/lib/postjob/cli/ps.rb +4 -26
- data/lib/postjob/cli/queues.rb +66 -0
- data/lib/postjob/cli/run.rb +19 -6
- data/lib/postjob/cli/sessions.rb +5 -4
- data/lib/postjob/host.rb +26 -5
- data/lib/postjob/migrations/001_helpers.sql +19 -0
- data/lib/postjob/migrations/007_job_results.sql +0 -26
- data/lib/postjob/migrations/012_hosts.sql +48 -5
- data/lib/postjob/migrations/013_worker_sessions.sql +12 -1
- data/lib/postjob/migrations/013a_checkout_runnable.sql +47 -5
- data/lib/postjob/migrations/016_sessions_functions.sql +5 -3
- data/lib/postjob/migrations/017_zombie_check.sql +64 -18
- data/lib/postjob/migrations/018_heartbeat.sql +36 -3
- data/lib/postjob/migrations/021_cron_jobs.sql +12 -11
- data/lib/postjob/migrations.rb +1 -1
- data/lib/postjob/queue/notifications.rb +15 -7
- data/lib/postjob/queue.rb +21 -8
- data/lib/postjob/runner.rb +1 -1
- data/lib/postjob/worker_session.rb +9 -5
- data/lib/postjob.rb +62 -26
- data/lib/tools/heartbeat.rb +2 -1
- data/spec/postjob/events/job_event_spec.rb +2 -2
- data/spec/postjob/worker_session_spec.rb +1 -1
- data/spec/postjob/zombie_spec.rb +54 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/test_helper.rb +3 -8
- metadata +12 -9
- data/spec/postjob/events/zombie_event_spec.rb +0 -61
data/lib/postjob.rb
CHANGED
@@ -38,6 +38,8 @@ module Postjob
|
|
38
38
|
attr_accessor :fast_mode # :nodoc:
|
39
39
|
self.fast_mode = false
|
40
40
|
|
41
|
+
CRON_INTERVAL_MIN = 60
|
42
|
+
|
41
43
|
# Enqueues a workflow.
|
42
44
|
#
|
43
45
|
# Options include
|
@@ -91,8 +93,8 @@ module Postjob
|
|
91
93
|
greedy = spec.options.greedy
|
92
94
|
end
|
93
95
|
|
94
|
-
if cron_interval && cron_interval <
|
95
|
-
raise "cron interval must be at least
|
96
|
+
if cron_interval && cron_interval < CRON_INTERVAL_MIN
|
97
|
+
raise "cron interval must be at least #{CRON_INTERVAL_MIN} seconds"
|
96
98
|
end
|
97
99
|
|
98
100
|
# -- disable existing cron jobs -------------------------------------------
|
@@ -105,15 +107,18 @@ module Postjob
|
|
105
107
|
|
106
108
|
tags = stringify_hash(tags) if tags
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
110
|
+
session_id = current_session_id if current_session?
|
111
|
+
session_id ||= "00000000-0000-0000-0000-000000000000"
|
112
|
+
|
113
|
+
job = Queue.enqueue_job session_id, workflow, *args, queue: queue,
|
114
|
+
parent_id: parent_id,
|
115
|
+
max_attempts: max_attempts,
|
116
|
+
timeout: timeout,
|
117
|
+
tags: tags,
|
118
|
+
version: version,
|
119
|
+
cron_interval: cron_interval,
|
120
|
+
sticky: sticky,
|
121
|
+
greedy: greedy
|
117
122
|
|
118
123
|
logger.info "Generated process #{job}"
|
119
124
|
job.id
|
@@ -160,16 +165,20 @@ module Postjob
|
|
160
165
|
# b) the block yielded into returns false.
|
161
166
|
#
|
162
167
|
# This method returns the number of processed jobs.
|
163
|
-
def run(count: nil,
|
168
|
+
def run(count: nil, queues: nil, heartbeat: true, &block)
|
169
|
+
queues ||= [ "ruby" ]
|
170
|
+
|
164
171
|
# to run 10^12 jobs that would take 1 msecs each we would need, at least,
|
165
172
|
# 760 years - so this default should be fine. Also, someone should update
|
166
173
|
# the machine in the meantime :)
|
167
174
|
count ||= 1_000_000_000_000
|
168
175
|
|
176
|
+
start_worker_session!(heartbeat: heartbeat, queues: queues)
|
177
|
+
|
169
178
|
processed_jobs_count = 0
|
170
179
|
|
171
180
|
loop do
|
172
|
-
processed_job_id, shutdown = Postjob.step(
|
181
|
+
processed_job_id, shutdown = Postjob.step(queues: queues)
|
173
182
|
processed_jobs_count += 1 if processed_job_id
|
174
183
|
|
175
184
|
break if processed_jobs_count >= count
|
@@ -177,12 +186,31 @@ module Postjob
|
|
177
186
|
break if shutdown == :shutdown
|
178
187
|
|
179
188
|
next if processed_job_id
|
180
|
-
Queue::Notifications.wait_for_new_job(
|
189
|
+
shutdown = Queue::Notifications.wait_for_new_job(current_session_id, queues: queues)
|
190
|
+
break if shutdown == :shutdown
|
181
191
|
end
|
182
192
|
|
183
193
|
processed_jobs_count
|
184
194
|
end
|
185
195
|
|
196
|
+
def start_worker_session!(heartbeat: true, queues: nil) # :nodoc:
|
197
|
+
queues ||= [ "ruby" ]
|
198
|
+
|
199
|
+
# We can't restart a new worker_session (this is currently not supported)
|
200
|
+
# and probably also unnecessary. Instead we ignore this call, as long as
|
201
|
+
# the queue setting is identical.
|
202
|
+
#
|
203
|
+
# A call to start_worker_session! is valid only during tests.
|
204
|
+
if @worker_session
|
205
|
+
raise ArgumentError, "You cannot restart a worker session" if ENV["RACK_ENV"] != "test"
|
206
|
+
raise ArgumentError, "You cannot restart a worker_session with different queues" if @worker_session.queues != queues
|
207
|
+
|
208
|
+
return @worker_session
|
209
|
+
end
|
210
|
+
|
211
|
+
@worker_session = WorkerSession.start!(Registry.runnable_workflows_with_versions, heartbeat: heartbeat, queues: queues)
|
212
|
+
end
|
213
|
+
|
186
214
|
# Runs a single job
|
187
215
|
#
|
188
216
|
# This method tries to check out a runnable job. If it finds one the
|
@@ -195,18 +223,25 @@ module Postjob
|
|
195
223
|
# self.run to terminate the run loop.
|
196
224
|
#
|
197
225
|
# or nil, when no job could be checked out.
|
198
|
-
def step(
|
199
|
-
|
226
|
+
def step(queues: nil)
|
227
|
+
expect! queues => [Array, nil]
|
228
|
+
queues ||= [ "ruby" ]
|
229
|
+
|
230
|
+
job = Postjob::Queue.checkout(current_session_id, queues: queues)
|
200
231
|
|
201
232
|
[ job.id, process_job(job) ] if job
|
202
233
|
end
|
203
234
|
|
235
|
+
def current_session?
|
236
|
+
@worker_session != @nil
|
237
|
+
end
|
238
|
+
|
204
239
|
# This method connects to the queue. This means it registers as a new worker_session,
|
205
240
|
# if there was no worker_session yet.
|
206
|
-
def
|
207
|
-
|
208
|
-
|
209
|
-
|
241
|
+
def current_session_id
|
242
|
+
raise("worker_session hasn't been started yet.") unless @worker_session
|
243
|
+
|
244
|
+
@worker_session.id
|
210
245
|
end
|
211
246
|
|
212
247
|
# Explicitely resolve a workflow.
|
@@ -214,7 +249,7 @@ module Postjob
|
|
214
249
|
job = Queue.find_job_by_token(token)
|
215
250
|
raise "No job with token #{token}" unless job
|
216
251
|
|
217
|
-
Queue.set_job_result
|
252
|
+
Queue.set_job_result current_session_id, job, result, version: nil
|
218
253
|
end
|
219
254
|
|
220
255
|
# Registers a workflow.
|
@@ -264,13 +299,14 @@ module Postjob
|
|
264
299
|
raise "Integrity check failed: job's workflow version changed (from #{job.workflow_version} to #{version})"
|
265
300
|
end
|
266
301
|
|
267
|
-
|
302
|
+
session_id = current_session_id if current_session?
|
303
|
+
session_id ||= "00000000-0000-0000-0000-000000000000"
|
268
304
|
|
269
305
|
case status
|
270
|
-
when :failed then Queue.set_job_error
|
271
|
-
when :err then Queue.set_job_error
|
272
|
-
when :pending then Queue.set_job_pending
|
273
|
-
when :ok then Queue.set_job_result
|
306
|
+
when :failed then Queue.set_job_error session_id, job, *value, status: :failed, version: version
|
307
|
+
when :err then Queue.set_job_error session_id, job, *value, status: :err, version: version
|
308
|
+
when :pending then Queue.set_job_pending session_id, job, version: version
|
309
|
+
when :ok then Queue.set_job_result session_id, job, value, version: version
|
274
310
|
else raise ArgumentError, "Invalid status #{status.inspect}"
|
275
311
|
end
|
276
312
|
|
data/lib/tools/heartbeat.rb
CHANGED
@@ -69,12 +69,12 @@ describe "Job Events" do
|
|
69
69
|
|
70
70
|
it "sets the job's last_worker_session_id" do
|
71
71
|
last_worker_session_ids = jobs.map(&:last_worker_session_id)
|
72
|
-
expect(last_worker_session_ids.uniq).to eq([Postjob.
|
72
|
+
expect(last_worker_session_ids.uniq).to eq([Postjob.current_session_id])
|
73
73
|
end
|
74
74
|
|
75
75
|
it "records the worker session id" do
|
76
76
|
worker_session_ids = events.map(&:worker_session_id).uniq
|
77
|
-
expect(worker_session_ids).to eq([Postjob.
|
77
|
+
expect(worker_session_ids).to eq([Postjob.current_session_id])
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -11,7 +11,7 @@ describe "Postjob::WorkerSession.start!" do
|
|
11
11
|
|
12
12
|
it "creates a new session" do
|
13
13
|
expect do
|
14
|
-
session = Postjob::WorkerSession.start!(workflows_with_versions)
|
14
|
+
session = Postjob::WorkerSession.start!(workflows_with_versions, queues: [ "ruby" ])
|
15
15
|
expect(session.id).to match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i)
|
16
16
|
end.to change { session_count }.by(1)
|
17
17
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module ZombieSpecWorkflow
|
4
|
+
Postjob.register_workflow self
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "Zombie Detection" do
|
8
|
+
include TestHelper
|
9
|
+
|
10
|
+
before do
|
11
|
+
# announce the ZombieSpecWorkflow
|
12
|
+
# Postjob.enqueue! "ZombieSpecWorkflow"
|
13
|
+
|
14
|
+
# # Start a worker session.
|
15
|
+
session = Postjob.start_worker_session!(heartbeat: false)
|
16
|
+
@host_id = session.host_id
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:host_id) { @host_id }
|
20
|
+
|
21
|
+
def send_heartbeat!
|
22
|
+
# pass in a artificial heartbeat
|
23
|
+
::Postjob::Queue.host_heartbeat(host_id, {})
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a processing job which has the latest heartbeat from its host for > 5 minute" do
|
27
|
+
before do
|
28
|
+
@job_id = Postjob.enqueue! "ZombieSpecWorkflow"
|
29
|
+
SQL.ask "UPDATE postjob.postjobs SET status='processing'"
|
30
|
+
send_heartbeat!
|
31
|
+
SQL.ask "UPDATE postjob.events SET created_at = (now() at time zone 'utc' - interval '6 minutes')"
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when sending a heartbeat" do
|
35
|
+
before do
|
36
|
+
send_heartbeat!
|
37
|
+
end
|
38
|
+
|
39
|
+
it "changes the job to zombie" do
|
40
|
+
job = SQL.ask "SELECT * FROM postjob.postjobs WHERE id= #{@job_id}", into: Postjob::Job
|
41
|
+
|
42
|
+
expect(job.error).to eq("Zombie")
|
43
|
+
expect(job.error_message).to match("host .* disappeared")
|
44
|
+
expect(job.status).to eq("err")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "creates a zombie event with a zombie_count" do
|
48
|
+
event = SQL.ask "SELECT * FROM postjob.events WHERE name='zombie' ORDER BY created_at DESC LIMIT 1", into: OpenStruct
|
49
|
+
expect(event.name).to eq("zombie")
|
50
|
+
expect(event.attributes).to eq("zombie_count" => 1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/test_helper.rb
CHANGED
@@ -34,15 +34,8 @@ module TestHelper
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def print_sql(sql, *args)
|
38
|
-
require "table_print"
|
39
|
-
|
40
|
-
records = Simple::SQL.all(sql, *args, into: Hash)
|
41
|
-
tp records
|
42
|
-
end
|
43
|
-
|
44
37
|
def print_jobs
|
45
|
-
|
38
|
+
sql = <<~SQL
|
46
39
|
SELECT
|
47
40
|
id,
|
48
41
|
workflow AS job,
|
@@ -59,5 +52,7 @@ module TestHelper
|
|
59
52
|
FROM postjobs
|
60
53
|
ORDER BY id
|
61
54
|
SQL
|
55
|
+
|
56
|
+
Simple::SQL.print(sql)
|
62
57
|
end
|
63
58
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postjob
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- radiospiel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
version: '0.4'
|
124
124
|
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
|
-
version: 0.4.
|
126
|
+
version: 0.4.15
|
127
127
|
type: :runtime
|
128
128
|
prerelease: false
|
129
129
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -133,27 +133,27 @@ dependencies:
|
|
133
133
|
version: '0.4'
|
134
134
|
- - ">="
|
135
135
|
- !ruby/object:Gem::Version
|
136
|
-
version: 0.4.
|
136
|
+
version: 0.4.15
|
137
137
|
- !ruby/object:Gem::Dependency
|
138
138
|
name: simple-cli
|
139
139
|
requirement: !ruby/object:Gem::Requirement
|
140
140
|
requirements:
|
141
141
|
- - "~>"
|
142
142
|
- !ruby/object:Gem::Version
|
143
|
-
version: '0
|
143
|
+
version: '0'
|
144
144
|
- - ">="
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: 0.2.
|
146
|
+
version: 0.2.8
|
147
147
|
type: :runtime
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
151
|
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: '0
|
153
|
+
version: '0'
|
154
154
|
- - ">="
|
155
155
|
- !ruby/object:Gem::Version
|
156
|
-
version: 0.2.
|
156
|
+
version: 0.2.8
|
157
157
|
- !ruby/object:Gem::Dependency
|
158
158
|
name: table_print
|
159
159
|
requirement: !ruby/object:Gem::Requirement
|
@@ -231,9 +231,11 @@ files:
|
|
231
231
|
- lib/postjob/cli/enqueue.rb
|
232
232
|
- lib/postjob/cli/events.rb
|
233
233
|
- lib/postjob/cli/heartbeat.rb
|
234
|
+
- lib/postjob/cli/helpers.rb
|
234
235
|
- lib/postjob/cli/hosts.rb
|
235
236
|
- lib/postjob/cli/job.rb
|
236
237
|
- lib/postjob/cli/ps.rb
|
238
|
+
- lib/postjob/cli/queues.rb
|
237
239
|
- lib/postjob/cli/run.rb
|
238
240
|
- lib/postjob/cli/sessions.rb
|
239
241
|
- lib/postjob/cli/version.rb
|
@@ -241,6 +243,7 @@ files:
|
|
241
243
|
- lib/postjob/host.rb
|
242
244
|
- lib/postjob/job.rb
|
243
245
|
- lib/postjob/migrations.rb
|
246
|
+
- lib/postjob/migrations/001_helpers.sql
|
244
247
|
- lib/postjob/migrations/002_statuses.rb
|
245
248
|
- lib/postjob/migrations/003_postjobs.sql
|
246
249
|
- lib/postjob/migrations/003a_processing.sql
|
@@ -283,7 +286,6 @@ files:
|
|
283
286
|
- spec/postjob/enqueue_spec.rb
|
284
287
|
- spec/postjob/events/heartbeat_event_spec.rb
|
285
288
|
- spec/postjob/events/job_event_spec.rb
|
286
|
-
- spec/postjob/events/zombie_event_spec.rb
|
287
289
|
- spec/postjob/full_workflow_spec.rb
|
288
290
|
- spec/postjob/host_spec.rb
|
289
291
|
- spec/postjob/job_control/error_status_spec.rb
|
@@ -297,6 +299,7 @@ files:
|
|
297
299
|
- spec/postjob/step_spec.rb
|
298
300
|
- spec/postjob/worker_session_spec.rb
|
299
301
|
- spec/postjob/workflows/child_workflow_spec.rb
|
302
|
+
- spec/postjob/zombie_spec.rb
|
300
303
|
- spec/spec_helper.rb
|
301
304
|
- spec/support/configure_active_record.rb
|
302
305
|
- spec/support/configure_database.rb
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
module HeartbeatSpecWorkflow
|
4
|
-
Postjob.register_workflow self
|
5
|
-
end
|
6
|
-
|
7
|
-
describe "Heartbeat Events" do
|
8
|
-
include TestHelper
|
9
|
-
|
10
|
-
before do
|
11
|
-
Postjob.enqueue! "HeartbeatSpecWorkflow"
|
12
|
-
|
13
|
-
@host_id = Simple::SQL.ask "SELECT id FROM postjob.hosts WHERE id != $1", null_host_id
|
14
|
-
|
15
|
-
send_heartbeat!
|
16
|
-
|
17
|
-
Simple::SQL.ask "UPDATE postjob.postjobs SET status='processing'"
|
18
|
-
end
|
19
|
-
|
20
|
-
let(:host_id) { @host_id || raise("host_id must be set") }
|
21
|
-
|
22
|
-
def send_heartbeat!
|
23
|
-
::Postjob::Queue.host_heartbeat(host_id, {})
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "::Postjob::Queue.host_heartbeat" do
|
27
|
-
context "zombies in the database" do
|
28
|
-
before do
|
29
|
-
Simple::SQL.ask "UPDATE postjob.events SET created_at = (now() at time zone 'utc' - interval '2 days')"
|
30
|
-
end
|
31
|
-
|
32
|
-
it "does creates a zombie events" do
|
33
|
-
send_heartbeat!
|
34
|
-
|
35
|
-
event = Simple::SQL.ask <<~SQL, into: Hash
|
36
|
-
SELECT * FROM postjob.events
|
37
|
-
WHERE name='zombie' ORDER BY created_at DESC
|
38
|
-
SQL
|
39
|
-
|
40
|
-
expect(event).to include(
|
41
|
-
attributes: { "zombie_count" => 1 },
|
42
|
-
name: "zombie",
|
43
|
-
postjob_id: nil
|
44
|
-
)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context "no zombies in the database, 2" do
|
50
|
-
it "does creates a zombie events" do
|
51
|
-
send_heartbeat!
|
52
|
-
|
53
|
-
event = Simple::SQL.ask <<~SQL, into: Hash
|
54
|
-
SELECT * FROM postjob.events
|
55
|
-
WHERE name='zombie' ORDER BY created_at DESC
|
56
|
-
SQL
|
57
|
-
|
58
|
-
expect(event).to be_nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|