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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/postjob/cli/cron.rb +24 -0
  3. data/lib/postjob/cli/db.rb +1 -2
  4. data/lib/postjob/cli/events.rb +2 -2
  5. data/lib/postjob/cli/heartbeat.rb +2 -2
  6. data/lib/postjob/cli/helpers.rb +28 -0
  7. data/lib/postjob/cli/hosts.rb +32 -15
  8. data/lib/postjob/cli/job.rb +2 -0
  9. data/lib/postjob/cli/ps.rb +4 -26
  10. data/lib/postjob/cli/queues.rb +66 -0
  11. data/lib/postjob/cli/run.rb +19 -6
  12. data/lib/postjob/cli/sessions.rb +5 -4
  13. data/lib/postjob/host.rb +26 -5
  14. data/lib/postjob/migrations/001_helpers.sql +19 -0
  15. data/lib/postjob/migrations/007_job_results.sql +0 -26
  16. data/lib/postjob/migrations/012_hosts.sql +48 -5
  17. data/lib/postjob/migrations/013_worker_sessions.sql +12 -1
  18. data/lib/postjob/migrations/013a_checkout_runnable.sql +47 -5
  19. data/lib/postjob/migrations/016_sessions_functions.sql +5 -3
  20. data/lib/postjob/migrations/017_zombie_check.sql +64 -18
  21. data/lib/postjob/migrations/018_heartbeat.sql +36 -3
  22. data/lib/postjob/migrations/021_cron_jobs.sql +12 -11
  23. data/lib/postjob/migrations.rb +1 -1
  24. data/lib/postjob/queue/notifications.rb +15 -7
  25. data/lib/postjob/queue.rb +21 -8
  26. data/lib/postjob/runner.rb +1 -1
  27. data/lib/postjob/worker_session.rb +9 -5
  28. data/lib/postjob.rb +62 -26
  29. data/lib/tools/heartbeat.rb +2 -1
  30. data/spec/postjob/events/job_event_spec.rb +2 -2
  31. data/spec/postjob/worker_session_spec.rb +1 -1
  32. data/spec/postjob/zombie_spec.rb +54 -0
  33. data/spec/spec_helper.rb +2 -0
  34. data/spec/support/test_helper.rb +3 -8
  35. metadata +12 -9
  36. 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: e26be7454c3a0ddb3d26d67b4e51918e8e16e306
4
- data.tar.gz: f837746f80bba5cebbdd0fea7c7ad7c26eacbc5b
3
+ metadata.gz: 175e2a30ec560a1728f3b3a62750bd5afb38df3d
4
+ data.tar.gz: 54328faf7c7966c21743575ab8d7a9c94b66180d
5
5
  SHA512:
6
- metadata.gz: 361995efdeda5d1ca51de93c0807e37737581c32f53c25f20dc45c50c65dc008260abccc0d421776d59186997071fd2aa14cc23828f95145de6536bcf7859589
7
- data.tar.gz: f374453e87325c9ddb05d4b1cad18f20d26b3a6c64a596de23ab471bc6bbc57017b20df5c589944e1cc90994ad717e4d1e57b7fa8ec6ab953ef48e6ec0a33adc
6
+ metadata.gz: 98960758ed5262988f82ac8cb046752be425b15bd91b2355e575f69007d43b7161580d2796af4d4bd6ebcea71d87f955d442f061ad43ba720efb02a2da172584
7
+ data.tar.gz: 5f1bdd2b015c706cddc7e5874bcbfada7ebc4d3c22f34511d6f5d14eb0c3f28261eebd834670cbd1e68920776b1132a20482d7c0bc58e4a13d55edbb175ee996
@@ -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)
@@ -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
- records = "SELECT name,value FROM postjob.settings ORDER BY name"
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
@@ -37,7 +37,7 @@ module Postjob::CLI
37
37
  # Example:
38
38
  #
39
39
  # postjob events
40
- def events(limit: "100")
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: "100")
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: "100")
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: "100")
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
@@ -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
- heartbeat.attributes AS heartbeat,
16
- heartbeat.created_at AS heartbeat_created_at
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
- worker_sessions.host_id,
21
- MAX(events.id) AS event_id
22
- FROM postjob.worker_sessions
23
- LEFT JOIN postjob.events events ON events.worker_session_id=worker_sessions.id
24
- WHERE events.name = 'heartbeat'
25
- GROUP BY worker_sessions.host_id
26
- ) q ON q.host_id=hosts.id
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: "100")
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
@@ -87,6 +87,8 @@ module Postjob::CLI
87
87
  public
88
88
 
89
89
  def registry
90
+ require "table_print"
91
+
90
92
  workflows_with_versions = Postjob::Registry.workflows.keys.reject { |k| k[1] == "" }
91
93
  workflows_with_versions = workflows_with_versions.sort_by { |name, _version| name }
92
94
 
@@ -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 6) || '...', 'yes')
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: "100", tags: nil, queue: nil, only_root: nil)
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: "100", tags: nil, queue: nil)
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: "100", tags: nil, full: false, queue: nil, only_root: false)
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
@@ -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 queue.
15
- # - quiet don't show progress.
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: nil, quiet: false, fast: false)
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
- queue = queue.split(",") if queue
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, queue: queue) do |job_id|
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
@@ -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: "100")
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: "100")
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
- env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
27
- File.join Dir.tmpdir, "postjob.#{env}.#{Process.uid}.host_id"
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
- CREATE OR REPLACE FUNCTION {SCHEMA_NAME}.host_register(p_attrs JSONB)
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 := gen_random_uuid();
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
+ $$;