workhorse 1.3.0.rc2 → 1.3.0.rc4

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: f2eebb8cbdfe18e11988f8ba9b0ef440c8e4cf0ad29efb6e8d6e98fd036fb2fe
4
- data.tar.gz: 97ee81bce90ab26428b2e17b1a4049762230131840c270762f7d85b3de0b7f7c
3
+ metadata.gz: dca93061dd92dfbd43f1a44dcb2958181257d16079f408114af13a80197e48c6
4
+ data.tar.gz: 8a4c202c76b7380912288331bc8ef24a4dc8839c4455a4c455b48dddeb631588
5
5
  SHA512:
6
- metadata.gz: ddca8c7bcee25b1836f9d111308450679e9773c643c8ff5ad86758e8b366f11f12d43bfbbf101ba1d52ac67ea83efa5c1c27435efbab55f8b33acdc0aa200740
7
- data.tar.gz: f58bdd1efd155864dd32a99a9b514f980c4d81a30a26adaee8941b3b5be473f387d22ba4dce4e4969fd2e043ca06950ef9d75f048a51e7ce431f0a19095d63ea
6
+ metadata.gz: c8b5829f8d84bd7ed135bfcc467f5b9afd313acd9f36d94b90bf77e106b0cf6f627189bf89e6f0ba31aecf791875fd5217091b3b5034d109473623da5d1b4cf3
7
+ data.tar.gz: b50b7514b14ce6170b192835a9d61b03d84577de0466ef471bd60f5561a873930151d61aa9c1c3638dce889c0bdf6432a6ae7289f9e061ff4e8220313dc3937a
@@ -48,7 +48,7 @@ jobs:
48
48
  - name: Set up Ruby
49
49
  uses: ruby/setup-ruby@v1
50
50
  with:
51
- ruby-version: 2.5.1
51
+ ruby-version: 3.0.1
52
52
  bundler-cache: true
53
53
  - name: Run rubocop
54
54
  run: bundle exec rubocop
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Workhorse Changelog
2
2
 
3
+ ## 1.3.0.rc4 - 2025-08-27
4
+
5
+ * Fix race-condition in polling mechanism which could result in workers
6
+ trying to run a job that is not yet locked.
7
+
8
+ Sitrox reference: #128333.
9
+
10
+ ## 1.3.0.rc3 - 2025-06-10
11
+
12
+ * Require Rails 7.0.0 or later
13
+
3
14
  ## 1.3.0.rc2 - 2025-06-10
4
15
 
5
16
  * Update development dependencies
@@ -46,7 +57,7 @@
46
57
  * Comply with RuboCop
47
58
 
48
59
  * Skip `at_exit` handlers when exiting in ShellHandler. This ensures
49
- compatibility with the `debug` gem, which whould otherwise hang when using the
60
+ compatibility with the `debug` gem, which would otherwise hang when using the
50
61
  Workhorse shell handler.
51
62
 
52
63
  Sitrox reference: #128333.
@@ -296,7 +307,7 @@ battle before it can be considered stable.
296
307
  * Simplify locking during polling. Other than locking individual jobs, pollers
297
308
  now acquire a global lock. While this can lead to many pollers waiting for
298
309
  each others locks, performing a poll is usually done very quickly and the
299
- performance drawback is to be considered neglegible. This change should work
310
+ performance drawback is to be considered negligible. This change should work
300
311
  around some deadlock issues as well as an issue where a job was obtained by
301
312
  more than one poller.
302
313
 
data/FAQ.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## Why should I use workhorse over *X*?
4
4
 
