workhorse 0.5.1 → 0.6.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: a9b9363247117e0981b8b7304b3ce21d62fc7edf4d5ad3e804d707c0c39ce04e
4
- data.tar.gz: 29a8d3cead2aad95cff3823ab2c88710d97ac26be534aeaba5d3b5570d6ee4a1
3
+ metadata.gz: 31b8b6352a1a32590d8c3d817279a0a7d2cd4c594efe27806acdbea0d1d42062
4
+ data.tar.gz: e68c163ad8f9dbf36ff2dbbd0e4696891b6daf0654b7c343b53f36d7404d8335
5
5
  SHA512:
6
- metadata.gz: d981452366b82c427ab3a8dce44ff100c704e611a39d48e855ef2239a96d67c443d1b7b95f4c87587474a0879430a16eebe1229b89a3fdb3c4414898ed6dd01d
7
- data.tar.gz: 1227833eea17e9202e4d82ea7f9eb36f685e5039a3f63795c35eda88e0eb18de1e0f03d576958177403f3e0e3e2e2842627313fd9362252d5a8eeace29f368f2
6
+ metadata.gz: 4472fe2d228c7c6fcac24263f1ea33473593971ebaa11bf051131ecf8a810bc162152e5f9cd30aed5c1d9d4b59224f57c0a5b75a4ecb922c3e8bc8303bf3ba48
7
+ data.tar.gz: cc36d41fe33f5292ae3fbf99c32be84fc0ebd577a43d6e92b7b66009c315bbad4ca650fc0e9d41a37ebb03c3b5b2e82dbfbc9300b3fe889ec30929f7768015b6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Workhorse Change log
2
2
 
3
+ ## 0.6.0 - 2918-07-03
4
+
5
+ * Adapt {Workhorse::Daemon} to support a specific block for each worker. This
6
+ allows, for example, to run a scheduler like Rufus in a separate worker
7
+ process.
8
+
9
+ If you're using the daemon class, you will need to restructure your workhorse
10
+ starting script.
11
+
12
+ Since there is no `count` attribute anymore, transfer this:
13
+
14
+ ```ruby
15
+ Workhorse::Daemon::ShellHandler.run count: 5 do
16
+ # ... worker code
17
+ end
18
+ ```
19
+
20
+ into this:
21
+
22
+ ```ruby
23
+ Workhorse::Daemon::ShellHandler.run do |daemon|
24
+ 5.times do
25
+ daemon.worker do
26
+ # ... worker code
27
+ end
28
+ end
29
+ end
30
+ ```
31
+
32
+ See readme for more information.
33
+
3
34
  ## 0.5.1 - 2019-06-27
4
35
 
5
36
  * Add daemon command `kill`
data/README.md CHANGED
@@ -153,25 +153,28 @@ achieving regular execution:
153
153
 
154
154
  require './config/environment'
155
155
 
156
- Workhorse::Daemon::ShellHandler.run do
157
- worker = Workhorse::Worker.new(pool_size: 5, polling_interval: 10, logger: Rails.logger)
158
- scheduler = Rufus::Scheduler.new
156
+ Workhorse::Daemon::ShellHandler.run do |daemon|
157
+ # Start scheduler process
158
+ daemon.worker 'Scheduler' do
159
+ scheduler = Rufus::Scheduler.new
159
160
 
160
- worker.start
161
+ scheduler.cron '0/10 * * * *' do
162
+ Workhorse.enqueue Workhorse::Jobs::CleanupSucceededJobs.new
163
+ end
161
164
 
162
- scheduler.cron '0/10 * * * *' do
163
- Workhorse.enqueue Workhorse::Jobs::CleanupSucceededJobs.new
164
- end
165
+ Signal.trap 'TERM' do
166
+ scheduler.shutdown
167
+ end
165
168
 
166
- Signal.trap 'TERM' do
167
- scheduler.shutdown
168
- Thread.new do
169
- worker.shutdown
170
- end.join
169
+ scheduler.join
171
170
  end
172
171
 
173
- scheduler.join
174
- worker.wait
172
+ # Start 5 worker processes with 3 threads each
173
+ 5.times do
174
+ daemon.worker do
175
+ Workhorse::Worker.new(pool_size: 3, polling_interval: 10, logger: Rails.logger)
176
+ end
177
+ end
175
178
  end
176
179
  ```
177
180
 
@@ -224,7 +227,7 @@ For this case, the workhorse install routine automatically creates the file
224
227
  The script can be called as follows:
225
228
 
226
229
  ```bash
