ductwork 0.8.1 → 0.9.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fde482283602dcf363baed46b5d8468757ddb8f6cc6fabb9b9d5e37497e10f4
4
- data.tar.gz: 3aa0f1a9aa6f77b47997b64b246ed3dc7371e63554943110de8e06f98135f9d3
3
+ metadata.gz: 9edf0e9d07be90e39d099d4f2f9a65677b4b37144c8fa5c7d3d0a88ab81ed1a3
4
+ data.tar.gz: 91b2177670ee8333062ed6182512f4c5b2cd3ecfc3db9c36a77fcdf54397f65a
5
5
  SHA512:
6
- metadata.gz: bc475be1df97dada6099f8210b5694cbc4d90e5466d565a50674bac796fc252923f19a18e6beae3cf673db8d25fccf4bfb8178622d8574fd0e2b911e27dc8a0f
7
- data.tar.gz: e1caa07d54fe23a2f2ea1b90809371a53bfd791eba6751bab9b65dac254c7f7935cf54f4b217adb68e084abd478192000090c91005d644188f64e2d7930264fd
6
+ metadata.gz: 7583ac23a38875a63ac31b333348ddc9ec4e35b431ec90b335eb2580a241c508acbfcfdc462957a2ce39f5c7cb6094d146f60b8b4e49b753bfc0ead13967bcc4
7
+ data.tar.gz: 466d021eb4635d0d7ba284569100905069598f5c58edbdc3480e9db3d2de2ba09485b72935280598ac8223374cefebfa16b5d3c60a17709df9d2455a8c1273c5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Ductwork Changelog
2
2
 
3
+ ## [0.9.0]
4
+
5
+ - feat: add health check to job worker runner process - this is a basic check if a thread is healthy via `Thread#alive?` and restarts the thread if it is dead
6
+
3
7
  ## [0.8.1]
4
8
 
5
9
  - fix: properly wrap "units of work" in rails application executor in pipeline advancer
@@ -3,18 +3,44 @@
3
3
  module Ductwork
4
4
  module Processes
5
5
  class JobWorker
6
- def initialize(pipeline, running_context)
6
+ attr_reader :thread, :last_hearthbeat_at
7
+
8
+ def initialize(pipeline, id)
7
9
  @pipeline = pipeline
8
- @running_context = running_context
10
+ @id = id
11
+ @running_context = Ductwork::RunningContext.new
12
+ @thread = nil
13
+ @last_hearthbeat_at = Time.current
14
+ end
15
+
16
+ def start
17
+ @thread = Thread.new { work_loop }
18
+ @thread.name = "ductwork.job_worker.#{id}"
19
+ end
20
+
21
+ alias restart start
22
+
23
+ def alive?
24
+ thread&.alive? || false
25
+ end
26
+
27
+ def stop
28
+ running_context.shutdown!
9
29
  end
10
30
 
11
- def run
31
+ private
32
+
33
+ attr_reader :pipeline, :id, :running_context
34
+
35
+ def work_loop
12
36
  run_hooks_for(:start)
37
+
13
38
  Ductwork.logger.debug(
14
39
  msg: "Entering main work loop",
15
40
  role: :job_worker,
16
41
  pipeline: pipeline
17
42
  )
43
+
18
44
  while running_context.running?
19
45
  Ductwork.logger.debug(
20
46
  msg: "Attempting to claim job",
@@ -37,21 +63,16 @@ module Ductwork
37
63
  )
38
64
  sleep(polling_timeout)
39
65
  end
40
- end
41
-
42
- shutdown
43
- end
44
-
45
- private
46
66
 
47
- attr_reader :pipeline, :running_context
67
+ @last_hearthbeat_at = Time.current
68
+ end
48
69
 
49
- def shutdown
50
70
  Ductwork.logger.debug(
51
71
  msg: "Shutting down",
52
72
  role: :job_worker,
53
73
  pipeline: pipeline
54
74
  )
75
+
55
76
  run_hooks_for(:stop)
56
77
  end
57
78
 
@@ -6,7 +6,7 @@ module Ductwork
6
6
  def initialize(pipeline)
7
7
  @pipeline = pipeline
8
8
  @running_context = Ductwork::RunningContext.new
9
- @threads = create_threads
9
+ @job_workers = []
10
10
 
11
11
  Signal.trap(:INT) { running_context.shutdown! }
12
12
  Signal.trap(:TERM) { running_context.shutdown! }
@@ -24,7 +24,9 @@ module Ductwork
24
24
  end
25
25
 
26
26
  def run
