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
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
|
+
$$;
|