5
- There exist a variety of job backends for ruby,
5
+ There are a variety of job backends for Ruby,
6
6
  [delayed_job](https://github.com/collectiveidea/delayed_job) probably being the
7
7
  closest one to workhorse.
8
8
 
9
9
  Some key advantages we feel workhorse has over other Gems:
10
10
 
11
- - Workhorse is less than 500 lines of code at its core. The code is supposed to
11
+ - Workhorse is less than 500 lines of code at its core. The code is designed to
12
12
  be easily readable, understandable, and modifiable.
13
13
 
14
14
  - Workhorse allows you to run multiple jobs simultaneously *in the same
@@ -20,7 +20,7 @@ figure out which one best suits your needs.
20
20
 
21
21
  ## My code is not thread safe. How can I use workhorse safely?
22
22
 
23
- Job code that is not thread safe can not be executed safely in multiple threads
23
+ Job code that is not thread safe cannot be executed safely in multiple threads
24
24
  of the same process. In these cases, set the `pool_size` to `1` and, if you
25
25
  still want to execute multiple jobs simultaneously, the daemon `count` to a
26
26
  number greater than `1`:
@@ -33,10 +33,10 @@ end
33
33
 
34
34
  ## I'm using jRuby. How can I use the daemon handler?
35
35
 
36
- As java processes in general can not be forked safely, the daemon handler
36
+ As Java processes in general cannot be forked safely, the daemon handler
37
37
  provided with this Gem does not support jRuby platforms.
38
38
 
39
- If your jRuby application consists of one single application process, it is
39
+ If your jRuby application consists of a single application process, it is
40
40
  recommended to just start the job backend in the same process:
41
41
 
42
42
  ```ruby
@@ -48,7 +48,7 @@ This code is non-blocking, which means that it will run as long as the process
48
48
  is up. Make sure to trap `INT` and `TERM` and call `worker.shutdown` when the
49
49
  process stops.
50
50
 
51
- If you have multiple application processes though, you may want to start the
51
+ If you have multiple application processes, however, you may want to start the
52
52
  worker in a separate process. For this purpose, adapt the startup script
53
53
  `bin/workhorse.rb` so that it is blocking:
54
54
 
@@ -56,9 +56,9 @@ worker in a separate process. For this purpose, adapt the startup script
56
56
  Workhorse::Worker.start_and_wait(pool_size: 5)
57
57
  ```
58
58
 
59
- This can then be started and "daemonized" using standard linux tools.
59
+ This can then be started and "daemonized" using standard Linux tools.
60
60
 
61
- ## I'm getting random autoloading exeptions
61
+ ## I'm getting random autoloading exceptions
62
62
 
63
63
  Make sure to always start the worker in *production mode*, i.e.:
64
64
 
@@ -69,7 +69,7 @@ RAILS_ENV=production bin/workhorse.rb start
69
69
  ## I'm getting "No live threads left. Deadlock?" exceptions
70
70
 
71
71
  Make sure the Worker is logging somewhere and check the logs. Typically there is
72
- an underlying error that leads to the exception, e.g. missing migration in
72
+ an underlying error that leads to the exception, e.g., a missing migration in
73
73
  production mode.
74
74
 
75
75
  ## Why does workhorse not support timeouts?
@@ -77,5 +77,5 @@ production mode.
77
77
  Generic timeout implementations are [a dangerous
78
78
  thing](http://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/)
79
79
  in Ruby. This is why we decided against providing this feature in Workhorse and
80
- recommend to implement the timeouts inside of your jobs - i.e. via network
80
+ recommend to implement timeouts inside of your jobs - i.e. via network
81
81
  timeouts.
data/Gemfile CHANGED
@@ -3,12 +3,12 @@ source 'https://rubygems.org'
3
3
  # Specify gem dependencies in the .gemspec file
4
4
  gemspec
5
5
 
6
+ gem 'activejob', '~> 7.1.3'
7
+ gem 'activerecord', '~> 7.1.3'
8
+ gem 'benchmark-ips'
6
9
  gem 'bundler'
7
- gem 'rake'
8
- gem 'rubocop', '~> 1.28.0' # Latest version supported with Ruby 2.5
9
10
  gem 'minitest'
10
11
  gem 'mysql2'
11
- gem 'benchmark-ips'
12
- gem 'activejob', '~> 7.1.3'
13
- gem 'activerecord', '~> 7.1.3'
14
12
  gem 'pry'
13
+ gem 'rake'
14
+ gem 'rubocop', '~> 1.28.0' # Latest version supported with Ruby 2.5
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2017 - 2024 Sitrox
3
+ Copyright (c) 2017 - 2025 Sitrox
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # Workhorse
5
5
 
6
- Multi-threaded job backend with database queuing for ruby. Battle-tested and ready for production-use.
6
+ Multi-threaded job backend with database queuing for Ruby. Battle-tested and ready for production-use.
7
7
 
8
8
  ## Introduction
9
9
 
@@ -12,7 +12,7 @@ How it works:
12
12
  * Jobs are instances of classes that support the `perform` method.
13
13
  * Jobs are persisted in the database using ActiveRecord.
14
14
  * Each job has a priority, the default being 0. Jobs with higher priorities
15
- (lower is higher, 0 the highest) get processed first.
15
+ (lower numbers have higher priority, with 0 being the highest) get processed first.
16
16
  * Each job can be set to execute after a certain date / time.
17
17
  * You can start one or more worker processes.
18
18
  * Each worker is configurable as to which queue(s) it processes. Jobs in the
@@ -34,12 +34,12 @@ What it does not do:
34
34
  ### Requirements
35
35
 
36
36
  * Ruby `>= 3.0` (may work with earlier versions but is untested)
37
- * Rails `>= 3.2`
37
+ * Rails `>= 7.0`
38
38
  * A database and table handler that properly supports row-level locking (such as
39
39
  MySQL with InnoDB, PostgreSQL, or Oracle).
40
40
  * If you are planning on using the daemons handler:
41
41
  * An operating system and file system that supports file locking.
42
- * MRI ruby (aka "CRuby") as jRuby does not support `fork`. See the
42
+ * MRI Ruby (aka "CRuby") as jRuby does not support `fork`. See the
43
43
  [FAQ](FAQ.md#im-using-jruby-how-can-i-use-the-daemon-handler) for possible workarounds.
44
44
 
45
45
  ### Installing under Rails
@@ -81,8 +81,8 @@ GRANT execute ON DBMS_LOCK TO <schema-name>;
81
81
  ### Basic jobs
82
82
 
83
83
  Workhorse can handle any jobs that support the `perform` method and are
84
- serializable. To queue a basic job, use the static method `Workhorse.enqueue`.
85
- You can optionally pass a queue name, a priority and a description (as a string).
84
+ serializable. To queue a basic job, use `Workhorse.enqueue`.
85
+ You can optionally pass a queue name, a priority, and a description.
86
86
 
87
87
  ```ruby
88
88
  class MyJob
@@ -108,15 +108,15 @@ method `Workhorse.enqueue_op`:
108
108
  Workhorse.enqueue_op Operations::Jobs::CleanUpDatabase, { queue: :maintenance, priority: 2 }, quiet: true
109
109
  ```
110
110
 
111
- The first argument of the method is the Operation you want to run. Params passed in
112
- using the second argument will be used by Workhorse and params passed using the
111
+ The first argument of the method is the Operation you want to run. Parameters passed in
112
+ using the second argument will be used by Workhorse and parameters passed using the
113
113
  third argument will be used for operation instantiation at job execution, i.e.:
114
114
 
115
115
  ```ruby
116
116
  Workhorse.enqueue_op <Operation Class Name>, { <Workhorse Options> }, { <RailsOps Options> }
117
117
  ```
118
118
 
119
- If you do not want to pass any params to the operation, just omit the third hash:
119
+ If you do not want to pass any parameters to the operation, just omit the third hash:
120
120
 
121
121
  ```ruby
122
122
  Workhorse.enqueue_op Operations::Jobs::CleanUpDatabase, queue: :maintenance, priority: 2
@@ -138,9 +138,9 @@ achieving regular execution:
138
138
  by scheduling the job before knowing whether the current run will succeed.
139
139
  Proceed down this path at your own peril!)
140
140
 
141
- *Example:* A job that takes 5 seconds to run and is set to reschedule itself
142
- after 10 minutes is started at 12:00 sharp. After one hour it will be set to
143
- execute at 13:00:30 at the earliest.
141
+ *Example:* A job that takes 5 seconds to run and reschedules itself every
142
+ 10 minutes. If started at 12:00 sharp, after one hour it will execute at
143
+ 13:00:30 at the earliest due to cumulative execution time.
144
144
 
145
145
  In its most basic form, the `perform` method of a job would look as follows:
146
146
 
@@ -198,7 +198,7 @@ achieving regular execution:
198
198
  of which is that only one 'worker' should be started by the ShellHandler.
199
199
  Otherwise there would be multiple jobs scheduled at the same time.
200
200
 
201
- Please refer to the documentation on
201
+ Please refer to the documentation for
202
202
  [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) (or the
203
203
  scheduler of your choice) for further options concerning the timing of the
204
204
  jobs.
@@ -227,8 +227,7 @@ Workhorse::Worker.start_and_wait(
227
227
 
228
228
  See [code documentation](http://www.rubydoc.info/github/sitrox/workhorse/Workhorse%2FWorker:initialize)
229
229
  for more information on the arguments. All arguments passed to `start_and_wait`
230
- are passed to the initialize. All arguments passed to `start_and_wait` are
231
- in turn passed to the initializer of `Workhorse::Worker`.
230
+ are passed to the initializer of `Workhorse::Worker`.
232
231
 
233
232
  ### Start workers using a daemon script
234
233
 
@@ -264,7 +263,7 @@ end
264
263
 
265
264
  ### Instant repolling
266
265
 
267
- Per default, each worker only polls in the given interval. This means that if
266
+ By default, each worker only polls in the given interval. This means that if
268
267
  you schedule, for example, 50 jobs at once and have a polling interval of 1
269
268
  minute with a queue size of 1, the poller would tackle the first job and then
270
269
  wait for a whole minute until the next poll. This would mean that these 50 jobs
@@ -287,8 +286,8 @@ transaction is created.
287
286
 
288
287
  ### Transaction callback
289
288
 
290
- By default, transactions are created using `ActiveRecord::Base.transaction { ...
291
- }`. You can customize this using the setting `config.tx_callback` in your
289
+ By default, transactions are created using `ActiveRecord::Base.transaction`.
290
+ You can customize this using the setting `config.tx_callback` in your
292
291
  `config/initializers/workhorse.rb` (see commented out section in the generated
293
292
  configuration file).
294
293
 
@@ -342,7 +341,7 @@ You can turn off transaction wrapping in the following ways:
342
341
  end
343
342
  ```
344
343
 
345
- For plain Workhrose job clases:
344
+ For plain Workhorse job classes:
346
345
 
347
346
  1. Add the following static method to your job class:
348
347
 
@@ -352,13 +351,14 @@ You can turn off transaction wrapping in the following ways:
352
351
  true
353
352
  end
354
353
  end
354
+ ```
355
355
 
356
356
  ## Exception handling
357
357
 
358
- Per default, exceptions occurring in a worker thread will only be visible in the
358
+ By default, exceptions occurring in a worker thread will only be visible in the
359
359
  respective log file, usually `production.log`. If you'd like to perform specific
360
360
  actions when an exception arises, set the global option `on_exception` to a
361
- callback of your linking, e.g.:
361
+ callback of your liking, e.g.:
362
362
 
363
363
  ```ruby
364
364
  # config/initializers/workhorse.rb
@@ -467,7 +467,7 @@ succeeded jobs. You can run this using your scheduler in a specific interval.
467
467
  ## Memory handling
468
468
 
469
469
  When a worker exceeds the memory limit specified by
470
- `config.max_worker_memory_mb` (assuming it is configured and > 0), it initiates
470
+ `config.max_worker_memory_mb` (assuming it is configured to a value greater than 0), it initiates
471
471
  a graceful shutdown process by creating a shutdown file named
472
472
  `tmp/pids/workhorse.<pid>.shutdown`.
473
473
 
@@ -564,4 +564,4 @@ Please consult the [FAQ](FAQ.md).
564
564
 
565
565
  ## Copyright
566
566
 
567
- Copyright © 2017 - 2024 Sitrox. See `LICENSE` for further details.
567
+ Copyright © 2017 - 2026 Sitrox. See `LICENSE` for further details.
data/Rakefile CHANGED
@@ -11,8 +11,8 @@ task :gemspec do
11
11
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
12
12
  spec.require_paths = ['lib']
13
13
 
14
- spec.add_dependency 'activesupport'
15
- spec.add_dependency 'activerecord'
14
+ spec.add_dependency 'activesupport', '>= 7.0.0'
15
+ spec.add_dependency 'activerecord', '>= 7.0.0'
16
16
  spec.add_dependency 'concurrent-ruby'
17
17
  end
18
18
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0.rc2
1
+ 1.3.0.rc4
@@ -1,28 +1,44 @@
1
1
  module ActiveJob
2
2
  module QueueAdapters
3
- # == Workhorse adapter for Active Job
3
+ # Workhorse adapter for ActiveJob.
4
4
  #
5
- # Workhorse is a multi-threaded job backend with database queuing for ruby.
6
- # Jobs are persisted in the database using ActiveRecird.
7
- # Read more about Workhorse {here}[https://github.com/sitrox/activejob].
5
+ # Workhorse is a multi-threaded job backend with database queuing for Ruby.
6
+ # Jobs are persisted in the database using ActiveRecord.
7
+ # Read more about Workhorse {here}[https://github.com/sitrox/workhorse].
8
8
  #
9
9
  # To use Workhorse, set the queue_adapter config to +:workhorse+.
10
10
  #
11
11
  # Rails.application.config.active_job.queue_adapter = :workhorse
12
+ #
13
+ # @example Configuration
14
+ # Rails.application.config.active_job.queue_adapter = :workhorse
12
15
  class WorkhorseAdapter
13
16
  # Defines whether enqueuing should happen implicitly to after commit when called
14
17
  # from inside a transaction. Most adapters should return true, but some adapters
15
18
  # that use the same database as Active Record and are transaction aware can return
16
19
  # false to continue enqueuing jobs as part of the transaction.
20
+ #
21
+ # @return [Boolean] False because Workhorse is transaction-aware
17
22
  def enqueue_after_transaction_commit?
18
23
  false
19
24
  end
20
25
 
21
- def enqueue(job) # :nodoc:
26
+ # Enqueues a job for immediate execution.
27
+ #
28
+ # @param job [ActiveJob::Base] The job to enqueue
29
+ # @return [Workhorse::DbJob] The created database job record
30
+ # @api private
31
+ def enqueue(job)
22
32
  Workhorse.enqueue_active_job(job)
23
33
  end
24
34
 
25
- def enqueue_at(job, timestamp = Time.now) # :nodoc:
35
+ # Enqueues a job for execution at a specific time.
36
+ #
37
+ # @param job [ActiveJob::Base] The job to enqueue
38
+ # @param timestamp [Time] When to execute the job
39
+ # @return [Workhorse::DbJob] The created database job record
40
+ # @api private
41
+ def enqueue_at(job, timestamp = Time.now)
26
42
  Workhorse.enqueue_active_job(job, perform_at: timestamp)
27
43
  end
28
44
  end
@@ -1,4 +1,5 @@
1
1
  module Workhorse
2
+ # Extension module for ActiveJob integration.
2
3
  module ActiveJobExtension
3
4
  extend ActiveSupport::Concern
4
5
 
@@ -8,10 +9,18 @@ module Workhorse
8
9
  end
9
10
 
10
11
  module ClassMethods
12
+ # Marks this job class to skip database transactions during execution.
13
+ # Use this for jobs that manage their own transactions or have long-running
14
+ # operations that should not be wrapped in a transaction.
15
+ #
16
+ # @return [void]
11
17
  def skip_tx
12
18
  self._skip_tx = true
13
19
  end
14
20
 
21
+ # Checks if this job class should skip database transactions.
22
+ #
23
+ # @return [Boolean] True if transactions should be skipped
15
24
  def skip_tx?
16
25
  _skip_tx
17
26
  end
@@ -1,11 +1,28 @@
1
1
  module Workhorse
2
+ # Daemon class for managing multiple worker processes.
3
+ # Provides functionality to start, stop, restart, and monitor worker processes
4
+ # through a simple Ruby DSL.
2
5
  class Daemon
6
+ # Internal representation of a worker process.
7
+ # Stores worker metadata and the block to execute.
3
8
  class Worker
9
+ # @return [Integer] The worker's unique ID
4
10
  attr_reader :id
11
+
12
+ # @return [String] The worker's display name
5
13
  attr_reader :name
14
+
15
+ # @return [Proc] The block containing the worker's logic
6
16
  attr_reader :block
17
+
18
+ # @return [Integer, nil] The worker's process ID when running
7
19
  attr_accessor :pid
8
20
 
21
+ # Creates a new worker definition.
22
+ #
23
+ # @param id [Integer] Unique identifier for this worker
24
+ # @param name [String] Display name for this worker
25
+ # @param block [Proc] Code block to execute in the worker process
9
26
  def initialize(id, name, &block)
10
27
  @id = id
11
28
  @name = name
@@ -13,9 +30,16 @@ module Workhorse
13
30
  end
14
31
  end
15
32
 
33
+ # @return [Array<Worker>] Array of defined workers
16
34
  # @private
17
35
  attr_reader :workers
18
36
 
37
+ # Creates a new daemon instance.
38
+ #
39
+ # @param pidfile [String, nil] Path template for PID files (use %i placeholder for worker ID)
40
+ # @param quiet [Boolean] Whether to suppress output during operations
41
+ # @yield [ScopedEnv] Configuration block for defining workers
42
+ # @raise [RuntimeError] If no workers are defined or pidfile format is invalid
19
43
  def initialize(pidfile: nil, quiet: false, &_block)
20
44
  @pidfile = pidfile
21
45
  @quiet = quiet
@@ -38,10 +62,19 @@ module Workhorse
38
62
  end
39
63
  end
40
64
 
65
+ # Defines a worker process.
66
+ #
67
+ # @param name [String] Display name for the worker
68
+ # @yield Block containing the worker's execution logic
69
+ # @return [void]
41
70
  def worker(name = 'Job Worker', &block)
42
71
  @workers << Worker.new(@workers.size + 1, name, &block)
43
72
  end
44
73
 
74
+ # Starts all defined workers.
75
+ #
76
+ # @param quiet [Boolean] Whether to suppress status output
77
+ # @return [Integer] Exit code (0 = success, 2 = some workers already running)
45
78
  def start(quiet: false)
46
79
  code = 0
47
80
 
@@ -81,6 +114,11 @@ module Workhorse
81
114
  return code
82
115
  end
83
116
 
117
+ # Stops all running workers.
118
+ #
119
+ # @param kill [Boolean] Whether to use KILL signal instead of TERM/INT
120
+ # @param quiet [Boolean] Whether to suppress status output
121
+ # @return [Integer] Exit code (0 = success, 2 = some workers already stopped)
84
122
  def stop(kill = false, quiet: false)
85
123
  code = 0
86
124
 
@@ -102,6 +140,10 @@ module Workhorse
102
140
  return code
103
141
  end
104
142
 
143
+ # Checks the status of all workers.
144
+ #
145
+ # @param quiet [Boolean] Whether to suppress status output
146
+ # @return [Integer] Exit code (0 = all running, 2 = some not running)
105
147
  def status(quiet: false)
106
148
  code = 0
107
149
 
@@ -122,6 +164,10 @@ module Workhorse
122
164
  return code
123
165
  end
124
166
 
167
+ # Watches workers and starts them if they're not running.
168
+ # In Rails environments, respects the tmp/stop.txt file.
169
+ #
170
+ # @return [Integer] Exit code from start operation or 0 if no action needed
125
171
  def watch
126
172
  if defined?(Rails)
127
173
  should_be_running = !File.exist?(Rails.root.join('tmp/stop.txt'))
@@ -136,11 +182,18 @@ module Workhorse
136
182
  end
137
183
  end
138
184
 
185
+ # Restarts all workers by stopping and then starting them.
186
+ #
187
+ # @return [Integer] Exit code from start operation
139
188
  def restart
140
189
  stop
141
190
  return start
142
191
  end
143
192
 
193
+ # Sends HUP signal to all workers to restart their logging.
194
+ # Useful for log rotation without full process restart.
195
+ #
196
+ # @return [Integer] Exit code (0 = success, 2 = some signals failed)
144
197
  def restart_logging
145
198
  code = 0
146
199
 
@@ -163,10 +216,20 @@ module Workhorse
163
216
 
164
217
  private
165
218
 
219
+ # Executes the given block for each defined worker.
220
+ #
221
+ # @yield [Worker] Each worker instance
222
+ # @return [void]
223
+ # @private
166
224
  def for_each_worker(&block)
167
225
  @workers.each(&block)
168
226
  end
169
227
 
228
+ # Starts a single worker process.
229
+ #
230
+ # @param worker [Worker] The worker to start
231
+ # @return [void]
232
+ # @private
170
233
  def start_worker(worker)
171
234
  check_rails_env if defined?(Rails)
172
235
 
@@ -185,6 +248,13 @@ module Workhorse
185
248
  Process.detach(pid)
186
249
  end
187
250
 
251
+ # Stops a single worker process.
252
+ #
253
+ # @param pid_file [String] Path to the worker's PID file
254
+ # @param pid [Integer] The worker's process ID
255
+ # @param kill [Boolean] Whether to use KILL signal
256
+ # @return [void]
257
+ # @private
188
258
  def stop_worker(pid_file, pid, kill: false)
189
259
  signals = kill ? %w[KILL] : %w[TERM INT]
190
260
 
@@ -201,10 +271,20 @@ module Workhorse
201
271
  File.delete(pid_file)
202
272
  end
203
273
 
274
+ # Sends HUP signal to a worker process.
275
+ #
276
+ # @param pid [Integer] The worker's process ID
277
+ # @return [void]
278
+ # @private
204
279
  def hup_worker(pid)
205
280
  Process.kill('HUP', pid)
206
281
  end
207
282
 
283
+ # Generates a process name for a worker.
284
+ #
285
+ # @param worker [Worker] The worker instance
286
+ # @return [String] Process name for ps output
287
+ # @private
208
288
  def process_name(worker)
209
289
  if defined?(Rails)
210
290
  path = Rails.root
@@ -215,6 +295,11 @@ module Workhorse
215
295
  return "Workhorse #{worker.name} ##{worker.id}: #{path}"
216
296
  end
217
297
 
298
+ # Checks if a process with the given PID is running.
299
+ #
300
+ # @param pid [Integer] Process ID to check
301
+ # @return [Boolean] True if process is running
302
+ # @private
218
303
  def process?(pid)
219
304
  return begin
220
305
  Process.kill(0, pid)
@@ -224,10 +309,20 @@ module Workhorse
224
309
  end
225
310
  end
226
311
 
312
+ # Returns the PID file path for a worker.
313
+ #
314
+ # @param worker [Worker] The worker instance
315
+ # @return [String] Path to the PID file
316
+ # @private
227
317
  def pid_file_for(worker)
228
318
  @pidfile % worker.id
229
319
  end
230
320
 
321
+ # Reads PID information for a worker.
322
+ #
323
+ # @param worker [Worker] The worker instance
324
+ # @return [Array<String, Integer, Boolean>] PID file path, PID, and active status
325
+ # @private
231
326
  def read_pid(worker)
232
327
  file = pid_file_for(worker)
233
328
  pid = nil
@@ -247,6 +342,10 @@ module Workhorse
247
342
  return file, pid, active
248
343
  end
249
344
 
345
+ # Warns if not running in production environment.
346
+ #
347
+ # @return [void]
348
+ # @private
250
349
  def check_rails_env
251
350
  unless Rails.env.production?
252
351
  warn 'WARNING: Always run workhorse workers in production environment. Other environments can lead to unexpected behavior.'