27
- create_process!
27
+ create_process_record!
28
+ start_job_workers
29
+
28
30
  Ductwork.logger.debug(
29
31
  msg: "Entering main work loop",
30
32
  role: :job_worker_runner,
@@ -34,7 +36,7 @@ module Ductwork
34
36
  while running?
35
37
  # TODO: Increase or make configurable
36
38
  sleep(5)
37
- attempt_synchronize_threads
39
+ check_thread_health
38
40
  report_heartbeat!
39
41
  end
40
42
 
@@ -43,43 +45,29 @@ module Ductwork
43
45
 
44
46
  private
45
47
 
46
- attr_reader :pipeline, :running_context, :threads
48
+ attr_reader :pipeline, :running_context, :job_workers
47
49
 
48
- def worker_count
49
- Ductwork.configuration.job_worker_count(pipeline)
50
+ def create_process_record!
51
+ Ductwork.wrap_with_app_executor do
52
+ Ductwork::Process.create!(
53
+ pid: ::Process.pid,
54
+ machine_identifier: Ductwork::MachineIdentifier.fetch,
55
+ last_heartbeat_at: Time.current
56
+ )
57
+ end
50
58
  end
51
59
 
52
- def create_threads
53
- worker_count.times.map do |i|
54
- Ductwork.logger.debug(
55
- msg: "Creating new thread",
56
- role: :job_worker_runner,
57
- pipeline: pipeline
58
- )
59
- thread = Thread.new do
60
- Ductwork::Processes::JobWorker
61
- .new(pipeline, running_context)
62
- .run
63
- end
64
- thread.name = "ductwork.job_worker.#{i}"
60
+ def start_job_workers
61
+ Ductwork.configuration.job_worker_count(pipeline).times do |i|
62
+ job_worker = Ductwork::Processes::JobWorker.new(pipeline, i)
63
+ job_workers.push(job_worker)
64
+ job_worker.start
65
65
 
66
66
  Ductwork.logger.debug(
67
- msg: "Created new thread",
67
+ msg: "Created new job worker",
68
68
  role: :job_worker_runner,
69
69
  pipeline: pipeline
70
70
  )
71
-
72
- thread
73
- end
74
- end
75
-
76
- def create_process!
77
- Ductwork.wrap_with_app_executor do
78
- Ductwork::Process.create!(
79
- pid: ::Process.pid,
80
- machine_identifier: Ductwork::MachineIdentifier.fetch,
81
- last_heartbeat_at: Time.current
82
- )
83
71
  end
84
72
  end
85
73
 
@@ -87,13 +75,17 @@ module Ductwork
87
75
  running_context.running?
88
76
  end
89
77
 
90
- def attempt_synchronize_threads
78
+ def check_thread_health
91
79
  Ductwork.logger.debug(
92
80
  msg: "Attempting to synchronize threads",
93
81
  role: :job_worker_runner,
94
82
  pipeline: pipeline
95
83
  )
96
- threads.each { |thread| thread.join(0.1) }
84
+ job_workers.each do |job_worker|
85
+ if !job_worker.alive?
86
+ job_worker.restart
87
+ end
88
+ end
97
89
  Ductwork.logger.debug(
98
90
  msg: "Synchronizing threads timed out",
99
91
  role: :job_worker_runner,
@@ -111,31 +103,36 @@ module Ductwork
111
103
 
112
104
  def shutdown!
113
105
  running_context.shutdown!
106
+ job_workers.each(&:stop)
114
107
  await_threads_graceful_shutdown
115
- kill_remaining_threads
116
- delete_process
108
+ kill_remaining_job_workers
109
+ delete_process_record!
117
110
  end
118
111
 
119
112
  def await_threads_graceful_shutdown
120
113
  timeout = Ductwork.configuration.job_worker_shutdown_timeout
121
114
  deadline = Time.current + timeout
122
115
 
123
- Ductwork.logger.debug(msg: "Attempting graceful shutdown", role: :job_worker_runner)
124
- while Time.current < deadline && threads.any?(&:alive?)
125
- threads.each do |thread|
116
+ Ductwork.logger.debug(
117
+ msg: "Attempting graceful shutdown",
118
+ role: :job_worker_runner
119
+ )
120
+
121
+ while Time.current < deadline && job_workers.any?(&:alive?)
122
+ job_workers.each do |job_worker|
126
123
  break if Time.current < deadline
127
124
 
128
125
  # TODO: Maybe make this configurable. If there's a ton of workers
129
126
  # it may not even get to the "later" ones depending on the timeout
130
- thread.join(1)
127
+ job_worker.thread.join(1)
131
128
  end
132
129
  end
133
130
  end
134
131
 
135
- def kill_remaining_threads
136
- threads.each do |thread|
137
- if thread.alive?
138
- thread.kill
132
+ def kill_remaining_job_workers
133
+ job_workers.each do |job_worker|
134
+ if job_worker.alive?
135
+ job_worker.thread.kill
139
136
  Ductwork.logger.debug(
140
137
  msg: "Killed thread",
141
138
  role: :job_worker_runner,
@@ -145,7 +142,7 @@ module Ductwork
145
142
  end
146
143
  end
147
144
 
148
- def delete_process
145
+ def delete_process_record!
149
146
  Ductwork.wrap_with_app_executor do
150
147
  Ductwork::Process.find_by!(
151
148
  pid: ::Process.pid,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ductwork
4
- VERSION = "0.8.1"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ductwork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Ewing