postjob 0.5.11 → 0.5.12
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 +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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 175e2a30ec560a1728f3b3a62750bd5afb38df3d
|
|
4
|
+
data.tar.gz: 54328faf7c7966c21743575ab8d7a9c94b66180d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98960758ed5262988f82ac8cb046752be425b15bd91b2355e575f69007d43b7161580d2796af4d4bd6ebcea71d87f955d442f061ad43ba720efb02a2da172584
|
|
7
|
+
data.tar.gz: 5f1bdd2b015c706cddc7e5874bcbfada7ebc4d3c22f34511d6f5d14eb0c3f28261eebd834670cbd1e68920776b1132a20482d7c0bc58e4a13d55edbb175ee996
|
data/lib/postjob/cli/cron.rb
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
|
+
# rubocop:disable Lint/HandleExceptions
|
|
2
|
+
|
|
1
3
|
module Postjob::CLI
|
|
4
|
+
# Lists cron jobs in the system
|
|
5
|
+
def cron(limit: "30", queue: nil)
|
|
6
|
+
limit = Integer(limit)
|
|
7
|
+
queue = queue.split(",") if queue
|
|
8
|
+
|
|
9
|
+
connect_to_database!
|
|
10
|
+
|
|
11
|
+
query = ps_query(limit: limit, queue: queue, tags: nil)
|
|
12
|
+
query = query.where("cron_interval IS NOT NULL")
|
|
13
|
+
# query = query
|
|
14
|
+
print_results query: query
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def cron_top(limit: "30", queue: nil)
|
|
18
|
+
loop do
|
|
19
|
+
system "clear"
|
|
20
|
+
cron limit: limit, queue: queue
|
|
21
|
+
sleep 1
|
|
22
|
+
end
|
|
23
|
+
rescue Interrupt
|
|
24
|
+
end
|
|
25
|
+
|
|
2
26
|
# Enqueues a cron workflow
|
|
3
27
|
def cron_enqueue(workflow, *args, interval:, queue: "ruby", tags: nil)
|
|
4
28
|
interval = Integer(interval)
|
data/lib/postjob/cli/db.rb
CHANGED
|
@@ -8,8 +8,7 @@ module Postjob::CLI
|
|
|
8
8
|
|
|
9
9
|
raise "No settings in this database schema" unless ::Postjob::Queue.settings?
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
tp Simple::SQL.all(records, into: Hash)
|
|
11
|
+
Simple::SQL.print "SELECT name,value FROM postjob.settings ORDER BY name"
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def db_migrate
|
data/lib/postjob/cli/events.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Postjob::CLI
|
|
|
37
37
|
# Example:
|
|
38
38
|
#
|
|
39
39
|
# postjob events
|
|
40
|
-
def events(limit: "
|
|
40
|
+
def events(limit: "30")
|
|
41
41
|
expect! limit => /\A\d+\z/
|
|
42
42
|
limit = Integer(limit)
|
|
43
43
|
|
|
@@ -49,7 +49,7 @@ module Postjob::CLI
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Show up-to-date events information once per second
|
|
52
|
-
def events_top(limit: "
|
|
52
|
+
def events_top(limit: "30")
|
|
53
53
|
loop do
|
|
54
54
|
system "clear"
|
|
55
55
|
events(limit: limit)
|
|
@@ -31,7 +31,7 @@ module Postjob::CLI
|
|
|
31
31
|
public
|
|
32
32
|
|
|
33
33
|
# Show the latest heartbeat events
|
|
34
|
-
def heartbeat(limit: "
|
|
34
|
+
def heartbeat(limit: "30")
|
|
35
35
|
expect! limit => /\A\d+\z/
|
|
36
36
|
limit = Integer(limit)
|
|
37
37
|
|
|
@@ -44,7 +44,7 @@ module Postjob::CLI
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Show up-to-date heartbeat information once per second
|
|
47
|
-
def heartbeat_top(limit: "
|
|
47
|
+
def heartbeat_top(limit: "30")
|
|
48
48
|
loop do
|
|
49
49
|
system "clear"
|
|
50
50
|
heartbeat(limit: limit)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module Postjob::CLI
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def parse_ids(*ids)
|
|
7
|
+
ids.map do |s|
|
|
8
|
+
s = s.gsub(/.*\./, "")
|
|
9
|
+
Integer(s)
|
|
10
|
+
end.uniq
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def print_results(query:, on_empty: nil, title: nil)
|
|
14
|
+
if title
|
|
15
|
+
puts "\n=== #{title} =======================================\n\n"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
records = Simple::SQL.print(query, into: Hash)
|
|
19
|
+
|
|
20
|
+
if records.total_count && records.total_count > records.length
|
|
21
|
+
logger.warn "Output limited up to limit #{records.length}. Use the --limit=<NN> command line option for a different limit."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if records.empty? && on_empty
|
|
25
|
+
logger.warn(on_empty)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/postjob/cli/hosts.rb
CHANGED
|
@@ -10,21 +10,21 @@ module Postjob::CLI
|
|
|
10
10
|
sql = <<-SQL
|
|
11
11
|
SELECT
|
|
12
12
|
hosts.id,
|
|
13
|
-
hosts.attributes,
|
|
14
13
|
hosts.created_at,
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
hosts.status,
|
|
15
|
+
heartbeats.attributes AS heartbeat,
|
|
16
|
+
heartbeats.created_at AS heartbeat_created_at,
|
|
17
|
+
hosts.attributes
|
|
17
18
|
FROM postjob.hosts hosts
|
|
18
19
|
LEFT JOIN (
|
|
19
20
|
SELECT
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
LEFT JOIN events heartbeat ON heartbeat.id=event_id
|
|
21
|
+
host_id,
|
|
22
|
+
attributes,
|
|
23
|
+
created_at,
|
|
24
|
+
rank() OVER (PARTITION BY host_id ORDER BY created_at DESC) AS rank
|
|
25
|
+
FROM postjob.events events
|
|
26
|
+
WHERE name='heartbeat'
|
|
27
|
+
) heartbeats ON heartbeats.host_id=hosts.id AND rank = 1
|
|
28
28
|
SQL
|
|
29
29
|
|
|
30
30
|
scope = Simple::SQL::Scope.new(sql)
|
|
@@ -42,7 +42,7 @@ module Postjob::CLI
|
|
|
42
42
|
# Example:
|
|
43
43
|
#
|
|
44
44
|
# postjob hosts
|
|
45
|
-
def hosts(limit: "
|
|
45
|
+
def hosts(limit: "30")
|
|
46
46
|
expect! limit => /\A\d+\z/
|
|
47
47
|
limit = Integer(limit)
|
|
48
48
|
|
|
@@ -54,9 +54,7 @@ module Postjob::CLI
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
# Show up-to-date hosts information once per second
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
def hosts_top(limit: "100")
|
|
57
|
+
def hosts_top(limit: "30")
|
|
60
58
|
loop do
|
|
61
59
|
system "clear"
|
|
62
60
|
hosts(limit: limit)
|
|
@@ -64,4 +62,23 @@ module Postjob::CLI
|
|
|
64
62
|
end
|
|
65
63
|
rescue Interrupt
|
|
66
64
|
end
|
|
65
|
+
|
|
66
|
+
# resets the host id
|
|
67
|
+
def host_reset
|
|
68
|
+
Postjob::Host.clear_storage
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Set the host to shutdown state
|
|
72
|
+
def host_shutdown
|
|
73
|
+
connect_to_database!
|
|
74
|
+
|
|
75
|
+
Simple::SQL.ask "UPDATE postjob.hosts SET status='shutdown' WHERE id=$1::uuid", ::Postjob.host_id
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Set the host to running again
|
|
79
|
+
def host_restart
|
|
80
|
+
connect_to_database!
|
|
81
|
+
|
|
82
|
+
Simple::SQL.ask "UPDATE postjob.hosts SET status='running' WHERE id=$1::uuid", ::Postjob.host_id
|
|
83
|
+
end
|
|
67
84
|
end
|
data/lib/postjob/cli/job.rb
CHANGED
data/lib/postjob/cli/ps.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Postjob::CLI
|
|
|
29
29
|
max_attempts,
|
|
30
30
|
cron_interval AS cron,
|
|
31
31
|
CASE
|
|
32
|
-
WHEN is_sticky THEN COALESCE(substring(sticky_host_id::varchar for
|
|
32
|
+
WHEN is_sticky THEN COALESCE(substring(sticky_host_id::varchar for 9) || '...', 'yes')
|
|
33
33
|
ELSE 'no'
|
|
34
34
|
END AS sticky,
|
|
35
35
|
is_greedy AS greedy,
|
|
@@ -64,7 +64,7 @@ module Postjob::CLI
|
|
|
64
64
|
#
|
|
65
65
|
# For a listing of all jobs in the system use ps:full, see 'postjob help ps:full'
|
|
66
66
|
# for details.
|
|
67
|
-
def ps(*ids, limit: "
|
|
67
|
+
def ps(*ids, limit: "30", tags: nil, queue: nil, only_root: nil)
|
|
68
68
|
expect! limit => /\A\d+\z/
|
|
69
69
|
limit = Integer(limit)
|
|
70
70
|
queue = queue.split(",") if queue
|
|
@@ -85,7 +85,7 @@ module Postjob::CLI
|
|
|
85
85
|
print_results query: query
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
def ps_full(*ids, limit: "
|
|
88
|
+
def ps_full(*ids, limit: "30", tags: nil, queue: nil)
|
|
89
89
|
queue = queue.split(",") if queue
|
|
90
90
|
|
|
91
91
|
connect_to_database!
|
|
@@ -101,7 +101,7 @@ module Postjob::CLI
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
# Show up-to-date information once per second
|
|
104
|
-
def ps_top(*ids, limit: "
|
|
104
|
+
def ps_top(*ids, limit: "30", tags: nil, full: false, queue: nil, only_root: false)
|
|
105
105
|
loop do
|
|
106
106
|
system "clear"
|
|
107
107
|
if full
|
|
@@ -159,26 +159,4 @@ module Postjob::CLI
|
|
|
159
159
|
pp job
|
|
160
160
|
end
|
|
161
161
|
end
|
|
162
|
-
|
|
163
|
-
private
|
|
164
|
-
|
|
165
|
-
def parse_ids(*ids)
|
|
166
|
-
ids.map do |s|
|
|
167
|
-
s = s.gsub(/.*\./, "")
|
|
168
|
-
Integer(s)
|
|
169
|
-
end.uniq
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def print_results(query:, on_empty: nil)
|
|
173
|
-
records = Simple::SQL.all(query, into: Hash)
|
|
174
|
-
tp records
|
|
175
|
-
|
|
176
|
-
if records.total_count > records.length
|
|
177
|
-
logger.warn "Output limited up to limit #{records.length}. Use the --limit=<NN> command line option for a different limit."
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
if records.empty? && on_empty
|
|
181
|
-
logger.warn(on_empty)
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
162
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# rubocop:disable Metrics/MethodLength
|
|
2
|
+
# rubocop:disable Lint/HandleExceptions
|
|
3
|
+
|
|
4
|
+
module Postjob::CLI
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def queues_query(time_name:)
|
|
8
|
+
sql = <<-SQL
|
|
9
|
+
SELECT
|
|
10
|
+
queue,
|
|
11
|
+
status,
|
|
12
|
+
iff(status = 'ready', waiting, processing) AS "#{time_name}"
|
|
13
|
+
FROM (
|
|
14
|
+
SELECT
|
|
15
|
+
queue,
|
|
16
|
+
status,
|
|
17
|
+
COUNT(*),
|
|
18
|
+
AVG (now() at time zone 'utc' - jobs.created_at) AS waiting,
|
|
19
|
+
AVG (jobs.updated_at - jobs.created_at) AS processing
|
|
20
|
+
FROM postjob.postjobs jobs
|
|
21
|
+
WHERE jobs.root_id=jobs.id
|
|
22
|
+
AND (jobs.next_run_at IS NULL OR jobs.next_run_at < (now() at time zone 'utc'))
|
|
23
|
+
GROUP BY queue, status
|
|
24
|
+
ORDER BY queue, status
|
|
25
|
+
) sq
|
|
26
|
+
SQL
|
|
27
|
+
|
|
28
|
+
scope = Simple::SQL::Scope.new(sql)
|
|
29
|
+
scope
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
public
|
|
33
|
+
|
|
34
|
+
# Show hosts status
|
|
35
|
+
#
|
|
36
|
+
# This command lists all worker_sessions currently in the system.
|
|
37
|
+
#
|
|
38
|
+
# Example:
|
|
39
|
+
#
|
|
40
|
+
# postjob hosts
|
|
41
|
+
def queues
|
|
42
|
+
connect_to_database!
|
|
43
|
+
|
|
44
|
+
query = queues_query(time_name: "waiting for..")
|
|
45
|
+
query = query.where(status: %w(ready))
|
|
46
|
+
print_results query: query, title: "Waiting jobs"
|
|
47
|
+
|
|
48
|
+
query = queues_query(time_name: "processing since..")
|
|
49
|
+
query = query.where(status: %w(sleep processing err))
|
|
50
|
+
print_results query: query, title: "Processing jobs"
|
|
51
|
+
|
|
52
|
+
query = queues_query(time_name: "processing time")
|
|
53
|
+
query = query.where(status: %w(failed ok timeout))
|
|
54
|
+
print_results query: query, title: "Finished jobs"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# # Show up-to-date hosts information once per second
|
|
58
|
+
def queues_top
|
|
59
|
+
loop do
|
|
60
|
+
system "clear"
|
|
61
|
+
queues
|
|
62
|
+
sleep 1
|
|
63
|
+
end
|
|
64
|
+
rescue Interrupt
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/postjob/cli/run.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
2
|
+
# rubocop:disable Metrics/ParameterLists
|
|
3
|
+
|
|
1
4
|
module Postjob::CLI
|
|
2
5
|
# Run a single job
|
|
3
6
|
def step
|
|
@@ -10,21 +13,31 @@ module Postjob::CLI
|
|
|
10
13
|
#
|
|
11
14
|
# Parameters:
|
|
12
15
|
#
|
|
13
|
-
# - count=<count> maximum number of jobs to process. Default: unlimited.
|
|
14
|
-
# - queue run only the specified
|
|
15
|
-
# -
|
|
16
|
+
# - --count=<count> maximum number of jobs to process. Default: unlimited.
|
|
17
|
+
# - --queue=queue1,queue2,queue3 run only the specified queues.
|
|
18
|
+
# - --heartbeat=no don't start heartbeat process.
|
|
19
|
+
# - --quiet don't show progress.
|
|
16
20
|
#
|
|
17
|
-
def run(count: nil, queue:
|
|
21
|
+
def run(count: nil, queue: "ruby", quiet: false, fast: false, host_id: nil, heartbeat: true)
|
|
22
|
+
expect! Integer(host_id, 16) => 1..0xffffffff if host_id
|
|
18
23
|
count = Integer(count) if count
|
|
19
|
-
|
|
24
|
+
|
|
25
|
+
expect! heartbeat => [ "yes", "no" ] if heartbeat.is_a?(String)
|
|
26
|
+
heartbeat = %w(yes true).include?(heartbeat) if heartbeat.is_a?(String)
|
|
27
|
+
expect! heartbeat => [ true, false ]
|
|
20
28
|
|
|
21
29
|
Postjob.fast_mode = (fast ? true : false)
|
|
22
30
|
|
|
23
31
|
connect_to_database!
|
|
24
32
|
|
|
33
|
+
if host_id
|
|
34
|
+
Postjob::Host.host_id = "%08x-0000-0000-0000-000000000000" % Integer(host_id, 16)
|
|
35
|
+
Postjob.logger.info "Using host_id: #{Postjob::Host.host_id}"
|
|
36
|
+
end
|
|
37
|
+
|
|
25
38
|
logger.success "Starting runner with pid #{$$}"
|
|
26
39
|
|
|
27
|
-
processed = Postjob.run(count: count,
|
|
40
|
+
processed = Postjob.run(count: count, queues: queue.split(","), heartbeat: heartbeat) do |job_id|
|
|
28
41
|
logger.info "Processed job w/id #{job_id}" if job_id
|
|
29
42
|
STDERR.print "." unless quiet
|
|
30
43
|
end
|
data/lib/postjob/cli/sessions.rb
CHANGED
|
@@ -10,7 +10,8 @@ module Postjob::CLI
|
|
|
10
10
|
sql = <<-SQL
|
|
11
11
|
SELECT
|
|
12
12
|
worker_sessions.id,
|
|
13
|
-
worker_sessions.host_id,
|
|
13
|
+
(substring(worker_sessions.host_id::varchar for 9) || '...') AS host_id,
|
|
14
|
+
array_to_string(worker_sessions.queues, ', ') AS queues,
|
|
14
15
|
worker_sessions.client_socket,
|
|
15
16
|
worker_sessions.workflows,
|
|
16
17
|
worker_sessions.created_at,
|
|
@@ -42,7 +43,7 @@ module Postjob::CLI
|
|
|
42
43
|
SQL
|
|
43
44
|
|
|
44
45
|
scope = Simple::SQL::Scope.new(sql)
|
|
45
|
-
|
|
46
|
+
scope = scope.where("worker_sessions.id != '00000000-0000-0000-0000-000000000000'::uuid")
|
|
46
47
|
scope
|
|
47
48
|
.paginate(per: limit, page: 1)
|
|
48
49
|
.order_by("heartbeat_created_at DESC NULLS LAST")
|
|
@@ -57,7 +58,7 @@ module Postjob::CLI
|
|
|
57
58
|
# Example:
|
|
58
59
|
#
|
|
59
60
|
# postjob sessions
|
|
60
|
-
def sessions(limit: "
|
|
61
|
+
def sessions(limit: "30")
|
|
61
62
|
expect! limit => /\A\d+\z/
|
|
62
63
|
limit = Integer(limit)
|
|
63
64
|
|
|
@@ -72,7 +73,7 @@ module Postjob::CLI
|
|
|
72
73
|
end
|
|
73
74
|
|
|
74
75
|
# Show up-to-date session information once per second
|
|
75
|
-
def sessions_top(limit: "
|
|
76
|
+
def sessions_top(limit: "30")
|
|
76
77
|
loop do
|
|
77
78
|
system "clear"
|
|
78
79
|
sessions(limit: limit)
|
data/lib/postjob/host.rb
CHANGED
|
@@ -2,6 +2,11 @@ require_relative "./record"
|
|
|
2
2
|
require "tempfile"
|
|
3
3
|
|
|
4
4
|
class Postjob::Host < Postjob::Record
|
|
5
|
+
attr_reader :id
|
|
6
|
+
attr_reader :attributes
|
|
7
|
+
attr_reader :status
|
|
8
|
+
attr_reader :created_at
|
|
9
|
+
|
|
5
10
|
class << self
|
|
6
11
|
def clear_storage
|
|
7
12
|
@host_id = nil
|
|
@@ -9,8 +14,19 @@ class Postjob::Host < Postjob::Record
|
|
|
9
14
|
File.unlink(storage_path) if File.exist?(storage_path)
|
|
10
15
|
end
|
|
11
16
|
|
|
17
|
+
UUID_REGEXP = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
|
|
18
|
+
|
|
19
|
+
def host_id=(host_id)
|
|
20
|
+
expect! host_id => UUID_REGEXP
|
|
21
|
+
|
|
22
|
+
return if @host_id == host_id
|
|
23
|
+
|
|
24
|
+
register_host(host_id: host_id)
|
|
25
|
+
@host_id = host_id
|
|
26
|
+
end
|
|
27
|
+
|
|
12
28
|
def host_id
|
|
13
|
-
@host_id ||= atomic_set_and_get(storage_path) { register_host }
|
|
29
|
+
@host_id ||= atomic_set_and_get(storage_path) { register_host(host_id: nil) }
|
|
14
30
|
end
|
|
15
31
|
|
|
16
32
|
private
|
|
@@ -23,8 +39,12 @@ class Postjob::Host < Postjob::Record
|
|
|
23
39
|
# the current user. We choose a tmp location for that reason. (A /var location
|
|
24
40
|
# would be even better - however, our systems do not have a user-writable /var).
|
|
25
41
|
def storage_path
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
@storage_path ||= begin
|
|
43
|
+
env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
|
44
|
+
storage_path = File.join Dir.tmpdir, "postjob.#{env}.#{Process.uid}.host_id"
|
|
45
|
+
Postjob.logger.info "Keeping host identifier in #{storage_path}"
|
|
46
|
+
storage_path
|
|
47
|
+
end
|
|
28
48
|
end
|
|
29
49
|
|
|
30
50
|
def atomic_set_and_get(path)
|
|
@@ -43,10 +63,11 @@ class Postjob::Host < Postjob::Record
|
|
|
43
63
|
value
|
|
44
64
|
end
|
|
45
65
|
|
|
46
|
-
def register_host
|
|
66
|
+
def register_host(host_id:)
|
|
67
|
+
expect! host_id => [ nil, UUID_REGEXP ]
|
|
47
68
|
attributes = {}
|
|
48
69
|
Postjob.logger.debug "registering host w/#{attributes.inspect}"
|
|
49
|
-
::Postjob::Queue.host_register(attributes)
|
|
70
|
+
::Postjob::Queue.host_register(attributes, host_id: host_id)
|
|
50
71
|
end
|
|
51
72
|
end
|
|
52
73
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
CREATE OR REPLACE FUNCTION iff(cond BOOLEAN, value anyelement) RETURNS anyelement AS $$
|
|
2
|
+
BEGIN
|
|
3
|
+
IF cond THEN
|
|
4
|
+
RETURN value;
|
|
5
|
+
ELSE
|
|
6
|
+
RETURN NULL;
|
|
7
|
+
END IF;
|
|
8
|
+
END;
|
|
9
|
+
$$ LANGUAGE plpgsql;
|
|
10
|
+
|
|
11
|
+
CREATE OR REPLACE FUNCTION iff(cond BOOLEAN, value anyelement, else_value anyelement) RETURNS anyelement AS $$
|
|
12
|
+
BEGIN
|
|
13
|
+
IF cond THEN
|
|
14
|
+
RETURN value;
|
|
15
|
+
ELSE
|
|
16
|
+
RETURN else_value;
|
|
17
|
+
END IF;
|
|
18
|
+
END;
|
|
19
|
+
$$ LANGUAGE plpgsql;
|
|
@@ -149,29 +149,3 @@ BEGIN
|
|
|
149
149
|
PERFORM {SCHEMA_NAME}._wakeup_parent_job(p_worker_session_id, job_id);
|
|
150
150
|
END;
|
|
151
151
|
$$ LANGUAGE plpgsql;
|
|
152
|
-
|
|
153
|
-
CREATE OR REPLACE FUNCTION {SCHEMA_NAME}._set_job_zombie(
|
|
154
|
-
job_id BIGINT,
|
|
155
|
-
p_fast_mode BOOLEAN) RETURNS VOID AS $$
|
|
156
|
-
DECLARE
|
|
157
|
-
p_worker_session_id UUID;
|
|
158
|
-
BEGIN
|
|
159
|
-
p_worker_session_id := {SCHEMA_NAME}._null_uuid();
|
|
160
|
-
|
|
161
|
-
PERFORM {SCHEMA_NAME}._reset_job_processing(p_worker_session_id, job_id);
|
|
162
|
-
|
|
163
|
-
-- write error info
|
|
164
|
-
UPDATE {SCHEMA_NAME}.postjobs
|
|
165
|
-
SET
|
|
166
|
-
error='Zombie',
|
|
167
|
-
error_message='zombie',
|
|
168
|
-
error_backtrace=NULL,
|
|
169
|
-
failed_attempts=failed_attempts+1,
|
|
170
|
-
next_run_at=NULL
|
|
171
|
-
WHERE id=job_id;
|
|
172
|
-
|
|
173
|
-
-- prepare next run, if any
|
|
174
|
-
PERFORM {SCHEMA_NAME}._prepare_rerun(job_id, 'err', p_fast_mode);
|
|
175
|
-
PERFORM {SCHEMA_NAME}._wakeup_parent_job(p_worker_session_id, job_id);
|
|
176
|
-
END;
|
|
177
|
-
$$ LANGUAGE plpgsql;
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
-- hosts ----------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
+
DO $$
|
|
4
|
+
BEGIN
|
|
5
|
+
CREATE TYPE {SCHEMA_NAME}.host_statuses AS ENUM (
|
|
6
|
+
'running', -- host is running
|
|
7
|
+
'shutdown', -- host is shutting down
|
|
8
|
+
'stopped' -- host is not running
|
|
9
|
+
);
|
|
10
|
+
EXCEPTION
|
|
11
|
+
WHEN duplicate_object THEN RAISE DEBUG 'type {SCHEMA_NAME}.host_statuses already exists';
|
|
12
|
+
END;
|
|
13
|
+
$$;
|
|
14
|
+
|
|
15
|
+
|
|
3
16
|
-- The hosts table records available hosts. hosts are the basis for the
|
|
4
17
|
-- sticky jobs feature - a sticky job is a job which can only be checked
|
|
5
18
|
-- out by a specific host.
|
|
@@ -7,6 +20,10 @@
|
|
|
7
20
|
|
|
8
21
|
CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.hosts (
|
|
9
22
|
id UUID PRIMARY KEY DEFAULT (gen_random_uuid()), -- UUID identifying a worker **host**
|
|
23
|
+
|
|
24
|
+
-- The workflow status, one of 'running', 'shutdown', 'stopped'
|
|
25
|
+
status {SCHEMA_NAME}.host_statuses NOT NULL DEFAULT 'stopped',
|
|
26
|
+
|
|
10
27
|
attributes JSONB NOT NULL DEFAULT '{}'::JSONB,
|
|
11
28
|
created_at timestamp NOT NULL DEFAULT (now() at time zone 'utc')
|
|
12
29
|
);
|
|
@@ -14,6 +31,14 @@ CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.hosts (
|
|
|
14
31
|
CREATE INDEX IF NOT EXISTS hosts_attributes_idx
|
|
15
32
|
ON {SCHEMA_NAME}.hosts USING GIN (attributes jsonb_path_ops);
|
|
16
33
|
|
|
34
|
+
DO $$
|
|
35
|
+
BEGIN
|
|
36
|
+
ALTER TABLE {SCHEMA_NAME}.hosts ADD COLUMN status {SCHEMA_NAME}.host_statuses NOT NULL DEFAULT 'stopped';
|
|
37
|
+
EXCEPTION
|
|
38
|
+
WHEN duplicate_column THEN RAISE DEBUG 'column {SCHEMA_NAME}.hosts.host_statuses already exists';
|
|
39
|
+
END;
|
|
40
|
+
$$;
|
|
41
|
+
|
|
17
42
|
-- returns _null_host_id ------------------------------------------------------
|
|
18
43
|
|
|
19
44
|
DO $$
|
|
@@ -28,15 +53,33 @@ $$ LANGUAGE plpgsql;
|
|
|
28
53
|
|
|
29
54
|
-- host_register: registers a host --------------------------------------------
|
|
30
55
|
|
|
31
|
-
|
|
56
|
+
DROP FUNCTION IF EXISTS {SCHEMA_NAME}.host_register(p_attrs JSONB);
|
|
57
|
+
CREATE OR REPLACE FUNCTION {SCHEMA_NAME}.host_register(p_attrs JSONB, v_id UUID)
|
|
32
58
|
RETURNS UUID
|
|
33
59
|
AS $$
|
|
34
|
-
DECLARE
|
|
35
|
-
v_id UUID;
|
|
36
60
|
BEGIN
|
|
37
|
-
v_id
|
|
61
|
+
IF v_id IS NULL THEN
|
|
62
|
+
v_id := gen_random_uuid();
|
|
63
|
+
END IF;
|
|
64
|
+
|
|
38
65
|
INSERT INTO {SCHEMA_NAME}.hosts (id, attributes)
|
|
39
|
-
VALUES (v_id, p_attrs)
|
|
66
|
+
VALUES (v_id, p_attrs)
|
|
67
|
+
ON CONFLICT(id) DO UPDATE SET attributes=p_attrs;
|
|
40
68
|
RETURN v_id;
|
|
41
69
|
END;
|
|
42
70
|
$$ LANGUAGE plpgsql;
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
-- wakeup runners after changing hosts
|
|
74
|
+
|
|
75
|
+
-- when a host changes its status to shutdown, all of its runners should
|
|
76
|
+
-- shutdown quickly.
|
|
77
|
+
|
|
78
|
+
BEGIN;
|
|
79
|
+
DROP TRIGGER IF EXISTS _wakeup_runners ON {SCHEMA_NAME}.hosts;
|
|
80
|
+
|
|
81
|
+
CREATE TRIGGER _wakeup_runners AFTER UPDATE
|
|
82
|
+
ON {SCHEMA_NAME}.hosts
|
|
83
|
+
FOR EACH STATEMENT
|
|
84
|
+
EXECUTE PROCEDURE {SCHEMA_NAME}._wakeup_runners();
|
|
85
|
+
COMMIT;
|
|
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.worker_sessions (
|
|
|
24
24
|
host_id UUID NOT NULL REFERENCES {SCHEMA_NAME}.hosts ON DELETE CASCADE, -- UUID identifying a worker **host**
|
|
25
25
|
client_socket VARCHAR, -- host:port of connection (from pg_stat_activity)
|
|
26
26
|
workflows VARCHAR[] NOT NULL, -- array of workflow versions available on that worker
|
|
27
|
+
queues VARCHAR[] NOT NULL, -- array of queue names available on that worker
|
|
27
28
|
attributes JSONB NOT NULL DEFAULT '{}'::JSONB,
|
|
28
29
|
created_at timestamp NOT NULL DEFAULT (now() at time zone 'utc')
|
|
29
30
|
);
|
|
@@ -38,7 +39,17 @@ DO $$
|
|
|
38
39
|
null_uuid UUID := {SCHEMA_NAME}._null_uuid();
|
|
39
40
|
BEGIN
|
|
40
41
|
IF NOT EXISTS (SELECT 1 FROM {SCHEMA_NAME}.worker_sessions WHERE id = null_uuid) THEN
|
|
41
|
-
INSERT INTO {SCHEMA_NAME}.worker_sessions(id, host_id, workflows) VALUES(null_uuid, null_uuid, '{}');
|
|
42
|
+
INSERT INTO {SCHEMA_NAME}.worker_sessions(id, host_id, workflows, queues) VALUES(null_uuid, null_uuid, '{}', ARRAY[]::varchar[]);
|
|
42
43
|
END IF;
|
|
43
44
|
END;
|
|
44
45
|
$$ LANGUAGE plpgsql;
|
|
46
|
+
|
|
47
|
+
DO $$
|
|
48
|
+
BEGIN
|
|
49
|
+
ALTER TABLE {SCHEMA_NAME}.worker_sessions ADD COLUMN queues VARCHAR[];
|
|
50
|
+
UPDATE {SCHEMA_NAME}.worker_sessions SET queues = Array['ruby'] WHERE queues IS NULL;
|
|
51
|
+
ALTER TABLE {SCHEMA_NAME}.worker_sessions ALTER COLUMN queues SET NOT NULL;
|
|
52
|
+
EXCEPTION
|
|
53
|
+
WHEN duplicate_column THEN RAISE DEBUG 'column {SCHEMA_NAME}.worker_sessions.queues already exists';
|
|
54
|
+
END;
|
|
55
|
+
$$;
|