workhorse 2.0.0.rc1 → 2.0.1
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 +4 -4
- data/CHANGELOG.md +43 -52
- data/VERSION +1 -1
- data/lib/workhorse/performer.rb +7 -0
- data/test/workhorse/performer_test.rb +15 -0
- data/workhorse.gemspec +3 -3
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 617b6616890ea3437b817a10d0c1f16438dae9c221a2bc53203793a92417a1ea
|
|
4
|
+
data.tar.gz: 75e040bf7b18a294f435d263087ece37cf9245d2486709681efbdada1c47a76e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f31d1f201019cda0ada3c1b0757c9e447d66f0db8b90e63bb8337d8b7e5414ef5d3ccbdd0ad081cd7a09145e9607c336cf51f9e946b7d606f0dfea08e559f1e
|
|
7
|
+
data.tar.gz: b306401d8a79d1481c595c924126b3cbe2e537691efefc0a69b7e16cc4eed2585fb36e267ab792d5ebadddede47ce0dd04c47c7be62312f9b968f55d183c2130
|
data/CHANGELOG.md
CHANGED
|
@@ -1,65 +1,22 @@
|
|
|
1
1
|
# Workhorse Changelog
|
|
2
2
|
|
|
3
|
-
## 2.0.
|
|
4
|
-
|
|
5
|
-
Sitrox reference: #154443.
|
|
3
|
+
## 2.0.1 - 2026-10-28
|
|
6
4
|
|
|
7
5
|
### Added
|
|
8
6
|
|
|
9
|
-
*
|
|
10
|
-
`
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
characters it allows before 12.2. See
|
|
14
|
-
[Database support](README.md#database-support).
|
|
7
|
+
* `Workhorse::Performer#db_job`: inside a job, `Workhorse.performer.db_job`
|
|
8
|
+
returns the job's own `Workhorse::DbJob`. This lets a job link something to
|
|
9
|
+
its record that outlives the job, for example a progress display that has to
|
|
10
|
+
end when the worker cleans up after a killed worker.
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
move straight to this one. Grant the schema execute permission on
|
|
18
|
-
`DBMS_LOCK`, as described under Database support.
|
|
19
|
-
|
|
20
|
-
### Fixed
|
|
21
|
-
|
|
22
|
-
* On Oracle, jobs are picked up in priority order. 1.x limited the rows it
|
|
23
|
-
selected by filtering on `ROWNUM`, which Oracle numbers before sorting, so
|
|
24
|
-
both the job taken from each queue and the jobs a poll picked up were an
|
|
25
|
-
arbitrary subset regardless of `priority`. Limiting now uses
|
|
26
|
-
`FETCH FIRST … ROWS ONLY`, which applies after the sort and is why 12c is
|
|
27
|
-
now the minimum.
|
|
28
|
-
|
|
29
|
-
* A poll that finds the global lock taken now waits for it with a polling
|
|
30
|
-
interval below half a second. The lock timeout follows the interval, and
|
|
31
|
-
MySQL and Oracle take it as whole seconds, so they rounded it down to not
|
|
32
|
-
waiting at all - only MariaDB honours a fraction. Each such poll gave up
|
|
33
|
-
at once and counted towards `Workhorse.max_global_lock_fails`, so several
|
|
34
|
-
workers contending for the lock could trigger its alarm about a crashed
|
|
35
|
-
worker. The timeout is now rounded up to whole seconds.
|
|
36
|
-
|
|
37
|
-
* A soft restart (`USR1`) no longer wedges a worker whose `tmp/pids` does not
|
|
38
|
-
exist yet, as on a fresh checkout or deployment. The worker touched its
|
|
39
|
-
shutdown file there after it had stopped accepting jobs, so failing on it
|
|
40
|
-
left a worker that took no jobs and never exited. The directory is now
|
|
41
|
-
created when missing, and a shutdown file that still cannot be written no
|
|
42
|
-
longer holds up the shutdown. The heartbeat and the memory-limit shutdown,
|
|
43
|
-
which write to the same directory, create it as well.
|
|
44
|
-
|
|
45
|
-
* `Workhorse.clean_stuck_jobs` works on Oracle. It is built on splitting
|
|
46
|
-
`locked_by` into host, PID and random component, which was written with
|
|
47
|
-
MySQL's `substring_index` and so raised on Oracle for as long as the option
|
|
48
|
-
has existed — visible only to an installation that turned it on, as it is
|
|
49
|
-
off by default.
|
|
50
|
-
|
|
51
|
-
## 2.0.0.rc0 - 2026-09-29
|
|
12
|
+
## 2.0.0 - 2026-10-14
|
|
52
13
|
|
|
53
14
|
Sitrox reference: #154443.
|
|
54
15
|
|
|
55
16
|
### Breaking changes
|
|
56
17
|
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
generated migrations are gone, along with
|
|
60
|
-
`Workhorse::Poller::ORACLE_LOCK_MODE` and `ORACLE_LOCK_HANDLE`. It was never
|
|
61
|
-
covered by CI, so it only ever had manual verification. An Oracle
|
|
62
|
-
installation has no upgrade path and should stay on 1.x. See
|
|
18
|
+
* Oracle 12c is the minimum, as job selection now limits its rows with
|
|
19
|
+
`FETCH FIRST … ROWS ONLY` (see the priority fix below). See
|
|
63
20
|
[Database support](README.md#database-support).
|
|
64
21
|
|
|
65
22
|
* A forked daemon worker no longer runs the `at_exit` handlers registered by
|
|
@@ -108,7 +65,9 @@ Sitrox reference: #154443.
|
|
|
108
65
|
* The composite indexes `[state, perform_at]`, `[state, priority, created_at]`
|
|
109
66
|
and `[state, expires_at]` in the generated `jobs` migration, replacing the
|
|
110
67
|
single-column index on `state`. `rails generate workhorse:install` now emits
|
|
111
|
-
two migrations rather than one.
|
|
68
|
+
two migrations rather than one. On Oracle, the generated migrations leave
|
|
69
|
+
out the index prefix lengths Oracle rejects and name every index within the
|
|
70
|
+
30 characters it allows before 12.2.
|
|
112
71
|
|
|
113
72
|
* `fugit` as a runtime dependency, for parsing cron expressions.
|
|
114
73
|
|
|
@@ -127,8 +86,40 @@ Sitrox reference: #154443.
|
|
|
127
86
|
|
|
128
87
|
* `Workhorse::DbJob#reset!` accepts `expired` as the terminal state it is.
|
|
129
88
|
|
|
89
|
+
* Oracle is covered by CI against `activerecord-oracle_enhanced-adapter`
|
|
90
|
+
rather than only by hand.
|
|
91
|
+
|
|
130
92
|
### Fixed
|
|
131
93
|
|
|
94
|
+
* On Oracle, jobs are picked up in priority order. 1.x limited the rows it
|
|
95
|
+
selected by filtering on `ROWNUM`, which Oracle numbers before sorting, so
|
|
96
|
+
both the job taken from each queue and the jobs a poll picked up were an
|
|
97
|
+
arbitrary subset regardless of `priority`. Limiting now uses
|
|
98
|
+
`FETCH FIRST … ROWS ONLY`, which applies after the sort and is why 12c is
|
|
99
|
+
now the minimum.
|
|
100
|
+
|
|
101
|
+
* A poll that finds the global lock taken now waits for it with a polling
|
|
102
|
+
interval below half a second. The lock timeout follows the interval, and
|
|
103
|
+
MySQL and Oracle take it as whole seconds, so they rounded it down to not
|
|
104
|
+
waiting at all - only MariaDB honours a fraction. Each such poll gave up
|
|
105
|
+
at once and counted towards `Workhorse.max_global_lock_fails`, so several
|
|
106
|
+
workers contending for the lock could trigger its alarm about a crashed
|
|
107
|
+
worker. The timeout is now rounded up to whole seconds.
|
|
108
|
+
|
|
109
|
+
* A soft restart (`USR1`) no longer wedges a worker whose `tmp/pids` does not
|
|
110
|
+
exist yet, as on a fresh checkout or deployment. The worker touched its
|
|
111
|
+
shutdown file there after it had stopped accepting jobs, so failing on it
|
|
112
|
+
left a worker that took no jobs and never exited. The directory is now
|
|
113
|
+
created when missing, and a shutdown file that still cannot be written no
|
|
114
|
+
longer holds up the shutdown. The heartbeat and the memory-limit shutdown,
|
|
115
|
+
which write to the same directory, create it as well.
|
|
116
|
+
|
|
117
|
+
* `Workhorse.clean_stuck_jobs` works on Oracle. It is built on splitting
|
|
118
|
+
`locked_by` into host, PID and random component, which was written with
|
|
119
|
+
MySQL's `substring_index` and so raised on Oracle for as long as the option
|
|
120
|
+
has existed — visible only to an installation that turned it on, as it is
|
|
121
|
+
off by default.
|
|
122
|
+
|
|
132
123
|
* A deadlock between shutting a worker down and the poller posting a job.
|
|
133
124
|
`Worker#shutdown` held the worker's mutex while waiting for the poller
|
|
134
125
|
thread, which could be waiting for that same mutex in `Worker#perform`. The
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.0.
|
|
1
|
+
2.0.1
|
data/lib/workhorse/performer.rb
CHANGED
|
@@ -10,6 +10,13 @@ module Workhorse
|
|
|
10
10
|
# @return [Workhorse::Worker] The worker that owns this performer
|
|
11
11
|
attr_reader :worker
|
|
12
12
|
|
|
13
|
+
# The job this performer performs. Inside a job, {Workhorse.performer}
|
|
14
|
+
# gives a job its own database record, e.g. to link something to it that
|
|
15
|
+
# outlives the job.
|
|
16
|
+
#
|
|
17
|
+
# @return [Workhorse::DbJob]
|
|
18
|
+
attr_reader :db_job
|
|
19
|
+
|
|
13
20
|
# Creates a new performer for a specific job.
|
|
14
21
|
#
|
|
15
22
|
# @param db_job_id [Integer] The ID of the {Workhorse::DbJob} to perform
|
|
@@ -30,6 +30,21 @@ class Workhorse::PerformerTest < WorkhorseTest
|
|
|
30
30
|
work_until(pool_size: 5, polling_interval: 0.2) { assert_equal 'failed', Workhorse::DbJob.first.state }
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
class OwnDbJobJob
|
|
34
|
+
class_attribute :db_job_id
|
|
35
|
+
|
|
36
|
+
def perform
|
|
37
|
+
self.class.db_job_id = Workhorse.performer.db_job.id
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_a_job_finds_its_own_db_job
|
|
42
|
+
db_job = Workhorse.enqueue OwnDbJobJob.new
|
|
43
|
+
work_until(pool_size: 1, polling_interval: 0.2) { assert_equal 'succeeded', db_job.reload.state }
|
|
44
|
+
|
|
45
|
+
assert_equal db_job.id, OwnDbJobJob.db_job_id
|
|
46
|
+
end
|
|
47
|
+
|
|
33
48
|
def test_on_exception
|
|
34
49
|
old_callback = Workhorse.on_exception
|
|
35
50
|
exception = nil
|
data/workhorse.gemspec
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: workhorse 2.0.
|
|
2
|
+
# stub: workhorse 2.0.1 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "workhorse".freeze
|
|
6
|
-
s.version = "2.0.
|
|
6
|
+
s.version = "2.0.1".freeze
|
|
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 = "2026-
|
|
11
|
+
s.date = "2026-10-28"
|
|
12
12
|
s.files = [".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.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/generators/workhorse/templates/create_table_workhorse_schedules.rb".freeze, "lib/workhorse.rb".freeze, "lib/workhorse/active_job_extension.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/detect_late_schedules_job.rb".freeze, "lib/workhorse/jobs/detect_stale_jobs_job.rb".freeze, "lib/workhorse/jobs/run_active_job.rb".freeze, "lib/workhorse/jobs/run_rails_op.rb".freeze, "lib/workhorse/notifiers/base.rb".freeze, "lib/workhorse/notifiers/file_system.rb".freeze, "lib/workhorse/notifiers/none.rb".freeze, "lib/workhorse/notifiers/redis.rb".freeze, "lib/workhorse/performer.rb".freeze, "lib/workhorse/poller.rb".freeze, "lib/workhorse/pool.rb".freeze, "lib/workhorse/schedule.rb".freeze, "lib/workhorse/schedules.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/daemon_test.rb".freeze, "test/workhorse/db_job_test.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/notifier_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/schedule_test.rb".freeze, "test/workhorse/worker_test.rb".freeze, "test/workhorse/yjit_test.rb".freeze, "workhorse.gemspec".freeze]
|
|
13
13
|
s.homepage = "https://github.com/sitrox/workhorse".freeze
|
|
14
14
|
s.licenses = ["MIT".freeze]
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: workhorse
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sitrox
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -143,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
143
143
|
- !ruby/object:Gem::Version
|
|
144
144
|
version: '0'
|
|
145
145
|
requirements: []
|
|
146
|
-
rubygems_version:
|
|
146
|
+
rubygems_version: 3.6.9
|
|
147
147
|
specification_version: 4
|
|
148
148
|
summary: Multi-threaded job backend with database queuing for ruby.
|
|
149
149
|
test_files:
|