227
- RAILS_ENV=production bundle exec bin/workhorse.rb start|stop|status|watch|restart|usage
230
+ RAILS_ENV=production bundle exec bin/workhorse.rb start|stop|kill|status|watch|restart|usage
228
231
  ```
229
232
 
230
233
  #### Background and customization
@@ -233,10 +236,14 @@ Within the shell handler, you can instantiate, configure, and start a worker as
233
236
  described under [Start workers manually](#start-workers-manually):
234
237
 
235
238
  ```ruby
236
- Workhorse::Daemon::ShellHandler.run count: 5 do
237
- # This will be run 5 times, each time in a separate process. Per process, it
238
- # will be able to process 3 jobs concurrently.
239
- Workhorse::Worker.start_and_wait(pool_size: 3, logger: Rails.logger)
239
+ Workhorse::Daemon::ShellHandler.run do |daemon|
240
+ 5.times do
241
+ daemon.worker do
242
+ # This will be run 5 times, each time in a separate process. Per process, it
243
+ # will be able to process 3 jobs concurrently.
244
+ Workhorse::Worker.start_and_wait(pool_size: 3, logger: Rails.logger)
245
+ end
246
+ end
240
247
  end
241
248
  ```
242
249
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.1
1
+ 0.6.0
data/lib/workhorse.rb CHANGED
@@ -4,6 +4,7 @@ require 'active_record'
4
4
  require 'concurrent'
5
5
 
6
6
  require 'workhorse/enqueuer'
7
+ require 'workhorse/scoped_env'
7
8
 
8
9
  module Workhorse
9
10
  # Check if the available Arel version is greater or equal than 7.0.0
@@ -1,17 +1,32 @@
1
1
  module Workhorse
2
2
  class Daemon
3
- def initialize(count: 1, pidfile: nil, quiet: false, &block)
4
- @count = count
3
+ class Worker
4
+ attr_reader :id
5
+ attr_reader :name
6
+ attr_reader :block
7
+
8
+ def initialize(id, name, &block)
9
+ @id = id
10
+ @name = name
11
+ @block = block
12
+ end
13
+ end
14
+
15
+ def initialize(pidfile: nil, quiet: false, &block)
5
16
  @pidfile = pidfile
6
17
  @quiet = quiet
7
- @block = block
18
+ @workers = []
19
+
20
+ yield ScopedEnv.new(self, [:worker])
8
21
 
9
- fail 'Count must be an integer > 0.' unless count.is_a?(Integer) && count > 0
22
+ @count = @workers.count
23
+
24
+ fail 'No workers are defined.' if @count == 1
10
25
 
11
26
  FileUtils.mkdir_p('tmp/pids')
12
27
 
13
28
  if @pidfile.nil?
14
- @pidfile = count > 1 ? 'tmp/pids/workhorse.%i.pid' : 'tmp/pids/workhorse.pid'
29
+ @pidfile = @count > 1 ? 'tmp/pids/workhorse.%i.pid' : 'tmp/pids/workhorse.pid'
15
30
  elsif @count > 1 && !@pidfile.include?('%s')
16
31
  fail 'Pidfile must include placeholder "%s" for worker id when specifying a count > 1.'
17
32
  elsif @count == 0 && @pidfile.include?('%s')
@@ -19,22 +34,26 @@ module Workhorse
19
34
  end
20
35
  end
21
36
 
37
+ def worker(name = 'Job Worker', &block)
38
+ @workers << Worker.new(@workers.size + 1, name, &block)
39
+ end
40
+
22
41
  def start
23
42
  code = 0
24
43
 
25
- for_each_worker do |worker_id|
26
- pid_file, pid = read_pid(worker_id)
44
+ for_each_worker do |worker|
45
+ pid_file, pid = read_pid(worker)
27
46
 
28
47
  if pid_file && pid
29
- warn "Worker ##{worker_id}: Already started (PID #{pid})"
48
+ warn "Worker ##{worker.id} (#{worker.name}): Already started (PID #{pid})"
30
49
  code = 1
31
50
  elsif pid_file
32
51
  File.delete pid_file
33
- puts "Worker ##{worker_id}: Starting (stale pid file)"
34
- start_worker worker_id
52
+ puts "Worker ##{worker.id} (#{worker.name}): Starting (stale pid file)"
53
+ start_worker worker
35
54
  else
36
- warn "Worker ##{worker_id}: Starting"
37
- start_worker worker_id
55
+ warn "Worker ##{worker.id} (#{worker.name}): Starting"
56
+ start_worker worker
38
57
  end
39
58
  end
40
59
 
@@ -44,17 +63,17 @@ module Workhorse
44
63
  def stop(kill = false)
45
64
  code = 0
46
65
 
47
- for_each_worker do |worker_id|
48
- pid_file, pid = read_pid(worker_id)
66
+ for_each_worker do |worker|
67
+ pid_file, pid = read_pid(worker)
49
68
 
50
69
  if pid_file && pid
51
- puts "Worker ##{worker_id}: Stopping"
70
+ puts "Worker (#{worker.name}) ##{worker.id}: Stopping"
52
71
  stop_worker pid_file, pid, kill
53
72
  elsif pid_file
54
73
  File.delete pid_file
55
- puts "Worker ##{worker_id}: Already stopped (stale PID file)"
74
+ puts "Worker (#{worker.name}) ##{worker.id}: Already stopped (stale PID file)"
56
75
  else
57
- warn "Worker ##{worker_id}: Already stopped"
76
+ warn "Worker (#{worker.name}) ##{worker.id}: Already stopped"
58
77
  code = 1
59
78
  end
60
79
  end
@@ -65,16 +84,16 @@ module Workhorse
65
84
  def status(quiet: false)
66
85
  code = 0
67
86
 
68
- for_each_worker do |worker_id|
69
- pid_file, pid = read_pid(worker_id)
87
+ for_each_worker do |worker|
88
+ pid_file, pid = read_pid(worker)
70
89
 
71
90
  if pid_file && pid
72
- puts "Worker ##{worker_id}: Running" unless quiet
91
+ puts "Worker ##{worker.id} (#{worker.name}): Running" unless quiet
73
92
  elsif pid_file
74
- warn "Worker ##{worker_id}: Not running (stale PID file)" unless quiet
93
+ warn "Worker ##{worker.id} (#{worker.name}): Not running (stale PID file)" unless quiet
75
94
  code = 1
76
95
  else
77
- warn "Worker ##{worker_id}: Not running" unless quiet
96
+ warn "Worker ##{worker.id} (#{worker.name}): Not running" unless quiet
78
97
  code = 1
79
98
  end
80
99
  end
@@ -104,15 +123,15 @@ module Workhorse
104
123
  private
105
124
 
106
125
  def for_each_worker(&block)
107
- 1.upto(@count, &block)
126
+ @workers.each(&block)
108
127
  end
109
128
 
110
- def start_worker(worker_id)
129
+ def start_worker(worker)
111
130
  pid = fork do
112
- $0 = process_name(worker_id)
113
- @block.call
131
+ $0 = process_name(worker)
132
+ worker.block.call
114
133
  end
115
- IO.write(pid_file_for(worker_id), pid)
134
+ IO.write(pid_file_for(worker), pid)
116
135
  end
117
136
 
118
137
  def stop_worker(pid_file, pid, kill = false)
@@ -131,14 +150,14 @@ module Workhorse
131
150
  File.delete(pid_file)
132
151
  end
133
152
 
134
- def process_name(worker_id)
153
+ def process_name(worker)
135
154
  if defined?(Rails)
136
155
  path = Rails.root
137
156
  else
138
157
  path = $PROGRAM_NAME
139
158
  end
140
159
 
141
- return "Workhorse Worker ##{worker_id}: #{path}"
160
+ return "Workhorse #{worker.name} ##{worker.id}: #{path}"
142
161
  end
143
162
 
144
163
  def process?(pid)
@@ -150,12 +169,12 @@ module Workhorse
150
169
  end
151
170
  end
152
171
 
153
- def pid_file_for(worker_id)
154
- @pidfile % worker_id
172
+ def pid_file_for(worker)
173
+ @pidfile % worker.id
155
174
  end
156
175
 
157
- def read_pid(worker_id)
158
- file = pid_file_for(worker_id)
176
+ def read_pid(worker)
177
+ file = pid_file_for(worker)
159
178
 
160
179
  if File.exist?(file)
161
180
  pid = IO.read(file).to_i
@@ -0,0 +1,23 @@
1
+ module Workhorse
2
+ class ScopedEnv
3
+ def initialize(delegation_object, methods, backup_binding = nil)
4
+ @delegation_object = delegation_object
5
+ @methods = methods
6
+ @backup_binding = backup_binding
7
+ end
8
+
9
+ def method_missing(symbol, *args, &block)
10
+ if @methods.include?(symbol)
11
+ @delegation_object.send(symbol, *args, &block)
12
+ elsif @backup_binding&.respond_to?(symbol)
13
+ @backup_binding.send(symbol, *args, &block)
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def respond_to_missing?(symbol, include_private = false)
20
+ @methods.include?(symbol) || super
21
+ end
22
+ end
23
+ end
data/workhorse.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: workhorse 0.5.1 ruby lib
2
+ # stub: workhorse 0.6.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "workhorse".freeze
6
- s.version = "0.5.1"
6
+ s.version = "0.6.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
10
10
  s.authors = ["Sitrox".freeze]
11
- s.date = "2019-06-27"
12
- s.files = [".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, ".travis.yml".freeze, "CHANGELOG.md".freeze, "FAQ.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "bin/rubocop".freeze, "lib/active_job/queue_adapters/workhorse_adapter.rb".freeze, "lib/generators/workhorse/install_generator.rb".freeze, "lib/generators/workhorse/templates/bin/workhorse.rb".freeze, "lib/generators/workhorse/templates/config/initializers/workhorse.rb".freeze, "lib/generators/workhorse/templates/create_table_jobs.rb".freeze, "lib/workhorse.rb".freeze, "lib/workhorse/daemon.rb".freeze, "lib/workhorse/daemon/shell_handler.rb".freeze, "lib/workhorse/db_job.rb".freeze, "lib/workhorse/enqueuer.rb".freeze, "lib/workhorse/jobs/cleanup_succeeded_jobs.rb".freeze, "lib/workhorse/jobs/run_active_job.rb".freeze, "lib/workhorse/jobs/run_rails_op.rb".freeze, "lib/workhorse/performer.rb".freeze, "lib/workhorse/poller.rb".freeze, "lib/workhorse/pool.rb".freeze, "lib/workhorse/worker.rb".freeze, "test/active_job/queue_adapters/workhorse_adapter_test.rb".freeze, "test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/db_job_test.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze, "workhorse.gemspec".freeze]
11
+ s.date = "2019-07-03"
12
+ s.files = [".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, ".travis.yml".freeze, "CHANGELOG.md".freeze, "FAQ.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "bin/rubocop".freeze, "lib/active_job/queue_adapters/workhorse_adapter.rb".freeze, "lib/generators/workhorse/install_generator.rb".freeze, "lib/generators/workhorse/templates/bin/workhorse.rb".freeze, "lib/generators/workhorse/templates/config/initializers/workhorse.rb".freeze, "lib/generators/workhorse/templates/create_table_jobs.rb".freeze, "lib/workhorse.rb".freeze, "lib/workhorse/daemon.rb".freeze, "lib/workhorse/daemon/shell_handler.rb".freeze, "lib/workhorse/db_job.rb".freeze, "lib/workhorse/enqueuer.rb".freeze, "lib/workhorse/jobs/cleanup_succeeded_jobs.rb".freeze, "lib/workhorse/jobs/run_active_job.rb".freeze, "lib/workhorse/jobs/run_rails_op.rb".freeze, "lib/workhorse/performer.rb".freeze, "lib/workhorse/poller.rb".freeze, "lib/workhorse/pool.rb".freeze, "lib/workhorse/scoped_env.rb".freeze, "lib/workhorse/worker.rb".freeze, "test/active_job/queue_adapters/workhorse_adapter_test.rb".freeze, "test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/db_job_test.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze, "workhorse.gemspec".freeze]
13
13
  s.rubygems_version = "3.0.3".freeze
14
14
  s.summary = "Multi-threaded job backend with database queuing for ruby.".freeze
15
15
  s.test_files = ["test/active_job/queue_adapters/workhorse_adapter_test.rb".freeze, "test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/db_job_test.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: workhorse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sitrox
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-27 00:00:00.000000000 Z
11
+ date: 2019-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -213,6 +213,7 @@ files:
213
213
  - lib/workhorse/performer.rb
214
214
  - lib/workhorse/poller.rb
215
215
  - lib/workhorse/pool.rb
216
+ - lib/workhorse/scoped_env.rb
216
217
  - lib/workhorse/worker.rb
217
218
  - test/active_job/queue_adapters/workhorse_adapter_test.rb
218
219
  - test/lib/db_schema.rb