zizq 0.3.0 → 0.3.2
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/README.md +165 -37
- data/bin/zizq-worker +108 -32
- data/lib/zizq/client.rb +29 -3
- data/lib/zizq/configuration.rb +103 -18
- data/lib/zizq/tls_configuration.rb +27 -0
- data/lib/zizq/version.rb +1 -1
- data/lib/zizq/worker.rb +39 -39
- data/lib/zizq/worker_configuration.rb +48 -0
- data/lib/zizq.rb +5 -1
- data/sig/generated/zizq/client.rbs +9 -1
- data/sig/generated/zizq/configuration.rbs +54 -11
- data/sig/generated/zizq/tls_configuration.rbs +27 -0
- data/sig/generated/zizq/worker.rbs +16 -16
- data/sig/generated/zizq/worker_configuration.rbs +52 -0
- data/sig/zizq.rbs +2 -1
- metadata +23 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f0af940d511a8e1aa6d89df8ca13e98733430827d5f3202bcf7434a94ee02da
|
|
4
|
+
data.tar.gz: 9341d2815e344b3e4b8ca402185e3de01bd488cb5a3159f18e132d3c65cc18ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3e04dffd06a36db8ca940105675428390867bd8dee62b2bcf0ef7cbeef03fe080b35bf93061e9ec00b6834be0940e1d69d7bd837178718a0bd288b6895464c3
|
|
7
|
+
data.tar.gz: 9d5a7d77ca3e143dbcc8c5bb72a58eb1c60092335d206579646dac8df3cbf178a9627a0d8e7a68e394d2b07c32a60a82c915ab049175624760a314042d12f9b7
|
data/README.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Zizq — Official Ruby Client
|
|
2
2
|
|
|
3
|
+
This is the official Zizq client library for Ruby.
|
|
4
|
+
|
|
3
5
|
Zizq is a simple, zero dependency, single binary job queue system that is both
|
|
4
6
|
fast and durable. It is designed to work in any stack through a simple HTTP
|
|
5
7
|
API.
|
|
6
8
|
|
|
7
|
-
This is the official Zizq client library for Ruby.
|
|
8
|
-
|
|
9
9
|
[](https://github.com/zizq-labs/zizq-ruby/actions/workflows/ci.yml)
|
|
10
|
+
[](https://rubygems.org/gems/zizq)
|
|
10
11
|
|
|
11
12
|
## Features
|
|
12
13
|
|
|
@@ -28,26 +29,63 @@ This is the official Zizq client library for Ruby.
|
|
|
28
29
|
> If you have not yet installed the Zizq server, follow the
|
|
29
30
|
> [Getting Started](https://zizq.io/docs/getting-started) guide first.
|
|
30
31
|
|
|
31
|
-
Add it to your application's `Gemfile
|
|
32
|
+
Add it to your application's `Gemfile`:
|
|
32
33
|
|
|
33
|
-
```
|
|
34
|
-
gem 'zizq', '~> 0.3.
|
|
34
|
+
```ruby
|
|
35
|
+
gem 'zizq', '~> 0.3.2'
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
Or install it manually:
|
|
38
39
|
|
|
39
40
|
```shell
|
|
40
|
-
$ gem install zizq -v 0.3.
|
|
41
|
+
$ gem install zizq -v 0.3.2
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Ruby **3.2.8 or newer** is required. Client and server share version
|
|
45
|
+
numbers — keep the client's major/minor at or below the server's.
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Out of the box, the client talks to a server at `http://localhost:7890` —
|
|
50
|
+
fine for local development. For anything else, configure it with
|
|
51
|
+
`Zizq.configure` in your application's bootstrap (e.g. a Rails initializer):
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
require 'zizq'
|
|
55
|
+
|
|
56
|
+
Zizq.configure do |c|
|
|
57
|
+
c.url = 'https://zizq.your.network:7890'
|
|
58
|
+
c.logger = Logger.new('log/zizq.log')
|
|
59
|
+
|
|
60
|
+
c.tls.ca = '/path/to/server-ca-cert.pem'
|
|
61
|
+
|
|
62
|
+
# Optional worker defaults — applied to every Zizq::Worker
|
|
63
|
+
# instance and to runs of the `zizq-worker` executable. Explicit
|
|
64
|
+
# kwargs or CLI flags override these.
|
|
65
|
+
c.worker.queues = ['emails', 'payments']
|
|
66
|
+
c.worker.fiber_count = 25
|
|
67
|
+
end
|
|
41
68
|
```
|
|
42
69
|
|
|
43
|
-
|
|
70
|
+
For mutual TLS, also set `c.tls.client_cert` and `c.tls.client_key`.
|
|
71
|
+
|
|
72
|
+
> [!CAUTION]
|
|
73
|
+
> If your server is exposed directly to the internet, it should require
|
|
74
|
+
> mutual TLS — otherwise anybody can talk to it.
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
44
77
|
|
|
45
78
|
> [!TIP]
|
|
46
|
-
>
|
|
47
|
-
>
|
|
48
|
-
>
|
|
79
|
+
> This README is an overview. The
|
|
80
|
+
> [full documentation](https://zizq.io/docs/clients/ruby/) covers each
|
|
81
|
+
> feature in depth — middleware, custom dispatchers, Active Job, job
|
|
82
|
+
> querying, and more.
|
|
49
83
|
|
|
50
|
-
|
|
84
|
+
### Defining a job
|
|
85
|
+
|
|
86
|
+
In most Ruby applications, a job is a plain class that includes `Zizq::Job`.
|
|
87
|
+
The class declares its defaults with the `zizq_*` DSL and implements
|
|
88
|
+
`#perform`:
|
|
51
89
|
|
|
52
90
|
```ruby
|
|
53
91
|
class SendEmailJob
|
|
@@ -55,54 +93,140 @@ class SendEmailJob
|
|
|
55
93
|
|
|
56
94
|
zizq_queue 'emails'
|
|
57
95
|
zizq_priority 100
|
|
96
|
+
zizq_retry_limit 5
|
|
58
97
|
|
|
59
98
|
def perform(user_id, template:)
|
|
60
|
-
|
|
99
|
+
user = User.find(user_id)
|
|
100
|
+
Mailer.deliver(user, template)
|
|
61
101
|
end
|
|
62
102
|
end
|
|
63
103
|
```
|
|
64
104
|
|
|
65
|
-
|
|
105
|
+
Every default — `zizq_queue`, `zizq_priority`, `zizq_retry_limit`,
|
|
106
|
+
`zizq_backoff`, `zizq_retention`, `zizq_unique` — can be overridden per
|
|
107
|
+
enqueue. The job's class name (`"SendEmailJob"`) becomes the API-level job
|
|
108
|
+
type, so keep it stable once jobs are in flight.
|
|
109
|
+
|
|
110
|
+
### Enqueuing jobs
|
|
111
|
+
|
|
112
|
+
Enqueue a job by passing the class and the arguments your `#perform` method
|
|
113
|
+
expects:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
job = Zizq.enqueue(SendEmailJob, 42, template: 'welcome')
|
|
117
|
+
job.id # => "03fu0wm75gxgmfyfplwvazhex"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Override defaults for a single call with `Zizq.enqueue_with`, or with a block
|
|
121
|
+
that mutates the request:
|
|
66
122
|
|
|
67
123
|
```ruby
|
|
68
|
-
|
|
124
|
+
# Don't retry this one.
|
|
125
|
+
Zizq.enqueue_with(retry_limit: 0).enqueue(SendEmailJob, 42, template: 'welcome')
|
|
126
|
+
|
|
127
|
+
# Bump the priority via the block form.
|
|
128
|
+
Zizq.enqueue(SendEmailJob, 42, template: 'welcome') do |req|
|
|
129
|
+
req.priority = 1000
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Schedule a job for later with `delay` (seconds from now) or an absolute
|
|
134
|
+
`ready_at`:
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
Zizq.enqueue_with(delay: 3600).enqueue(SendEmailJob, 42, template: 'welcome')
|
|
138
|
+
Zizq.enqueue_with(ready_at: Time.new(2027, 3, 15, 14, 30)).enqueue(SendEmailJob, 42, template: 'welcome')
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
To enqueue many jobs efficiently, `Zizq.enqueue_bulk` sends them in a single
|
|
142
|
+
atomic request — across queues and job types, and `enqueue_raw` enqueues can
|
|
143
|
+
be mixed in too:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
Zizq.enqueue_bulk do |b|
|
|
147
|
+
signups.each { |user_id| b.enqueue(SendEmailJob, user_id, template: 'welcome') }
|
|
148
|
+
end
|
|
69
149
|
```
|
|
70
150
|
|
|
71
151
|
> [!NOTE]
|
|
72
|
-
> Jobs can also be enqueued
|
|
73
|
-
> designed
|
|
152
|
+
> Jobs can also be enqueued without `Zizq::Job` via `Zizq.enqueue_raw` —
|
|
153
|
+
> designed for cross-language workflows where, for example, a Ruby app
|
|
154
|
+
> enqueues jobs consumed by a Go service.
|
|
155
|
+
|
|
156
|
+
### Running a worker
|
|
74
157
|
|
|
75
|
-
|
|
158
|
+
Jobs are processed by a worker, typically in a separate process. The simplest
|
|
159
|
+
way is the `zizq-worker` executable bundled with the gem. **Rails apps need
|
|
160
|
+
no arguments** — `zizq-worker` auto-detects `config/environment.rb` when run
|
|
161
|
+
from the app's root:
|
|
76
162
|
|
|
77
163
|
```shell
|
|
78
|
-
$ zizq-worker
|
|
79
|
-
I, [
|
|
80
|
-
I, [
|
|
81
|
-
I, [2026-03-24T15:25:57.739861 #1331422] INFO -- : Worker 0:0 started
|
|
82
|
-
I, [2026-03-24T15:25:57.739962 #1331422] INFO -- : Worker 0:1 started
|
|
83
|
-
I, [2026-03-24T15:25:57.740131 #1331422] INFO -- : Worker 1:0 started
|
|
84
|
-
I, [2026-03-24T15:25:57.740211 #1331422] INFO -- : Worker 1:1 started
|
|
85
|
-
I, [2026-03-24T15:25:57.740352 #1331422] INFO -- : Worker 2:0 started
|
|
86
|
-
I, [2026-03-24T15:25:57.740408 #1331422] INFO -- : Worker 2:1 started
|
|
87
|
-
I, [2026-03-24T15:25:57.740532 #1331422] INFO -- : Worker 3:0 started
|
|
88
|
-
I, [2026-03-24T15:25:57.740590 #1331422] INFO -- : Worker 3:1 started
|
|
89
|
-
I, [2026-03-24T15:25:57.740722 #1331422] INFO -- : Worker 4:0 started
|
|
90
|
-
I, [2026-03-24T15:25:57.740776 #1331422] INFO -- : Worker 4:1 started
|
|
91
|
-
I, [2026-03-24T15:25:57.740844 #1331422] INFO -- : Zizq producer thread started
|
|
92
|
-
I, [2026-03-24T15:25:57.740878 #1331422] INFO -- : Connecting to http://localhost:7890...
|
|
93
|
-
I, [2026-03-24T15:25:57.792173 #1331422] INFO -- : Connected. Listening for jobs.
|
|
164
|
+
$ bundle exec zizq-worker
|
|
165
|
+
I, [...] INFO -- : Zizq worker starting: 1 threads, 25 fibers, prefetch=50
|
|
166
|
+
I, [...] INFO -- : Connected. Listening for jobs.
|
|
94
167
|
```
|
|
95
168
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
169
|
+
For Sinatra or other apps, pass the entrypoint explicitly:
|
|
170
|
+
|
|
171
|
+
```shell
|
|
172
|
+
$ bundle exec zizq-worker app.rb
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Worker defaults (`queues`, `thread_count`, `fiber_count`, `prefetch`) come
|
|
176
|
+
from your `Zizq.configure { |c| c.worker.* }` block. CLI flags
|
|
177
|
+
(`--threads`, `--fibers`, `--queue`, `--all-queues`, etc.) override the
|
|
178
|
+
configured defaults when needed. Leave `--fibers 1` if your application
|
|
179
|
+
isn't fiber-safe — no `Async` context is loaded in that case. `INT` /
|
|
180
|
+
`TERM` trigger a graceful shutdown (drains in-flight jobs up to
|
|
181
|
+
`--shutdown-deadline`, default 30s).
|
|
182
|
+
|
|
183
|
+
For more control — for example running the worker in-process alongside a
|
|
184
|
+
Rack app — construct `Zizq::Worker` directly:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
require 'zizq'
|
|
188
|
+
|
|
189
|
+
# Picks up queues, fiber_count, etc. from Zizq.configure { |c| c.worker.* };
|
|
190
|
+
# any kwarg here overrides those defaults.
|
|
191
|
+
worker = Zizq::Worker.new(queues: ['emails', 'payments'])
|
|
192
|
+
|
|
193
|
+
Signal.trap('INT') { worker.stop }
|
|
194
|
+
worker.run # blocks until the worker stops
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`#run` blocks until the worker terminates; `#stop` drains in-flight jobs
|
|
198
|
+
gracefully, `#kill` forces an immediate stop. On any unclean shutdown the
|
|
199
|
+
server returns unfinished jobs to the queue — no job is lost.
|
|
200
|
+
|
|
201
|
+
### Recurring jobs (cron)
|
|
202
|
+
|
|
203
|
+
Define a cron schedule in your application's startup code. Definitions are
|
|
204
|
+
idempotent — every process can safely define the same schedule, and Zizq
|
|
205
|
+
keeps the server in sync by adding, replacing, and removing entries as the
|
|
206
|
+
definition changes. Cron requires a Pro license on the server.
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
Zizq.define_crontab('maintenance', timezone: 'Europe/London') do |cron|
|
|
210
|
+
# Every 15 minutes.
|
|
211
|
+
cron.define_entry('refresh_warehouse', '*/15 * * * *').enqueue(
|
|
212
|
+
RefreshWarehouseJob, incremental: true
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# 9am London time, every day.
|
|
216
|
+
cron.define_entry('daily_digest', '0 9 * * *').enqueue(SendDailyDigestJob)
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Once defined, schedules can be inspected and managed via
|
|
221
|
+
`Zizq.crontab('maintenance')` — paused/resumed at the schedule level or per
|
|
222
|
+
entry, and deleted entirely when no longer needed.
|
|
99
223
|
|
|
100
224
|
## Resources
|
|
101
225
|
|
|
102
226
|
* [Ruby Client Docs](https://zizq.io/docs/clients/ruby/)
|
|
103
227
|
* [Getting Started Docs](https://zizq.io/docs/getting-started/)
|
|
104
228
|
* [Zizq Command Reference](https://zizq.io/docs/cli/)
|
|
105
|
-
* [Zizq
|
|
229
|
+
* [Zizq Ruby Client Source](https://github.com/zizq-labs/zizq-ruby)
|
|
106
230
|
* [Zizq Source](https://github.com/zizq-labs/zizq)
|
|
107
231
|
|
|
108
232
|
## Support & Feedback
|
|
@@ -111,3 +235,7 @@ If you need help using Zizq,
|
|
|
111
235
|
[create an issue](https://github.com/zizq-labs/zizq-ruby/issues) on the
|
|
112
236
|
[zizq-ruby](https://github.com/zizq-labs/zizq-ruby) repo. Feedback is very
|
|
113
237
|
welcome.
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT — see [LICENSE](LICENSE).
|
data/bin/zizq-worker
CHANGED
|
@@ -11,58 +11,96 @@ require "zizq"
|
|
|
11
11
|
# Default deadline for a graceful `stop` before escalating to `kill`.
|
|
12
12
|
DEFAULT_SHUTDOWN_DEADLINE = 30.0
|
|
13
13
|
|
|
14
|
-
# --- Defaults from env vars,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
# --- Defaults from env vars (nil = "not set, defer to Zizq.configure then Worker default") ---
|
|
15
|
+
#
|
|
16
|
+
# Note: env-var-unset values stay as nil rather than being seeded
|
|
17
|
+
# with the Worker's `DEFAULT_*` constants. This lets `Zizq.configure
|
|
18
|
+
# { |c| c.worker.<field> = ... }` win when neither the env nor the
|
|
19
|
+
# CLI explicitly set the value.
|
|
20
|
+
|
|
21
|
+
thread_count = ENV.key?("ZIZQ_THREADS") ? Integer(ENV["ZIZQ_THREADS"]) : nil
|
|
22
|
+
fiber_count = ENV.key?("ZIZQ_FIBERS") ? Integer(ENV["ZIZQ_FIBERS"]) : nil
|
|
18
23
|
prefetch = ENV.key?("ZIZQ_PREFETCH") ? Integer(ENV["ZIZQ_PREFETCH"]) : nil
|
|
19
24
|
shutdown_deadline = Float(ENV.fetch("ZIZQ_SHUTDOWN_DEADLINE", DEFAULT_SHUTDOWN_DEADLINE))
|
|
20
|
-
retry_min_wait =
|
|
21
|
-
retry_max_wait =
|
|
22
|
-
retry_multiplier =
|
|
23
|
-
|
|
24
|
-
queues
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
retry_min_wait = ENV.key?("ZIZQ_RETRY_MIN_WAIT") ? Float(ENV["ZIZQ_RETRY_MIN_WAIT"]) : nil
|
|
26
|
+
retry_max_wait = ENV.key?("ZIZQ_RETRY_MAX_WAIT") ? Float(ENV["ZIZQ_RETRY_MAX_WAIT"]) : nil
|
|
27
|
+
retry_multiplier = ENV.key?("ZIZQ_RETRY_MULTIPLIER") ? Float(ENV["ZIZQ_RETRY_MULTIPLIER"]) : nil
|
|
28
|
+
|
|
29
|
+
# Queue state: explicit list vs. all-queues vs. unset (defer to config).
|
|
30
|
+
# `queues_set_by_user` toggles to true once any CLI/env queue flag is seen.
|
|
31
|
+
queues = []
|
|
32
|
+
queues_set_by_user = false
|
|
33
|
+
all_queues = false
|
|
34
|
+
|
|
35
|
+
if ENV.key?("ZIZQ_QUEUES")
|
|
36
|
+
parsed = ENV["ZIZQ_QUEUES"].split(",").map(&:strip).reject(&:empty?)
|
|
37
|
+
queues = parsed
|
|
38
|
+
queues_set_by_user = true
|
|
28
39
|
end
|
|
29
40
|
|
|
30
41
|
# --- CLI flag parsing (overrides env var defaults) ---
|
|
31
42
|
|
|
32
43
|
parser = OptionParser.new do |opts|
|
|
33
|
-
opts.banner = "Usage: zizq-worker [OPTIONS]
|
|
44
|
+
opts.banner = "Usage: zizq-worker [OPTIONS] [ENTRYPOINT]"
|
|
34
45
|
|
|
35
46
|
opts.separator ""
|
|
36
|
-
opts.separator "Start a Zizq worker process.
|
|
37
|
-
opts.separator "
|
|
47
|
+
opts.separator "Start a Zizq worker process."
|
|
48
|
+
opts.separator ""
|
|
49
|
+
opts.separator "The ENTRYPOINT is a Ruby file that loads your application before the"
|
|
50
|
+
opts.separator "worker starts (so Zizq.configure runs and your job classes are loaded)."
|
|
51
|
+
opts.separator "Resolved in this order:"
|
|
52
|
+
opts.separator ""
|
|
53
|
+
opts.separator " 1. The ENTRYPOINT positional argument, if given."
|
|
54
|
+
opts.separator " 2. The ZIZQ_ENTRYPOINT environment variable, if set."
|
|
55
|
+
opts.separator " 3. config/environment.rb in the current directory, if it exists."
|
|
56
|
+
opts.separator " (This is the canonical Rails boot file, so Rails apps run with"
|
|
57
|
+
opts.separator " no entrypoint argument at all.)"
|
|
58
|
+
opts.separator ""
|
|
59
|
+
opts.separator "Configuration:"
|
|
60
|
+
opts.separator ""
|
|
61
|
+
opts.separator " Client config (url, format, TLS, logger) and worker defaults"
|
|
62
|
+
opts.separator " (queues, thread/fiber count, prefetch, etc.) belong in your"
|
|
63
|
+
opts.separator " Zizq.configure block inside the entrypoint, e.g.:"
|
|
38
64
|
opts.separator ""
|
|
39
|
-
opts.separator "
|
|
40
|
-
opts.separator "
|
|
65
|
+
opts.separator " Zizq.configure do |c|"
|
|
66
|
+
opts.separator " c.url = \"https://...\""
|
|
67
|
+
opts.separator " c.worker.queues = [\"emails\", \"webhooks\"]"
|
|
68
|
+
opts.separator " c.worker.fiber_count = 25"
|
|
69
|
+
opts.separator " end"
|
|
70
|
+
opts.separator ""
|
|
71
|
+
opts.separator " CLI flags and env vars below override whatever is configured there."
|
|
41
72
|
opts.separator ""
|
|
42
73
|
opts.separator "Options:"
|
|
43
74
|
|
|
44
|
-
opts.on("-t", "--threads N", Integer, "Number of worker threads (default: #{Zizq::Worker::DEFAULT_THREADS}, env: ZIZQ_THREADS)") do |n|
|
|
75
|
+
opts.on("-t", "--threads N", Integer, "Number of worker threads (fallback default: #{Zizq::Worker::DEFAULT_THREADS}, env: ZIZQ_THREADS)") do |n|
|
|
45
76
|
thread_count = n
|
|
46
77
|
end
|
|
47
78
|
|
|
48
|
-
opts.on("-f", "--fibers N", Integer, "Number of fibers per thread (default: #{Zizq::Worker::DEFAULT_FIBERS}, env: ZIZQ_FIBERS)") do |n|
|
|
79
|
+
opts.on("-f", "--fibers N", Integer, "Number of fibers per thread (fallback default: #{Zizq::Worker::DEFAULT_FIBERS}, env: ZIZQ_FIBERS)") do |n|
|
|
49
80
|
fiber_count = n
|
|
50
81
|
end
|
|
51
82
|
|
|
52
|
-
opts.on("-p", "--prefetch N", Integer, "Prefetch count (default: 2*threads*fibers, env: ZIZQ_PREFETCH)") do |n|
|
|
83
|
+
opts.on("-p", "--prefetch N", Integer, "Prefetch count (fallback default: 2*threads*fibers, env: ZIZQ_PREFETCH)") do |n|
|
|
53
84
|
prefetch = n
|
|
54
85
|
end
|
|
55
86
|
|
|
56
87
|
queues_from_cli = false
|
|
57
88
|
opts.on("-q", "--queue QUEUE", "Queue to process (repeatable or comma-separated, env: ZIZQ_QUEUES)") do |q|
|
|
58
|
-
# First
|
|
89
|
+
# First CLI `-q` replaces the env var default; subsequent ones
|
|
90
|
+
# accumulate. `queues_from_cli` distinguishes that from
|
|
91
|
+
# `queues_set_by_user` (which also tracks env-var input).
|
|
59
92
|
unless queues_from_cli
|
|
60
93
|
queues = []
|
|
61
94
|
queues_from_cli = true
|
|
62
95
|
end
|
|
96
|
+
queues_set_by_user = true
|
|
63
97
|
queues.concat(q.split(",").map(&:strip).reject(&:empty?))
|
|
64
98
|
end
|
|
65
99
|
|
|
100
|
+
opts.on("--all-queues", "Override any configured queues to process all queues (mutually exclusive with -q)") do
|
|
101
|
+
all_queues = true
|
|
102
|
+
end
|
|
103
|
+
|
|
66
104
|
opts.on("--shutdown-deadline N", Float, "Graceful shutdown deadline in seconds before escalating to kill (default: #{DEFAULT_SHUTDOWN_DEADLINE}, env: ZIZQ_SHUTDOWN_DEADLINE)") do |n|
|
|
67
105
|
shutdown_deadline = n
|
|
68
106
|
end
|
|
@@ -100,22 +138,38 @@ end
|
|
|
100
138
|
|
|
101
139
|
# --- Validate options ---
|
|
102
140
|
|
|
103
|
-
if thread_count < 1
|
|
141
|
+
if thread_count && thread_count < 1
|
|
104
142
|
warn "Error: --threads must be at least 1 (got #{thread_count})"
|
|
105
143
|
exit 1
|
|
106
144
|
end
|
|
107
145
|
|
|
108
|
-
if fiber_count < 1
|
|
146
|
+
if fiber_count && fiber_count < 1
|
|
109
147
|
warn "Error: --fibers must be at least 1 (got #{fiber_count})"
|
|
110
148
|
exit 1
|
|
111
149
|
end
|
|
112
150
|
|
|
113
|
-
|
|
151
|
+
if all_queues && queues_set_by_user
|
|
152
|
+
warn "Error: --all-queues and --queue (or ZIZQ_QUEUES) are mutually exclusive."
|
|
153
|
+
exit 1
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# --- Resolve and load entrypoint ---
|
|
157
|
+
#
|
|
158
|
+
# Precedence:
|
|
159
|
+
# 1. CLI argument: `zizq-worker path/to/entrypoint.rb`
|
|
160
|
+
# 2. ZIZQ_ENTRYPOINT env var
|
|
161
|
+
# 3. Auto-detect: `config/environment.rb` (Rails apps)
|
|
114
162
|
|
|
115
|
-
entrypoint = ARGV[0]
|
|
163
|
+
entrypoint = ARGV[0] || ENV["ZIZQ_ENTRYPOINT"] || (
|
|
164
|
+
File.file?("config/environment.rb") ? "config/environment.rb" : nil
|
|
165
|
+
)
|
|
116
166
|
|
|
117
167
|
if entrypoint.nil?
|
|
118
|
-
warn "Error:
|
|
168
|
+
warn "Error: no entrypoint found."
|
|
169
|
+
warn ""
|
|
170
|
+
warn "Specify one as a CLI argument or via the ZIZQ_ENTRYPOINT env var."
|
|
171
|
+
warn "Rails apps are detected automatically via the presence of"
|
|
172
|
+
warn "config/environment.rb in the current directory."
|
|
119
173
|
warn ""
|
|
120
174
|
warn parser.help
|
|
121
175
|
exit 1
|
|
@@ -128,17 +182,33 @@ end
|
|
|
128
182
|
|
|
129
183
|
require File.expand_path(entrypoint)
|
|
130
184
|
|
|
131
|
-
# ---
|
|
185
|
+
# --- Resolve queues ---
|
|
186
|
+
#
|
|
187
|
+
# --all-queues -> [] (explicit "all queues" override)
|
|
188
|
+
# -q / ZIZQ_QUEUES set -> the parsed list
|
|
189
|
+
# neither -> nil (defer to Zizq.configure → Worker default)
|
|
190
|
+
|
|
191
|
+
queues_value =
|
|
192
|
+
if all_queues
|
|
193
|
+
[]
|
|
194
|
+
elsif queues_set_by_user
|
|
195
|
+
queues
|
|
196
|
+
end
|
|
132
197
|
|
|
133
|
-
worker
|
|
198
|
+
# --- Start the worker ---
|
|
199
|
+
#
|
|
200
|
+
# `.compact` drops any unset kwarg so `Zizq::Worker#initialize`'s
|
|
201
|
+
# fallback chain (kwarg -> Zizq.configuration.worker -> DEFAULT_*)
|
|
202
|
+
# applies for whichever values weren't given on the CLI / via env.
|
|
203
|
+
worker = Zizq::Worker.new(**{
|
|
134
204
|
thread_count:,
|
|
135
205
|
fiber_count:,
|
|
136
206
|
prefetch:,
|
|
137
|
-
queues
|
|
207
|
+
queues: queues_value,
|
|
138
208
|
retry_min_wait:,
|
|
139
209
|
retry_max_wait:,
|
|
140
210
|
retry_multiplier:,
|
|
141
|
-
)
|
|
211
|
+
}.compact)
|
|
142
212
|
|
|
143
213
|
# `Zizq::Worker#stop` is patient (waits forever for in-flight jobs and
|
|
144
214
|
# acks to drain). We enforce the shutdown deadline at the CLI level: the
|
|
@@ -154,7 +224,10 @@ stopping = false
|
|
|
154
224
|
%w[INT TERM].each do |signal|
|
|
155
225
|
Signal.trap(signal) do
|
|
156
226
|
if stopping
|
|
157
|
-
exit(
|
|
227
|
+
# Second signal: hard exit. `exit!` (not `exit`) so SystemExit
|
|
228
|
+
# terminates the process from any thread context, including
|
|
229
|
+
# the signal trap.
|
|
230
|
+
exit!(1)
|
|
158
231
|
else
|
|
159
232
|
worker.stop
|
|
160
233
|
stopping = true
|
|
@@ -164,7 +237,10 @@ stopping = false
|
|
|
164
237
|
worker.logger.warn do
|
|
165
238
|
"Worker did not stop within #{shutdown_deadline}s, killing..."
|
|
166
239
|
end
|
|
167
|
-
exit
|
|
240
|
+
# `exit!` rather than `exit`: this runs in a watchdog thread,
|
|
241
|
+
# and `exit` would only raise SystemExit in *this* thread,
|
|
242
|
+
# leaving main (joining the hung worker thread) untouched.
|
|
243
|
+
exit!(1)
|
|
168
244
|
end
|
|
169
245
|
end
|
|
170
246
|
end
|
data/lib/zizq/client.rb
CHANGED
|
@@ -50,15 +50,30 @@ module Zizq
|
|
|
50
50
|
# Initialize a new instance of the client with the given base URL and
|
|
51
51
|
# optional format options.
|
|
52
52
|
#
|
|
53
|
+
# `read_timeout` and `stream_idle_timeout` are per-operation socket
|
|
54
|
+
# I/O timeouts (seconds). Each individual socket read/write is
|
|
55
|
+
# bounded by the timeout. The streaming `#take_jobs` endpoint uses
|
|
56
|
+
# `stream_idle_timeout` because the server sends heartbeats at
|
|
57
|
+
# periodic intervals which keeps the connection alive.
|
|
58
|
+
#
|
|
53
59
|
# @rbs url: String
|
|
54
60
|
# @rbs format: Zizq::format
|
|
55
61
|
# @rbs ssl_context: OpenSSL::SSL::SSLContext?
|
|
62
|
+
# @rbs read_timeout: Numeric
|
|
63
|
+
# @rbs stream_idle_timeout: Numeric
|
|
56
64
|
# @rbs return: void
|
|
57
|
-
def initialize(url:,
|
|
65
|
+
def initialize(url:,
|
|
66
|
+
format: :msgpack,
|
|
67
|
+
ssl_context: nil,
|
|
68
|
+
read_timeout: 30,
|
|
69
|
+
stream_idle_timeout: 30)
|
|
58
70
|
@url = url.chomp("/")
|
|
59
71
|
@format = format
|
|
60
72
|
|
|
61
|
-
endpoint_options = {
|
|
73
|
+
endpoint_options = {
|
|
74
|
+
protocol: Async::HTTP::Protocol::HTTP2,
|
|
75
|
+
timeout: read_timeout,
|
|
76
|
+
} #: Hash[Symbol, untyped]
|
|
62
77
|
endpoint_options[:ssl_context] = ssl_context if ssl_context
|
|
63
78
|
|
|
64
79
|
@endpoint = Async::HTTP::Endpoint.parse(
|
|
@@ -73,8 +88,14 @@ module Zizq
|
|
|
73
88
|
# on separate threads with their own HTTP/2 clients, so they're
|
|
74
89
|
# unaffected either way. HTTP/1.1 gives the stream a plain TCP
|
|
75
90
|
# socket with no framing tax and measurably better throughput.
|
|
91
|
+
#
|
|
92
|
+
# The stream endpoint uses `stream_idle_timeout` for its socket
|
|
93
|
+
# timeout so server heartbeats (~3s) keep it alive while only
|
|
94
|
+
# genuinely dead connections (no data for the full window)
|
|
95
|
+
# trigger a reconnect.
|
|
76
96
|
stream_endpoint_options = endpoint_options.merge(
|
|
77
97
|
protocol: Async::HTTP::Protocol::HTTP11,
|
|
98
|
+
timeout: stream_idle_timeout,
|
|
78
99
|
)
|
|
79
100
|
@stream_endpoint = Async::HTTP::Endpoint.parse(
|
|
80
101
|
@url,
|
|
@@ -709,7 +730,12 @@ module Zizq
|
|
|
709
730
|
response.close rescue nil
|
|
710
731
|
end
|
|
711
732
|
end
|
|
712
|
-
rescue SocketError,
|
|
733
|
+
rescue SocketError,
|
|
734
|
+
IOError,
|
|
735
|
+
EOFError,
|
|
736
|
+
Errno::ECONNRESET,
|
|
737
|
+
Errno::EPIPE,
|
|
738
|
+
IO::TimeoutError,
|
|
713
739
|
OpenSSL::SSL::SSLError => e
|
|
714
740
|
raise ConnectionError, e.message
|
|
715
741
|
end
|
data/lib/zizq/configuration.rb
CHANGED
|
@@ -29,18 +29,26 @@ module Zizq
|
|
|
29
29
|
# Logger instance to which to write log messages.
|
|
30
30
|
attr_accessor :logger #: Logger
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
# Per-operation socket I/O timeout (seconds) for regular API calls
|
|
33
|
+
# (enqueue, queries, mutations). Each socket read/write is bounded
|
|
34
|
+
# by this value. A request whose handshake or any single read exceeds
|
|
35
|
+
# this raises `IO::TimeoutError`.
|
|
33
36
|
#
|
|
34
|
-
#
|
|
37
|
+
# Default: 30.
|
|
38
|
+
attr_accessor :read_timeout #: Numeric
|
|
39
|
+
|
|
40
|
+
# Per-operation socket I/O timeout (seconds) for the long-lived
|
|
41
|
+
# `#take_jobs` stream. The server sends heartbeats every ~3 seconds,
|
|
42
|
+
# so each read returns within that window and keeps the connection
|
|
43
|
+
# alive; the connection only times out if the server falls silent for
|
|
44
|
+
# longer than this. The Worker catches the resulting error and
|
|
45
|
+
# reconnects with backoff.
|
|
35
46
|
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# client_cert: "path/to/client-cert.pem", # Client certificate for mTLS
|
|
39
|
-
# client_key: "path/to/client-key.pem", # Client private key for mTLS
|
|
40
|
-
# }
|
|
47
|
+
# Should be comfortably above the server's heartbeat interval to
|
|
48
|
+
# avoid false-positive disconnects.
|
|
41
49
|
#
|
|
42
|
-
#
|
|
43
|
-
attr_accessor :
|
|
50
|
+
# Default: 30.
|
|
51
|
+
attr_accessor :stream_idle_timeout #: Numeric
|
|
44
52
|
|
|
45
53
|
# Middleware chain for enqueue. Each middleware receives an
|
|
46
54
|
# `EnqueueRequest` and a chain to continue.
|
|
@@ -55,10 +63,79 @@ module Zizq
|
|
|
55
63
|
@format = :msgpack
|
|
56
64
|
@logger = Logger.new($stdout, level: Logger::INFO)
|
|
57
65
|
@tls = nil
|
|
66
|
+
@worker = nil
|
|
67
|
+
@read_timeout = 30
|
|
68
|
+
@stream_idle_timeout = 30
|
|
58
69
|
@enqueue_middleware = Middleware::Chain.new(Identity.new)
|
|
59
70
|
@dequeue_middleware = Middleware::Chain.new(Zizq::Job)
|
|
60
71
|
end
|
|
61
72
|
|
|
73
|
+
# TLS settings for connecting to the server over HTTPS.
|
|
74
|
+
#
|
|
75
|
+
# Configure via the `c.tls` accessors inside a `Zizq.configure`
|
|
76
|
+
# block:
|
|
77
|
+
#
|
|
78
|
+
# Zizq.configure do |c|
|
|
79
|
+
# c.tls.ca = "/path/to/server-ca-cert.pem"
|
|
80
|
+
# c.tls.client_cert = "/path/to/client-cert.pem"
|
|
81
|
+
# c.tls.client_key = "/path/to/client-key.pem"
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# All values may be PEM-encoded strings or file paths. Set
|
|
85
|
+
# `c.tls = nil` to explicitly disable TLS.
|
|
86
|
+
#
|
|
87
|
+
# Note: Mutual TLS support requires a Zizq Pro license on the
|
|
88
|
+
# server.
|
|
89
|
+
def tls #: () -> TlsConfiguration
|
|
90
|
+
@tls ||= TlsConfiguration.new
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def tls=(value) #: ((Hash[Symbol, String?] | TlsConfiguration)?) -> void
|
|
94
|
+
case value
|
|
95
|
+
when nil
|
|
96
|
+
@tls = nil
|
|
97
|
+
when TlsConfiguration
|
|
98
|
+
@tls = value
|
|
99
|
+
when Hash
|
|
100
|
+
@tls = TlsConfiguration.new(**value)
|
|
101
|
+
else
|
|
102
|
+
raise ArgumentError,
|
|
103
|
+
"Zizq.configure: tls= expects a Hash, Zizq::TlsConfiguration, or nil " \
|
|
104
|
+
"(got #{value.class})"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Defaults for `Zizq::Worker` instances. Apps populate this in
|
|
109
|
+
# their `Zizq.configure` block:
|
|
110
|
+
#
|
|
111
|
+
# Zizq.configure do |c|
|
|
112
|
+
# c.worker.queues = ["emails"]
|
|
113
|
+
# c.worker.fiber_count = 25
|
|
114
|
+
# end
|
|
115
|
+
#
|
|
116
|
+
# Anything left unset here falls through to the Worker's
|
|
117
|
+
# hardcoded defaults; anything explicitly passed to `Worker.new`
|
|
118
|
+
# (or set via `zizq-worker` CLI flags / env vars) overrides
|
|
119
|
+
# whatever is configured here.
|
|
120
|
+
def worker #: () -> WorkerConfiguration
|
|
121
|
+
@worker ||= WorkerConfiguration.new
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def worker=(value) #: ((Hash[Symbol, untyped] | WorkerConfiguration)?) -> void
|
|
125
|
+
case value
|
|
126
|
+
when nil
|
|
127
|
+
@worker = nil
|
|
128
|
+
when WorkerConfiguration
|
|
129
|
+
@worker = value
|
|
130
|
+
when Hash
|
|
131
|
+
@worker = WorkerConfiguration.new(**value)
|
|
132
|
+
else
|
|
133
|
+
raise ArgumentError,
|
|
134
|
+
"Zizq.configure: worker= expects a Hash, Zizq::WorkerConfiguration, or nil " \
|
|
135
|
+
"(got #{value.class})"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
62
139
|
# The job dispatcher.
|
|
63
140
|
# This is the terminal of the dequeue middleware chain.
|
|
64
141
|
# Defaults to `Zizq::Job` which finds and executes jobs written by mixing
|
|
@@ -89,6 +166,14 @@ module Zizq
|
|
|
89
166
|
raise ArgumentError, "Zizq.configure: format must be :msgpack or :json, got #{format.inspect}"
|
|
90
167
|
end
|
|
91
168
|
|
|
169
|
+
unless read_timeout.is_a?(Numeric) && read_timeout > 0
|
|
170
|
+
raise ArgumentError, "Zizq.configure: read_timeout must be a positive number, got #{read_timeout.inspect}"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
unless stream_idle_timeout.is_a?(Numeric) && stream_idle_timeout > 0
|
|
174
|
+
raise ArgumentError, "Zizq.configure: stream_idle_timeout must be a positive number, got #{stream_idle_timeout.inspect}"
|
|
175
|
+
end
|
|
176
|
+
|
|
92
177
|
tls = @tls
|
|
93
178
|
validate_tls!(tls) if tls
|
|
94
179
|
end
|
|
@@ -99,21 +184,22 @@ module Zizq
|
|
|
99
184
|
def ssl_context #: () -> OpenSSL::SSL::SSLContext?
|
|
100
185
|
tls = @tls
|
|
101
186
|
return nil unless tls
|
|
187
|
+
return nil if tls.to_h.values.all?(&:nil?)
|
|
102
188
|
|
|
103
189
|
ctx = OpenSSL::SSL::SSLContext.new
|
|
104
190
|
|
|
105
|
-
if (ca = tls
|
|
191
|
+
if (ca = tls.ca)
|
|
106
192
|
store = OpenSSL::X509::Store.new
|
|
107
193
|
store.add_cert(load_cert(ca))
|
|
108
194
|
ctx.cert_store = store
|
|
109
195
|
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
110
196
|
end
|
|
111
197
|
|
|
112
|
-
if (client_cert = tls
|
|
198
|
+
if (client_cert = tls.client_cert)
|
|
113
199
|
ctx.cert = load_cert(client_cert)
|
|
114
200
|
end
|
|
115
201
|
|
|
116
|
-
if (client_key = tls
|
|
202
|
+
if (client_key = tls.client_key)
|
|
117
203
|
ctx.key = load_key(client_key)
|
|
118
204
|
end
|
|
119
205
|
|
|
@@ -130,14 +216,13 @@ module Zizq
|
|
|
130
216
|
|
|
131
217
|
private
|
|
132
218
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
raise ArgumentError, "Zizq.configure: tls[:client_key] is required when tls[:client_cert] is set"
|
|
219
|
+
def validate_tls!(tls) #: (TlsConfiguration) -> void
|
|
220
|
+
if tls.client_cert && !tls.client_key
|
|
221
|
+
raise ArgumentError, "Zizq.configure: tls.client_key is required when tls.client_cert is set"
|
|
137
222
|
end
|
|
138
223
|
|
|
139
|
-
if tls
|
|
140
|
-
raise ArgumentError, "Zizq.configure: tls
|
|
224
|
+
if tls.client_key && !tls.client_cert
|
|
225
|
+
raise ArgumentError, "Zizq.configure: tls.client_cert is required when tls.client_key is set"
|
|
141
226
|
end
|
|
142
227
|
end
|
|
143
228
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
# TLS settings for connecting to the Zizq server over HTTPS.
|
|
9
|
+
#
|
|
10
|
+
# Set inside a `Zizq.configure` block via the `c.tls` accessors:
|
|
11
|
+
#
|
|
12
|
+
# Zizq.configure do |c|
|
|
13
|
+
# c.tls.ca = "/path/to/ca-cert.pem"
|
|
14
|
+
# c.tls.client_cert = "/path/to/client-cert.pem"
|
|
15
|
+
# c.tls.client_key = "/path/to/client-key.pem"
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# All values may be PEM-encoded strings or file paths.
|
|
19
|
+
#
|
|
20
|
+
# Note: Mutual TLS support requires a Zizq Pro license on the server.
|
|
21
|
+
TlsConfiguration = Struct.new(
|
|
22
|
+
:ca, #: String?
|
|
23
|
+
:client_cert, #: String?
|
|
24
|
+
:client_key, #: String?
|
|
25
|
+
keyword_init: true
|
|
26
|
+
)
|
|
27
|
+
end
|
data/lib/zizq/version.rb
CHANGED
data/lib/zizq/worker.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Zizq
|
|
|
15
15
|
#
|
|
16
16
|
# Total concurrency is calculated as `thread_count * fiber_count`.
|
|
17
17
|
class Worker
|
|
18
|
-
DEFAULT_THREADS =
|
|
18
|
+
DEFAULT_THREADS = 1 #: Integer
|
|
19
19
|
DEFAULT_FIBERS = 1 #: Integer
|
|
20
20
|
DEFAULT_RETRY_MIN_WAIT = 1
|
|
21
21
|
DEFAULT_RETRY_MAX_WAIT = 30
|
|
@@ -50,11 +50,6 @@ module Zizq
|
|
|
50
50
|
# pipeline full while ack round-trips are in flight.
|
|
51
51
|
attr_reader :prefetch #: Integer
|
|
52
52
|
|
|
53
|
-
# Proc to derive a worker ID string for each thread and fiber.
|
|
54
|
-
#
|
|
55
|
-
# When not present, the Zizq server assigns a random worker ID.
|
|
56
|
-
attr_reader :worker_id_proc #: (^(Integer, Integer) -> String?)?
|
|
57
|
-
|
|
58
53
|
# An instance of a Logger to be used for worker logging.
|
|
59
54
|
attr_reader :logger #: Logger
|
|
60
55
|
|
|
@@ -66,45 +61,55 @@ module Zizq
|
|
|
66
61
|
# their own `Zizq::Middleware::Chain` if middleware needs to be applied.
|
|
67
62
|
attr_reader :dispatcher #: ^(Resources::Job) -> void
|
|
68
63
|
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
64
|
+
# All keyword arguments default to `nil` and follow a three-level
|
|
65
|
+
# fallback chain:
|
|
66
|
+
#
|
|
67
|
+
# 1. Explicit kwarg passed to `Worker.new`.
|
|
68
|
+
# 2. `Zizq.configuration.worker.<field>` set in the app's
|
|
69
|
+
# `Zizq.configure` block.
|
|
70
|
+
# 3. The Worker's hardcoded `DEFAULT_*` constants.
|
|
71
|
+
#
|
|
72
|
+
# @rbs queues: Array[String]?
|
|
73
|
+
# @rbs thread_count: Integer?
|
|
74
|
+
# @rbs fiber_count: Integer?
|
|
72
75
|
# @rbs prefetch: Integer?
|
|
73
|
-
# @rbs retry_min_wait: (Float | Integer)
|
|
74
|
-
# @rbs retry_max_wait: (Float | Integer)
|
|
75
|
-
# @rbs retry_multiplier: (Float | Integer)
|
|
76
|
-
# @rbs worker_id: (^(Integer, Integer) -> String?)?
|
|
76
|
+
# @rbs retry_min_wait: (Float | Integer)?
|
|
77
|
+
# @rbs retry_max_wait: (Float | Integer)?
|
|
78
|
+
# @rbs retry_multiplier: (Float | Integer)?
|
|
77
79
|
# @rbs logger: Logger?
|
|
78
80
|
# @rbs dispatcher: (^(Resources::Job) -> void)?
|
|
79
81
|
# @rbs return: void
|
|
80
82
|
def initialize(
|
|
81
|
-
queues:
|
|
82
|
-
thread_count:
|
|
83
|
-
fiber_count:
|
|
83
|
+
queues: nil,
|
|
84
|
+
thread_count: nil,
|
|
85
|
+
fiber_count: nil,
|
|
84
86
|
prefetch: nil,
|
|
85
|
-
retry_min_wait:
|
|
86
|
-
retry_max_wait:
|
|
87
|
-
retry_multiplier:
|
|
88
|
-
worker_id: nil,
|
|
87
|
+
retry_min_wait: nil,
|
|
88
|
+
retry_max_wait: nil,
|
|
89
|
+
retry_multiplier: nil,
|
|
89
90
|
logger: nil,
|
|
90
91
|
dispatcher: nil
|
|
91
92
|
)
|
|
92
|
-
raise ArgumentError, "thread_count must be at least 1 (got #{thread_count})" if thread_count < 1
|
|
93
|
-
raise ArgumentError, "fiber_count must be at least 1 (got #{fiber_count})" if fiber_count < 1
|
|
94
|
-
|
|
95
93
|
Zizq.configuration.validate!
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@
|
|
99
|
-
@
|
|
100
|
-
@
|
|
101
|
-
@
|
|
102
|
-
@
|
|
103
|
-
@
|
|
104
|
-
@
|
|
94
|
+
config = Zizq.configuration.worker
|
|
95
|
+
|
|
96
|
+
@queues = queues || config.queues || []
|
|
97
|
+
@thread_count = thread_count || config.thread_count || DEFAULT_THREADS
|
|
98
|
+
@fiber_count = fiber_count || config.fiber_count || DEFAULT_FIBERS
|
|
99
|
+
@prefetch = prefetch || config.prefetch || @thread_count * @fiber_count * 2
|
|
100
|
+
@retry_min_wait = retry_min_wait || config.retry_min_wait || DEFAULT_RETRY_MIN_WAIT
|
|
101
|
+
@retry_max_wait = retry_max_wait || config.retry_max_wait || DEFAULT_RETRY_MAX_WAIT
|
|
102
|
+
@retry_multiplier = retry_multiplier || config.retry_multiplier || DEFAULT_RETRY_MULTIPLIER
|
|
105
103
|
@logger = logger || Zizq.configuration.logger
|
|
106
104
|
@dispatcher = dispatcher || Zizq.configuration.dequeue_middleware
|
|
107
105
|
|
|
106
|
+
if @thread_count < 1
|
|
107
|
+
raise ArgumentError, "thread_count must be at least 1 (got #{@thread_count})"
|
|
108
|
+
end
|
|
109
|
+
if @fiber_count < 1
|
|
110
|
+
raise ArgumentError, "fiber_count must be at least 1 (got #{@fiber_count})"
|
|
111
|
+
end
|
|
112
|
+
|
|
108
113
|
reset_runtime_state
|
|
109
114
|
end
|
|
110
115
|
|
|
@@ -360,14 +365,12 @@ module Zizq
|
|
|
360
365
|
format("Worker %d:%d started", thread_idx, fiber_idx)
|
|
361
366
|
end
|
|
362
367
|
|
|
363
|
-
wid = resolve_worker_id(thread_idx, fiber_idx)
|
|
364
|
-
|
|
365
368
|
loop do
|
|
366
369
|
# pop returns nil when queue is closed and empty
|
|
367
370
|
job = @dispatch_queue.pop
|
|
368
371
|
break if job.nil?
|
|
369
372
|
|
|
370
|
-
dispatch(job
|
|
373
|
+
dispatch(job)
|
|
371
374
|
end
|
|
372
375
|
|
|
373
376
|
logger.info do
|
|
@@ -392,7 +395,7 @@ module Zizq
|
|
|
392
395
|
#
|
|
393
396
|
# Delegates to the configured dispatcher (default: `Zizq::Job.dispatch`)
|
|
394
397
|
# and reports success or failure.
|
|
395
|
-
def dispatch(job
|
|
398
|
+
def dispatch(job) #: (Resources::Job) -> void
|
|
396
399
|
job_id, job_type = job.id, job.type
|
|
397
400
|
|
|
398
401
|
begin
|
|
@@ -460,8 +463,5 @@ module Zizq
|
|
|
460
463
|
))
|
|
461
464
|
end
|
|
462
465
|
|
|
463
|
-
def resolve_worker_id(thread_idx, fiber_idx) #: (Integer, Integer) -> String?
|
|
464
|
-
worker_id_proc&.call(thread_idx, fiber_idx)
|
|
465
|
-
end
|
|
466
466
|
end
|
|
467
467
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
# Defaults for `Zizq::Worker` instances. Accessed via
|
|
9
|
+
# `Zizq.configuration.worker` and typically populated inside an
|
|
10
|
+
# application's `Zizq.configure` block:
|
|
11
|
+
#
|
|
12
|
+
# Zizq.configure do |c|
|
|
13
|
+
# c.url = "https://..."
|
|
14
|
+
# c.worker.queues = ["emails", "webhooks"]
|
|
15
|
+
# c.worker.thread_count = 1
|
|
16
|
+
# c.worker.fiber_count = 25
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Every field defaults to `nil`, meaning "use the Worker's own
|
|
20
|
+
# hardcoded default." Anything explicitly passed to `Worker.new` —
|
|
21
|
+
# or set via CLI flag / env var when launching `zizq-worker` —
|
|
22
|
+
# overrides whatever is set here.
|
|
23
|
+
#
|
|
24
|
+
# See `Zizq::Worker#initialize` for the full resolution order
|
|
25
|
+
# (explicit kwarg → Zizq.configuration.worker → `Worker::DEFAULT_*`).
|
|
26
|
+
#
|
|
27
|
+
# Fields:
|
|
28
|
+
#
|
|
29
|
+
# * `queues` — Queues to consume. `[]` means all queues.
|
|
30
|
+
# * `thread_count` — Number of worker threads.
|
|
31
|
+
# * `fiber_count` — Number of fibers per worker thread.
|
|
32
|
+
# * `prefetch` — Server-side prefetch limit. Defaults to
|
|
33
|
+
# `2 * threads * fibers`.
|
|
34
|
+
# * `retry_min_wait` — Minimum reconnect backoff in seconds.
|
|
35
|
+
# * `retry_max_wait` — Maximum reconnect backoff in seconds.
|
|
36
|
+
# * `retry_multiplier` — Multiplicative backoff factor between
|
|
37
|
+
# reconnect attempts.
|
|
38
|
+
WorkerConfiguration = Struct.new(
|
|
39
|
+
:queues, #: Array[String]?
|
|
40
|
+
:thread_count, #: Integer?
|
|
41
|
+
:fiber_count, #: Integer?
|
|
42
|
+
:prefetch, #: Integer?
|
|
43
|
+
:retry_min_wait, #: (Float | Integer)?
|
|
44
|
+
:retry_max_wait, #: (Float | Integer)?
|
|
45
|
+
:retry_multiplier, #: (Float | Integer)?
|
|
46
|
+
keyword_init: true
|
|
47
|
+
)
|
|
48
|
+
end
|
data/lib/zizq.rb
CHANGED
|
@@ -29,7 +29,9 @@ module Zizq
|
|
|
29
29
|
autoload :Lifecycle, "zizq/lifecycle"
|
|
30
30
|
autoload :Query, "zizq/query"
|
|
31
31
|
autoload :Resources, "zizq/resources"
|
|
32
|
+
autoload :TlsConfiguration, "zizq/tls_configuration"
|
|
32
33
|
autoload :Worker, "zizq/worker"
|
|
34
|
+
autoload :WorkerConfiguration, "zizq/worker_configuration"
|
|
33
35
|
|
|
34
36
|
# Sentinel indicating a field should not be included in the request.
|
|
35
37
|
# Used as the default for update parameters.
|
|
@@ -81,7 +83,9 @@ module Zizq
|
|
|
81
83
|
@client = Client.new(
|
|
82
84
|
url: configuration.url,
|
|
83
85
|
format: configuration.format,
|
|
84
|
-
ssl_context: configuration.ssl_context
|
|
86
|
+
ssl_context: configuration.ssl_context,
|
|
87
|
+
read_timeout: configuration.read_timeout,
|
|
88
|
+
stream_idle_timeout: configuration.stream_idle_timeout
|
|
85
89
|
)
|
|
86
90
|
end
|
|
87
91
|
end
|
|
@@ -42,11 +42,19 @@ module Zizq
|
|
|
42
42
|
# Initialize a new instance of the client with the given base URL and
|
|
43
43
|
# optional format options.
|
|
44
44
|
#
|
|
45
|
+
# `read_timeout` and `stream_idle_timeout` are per-operation socket
|
|
46
|
+
# I/O timeouts (seconds). Each individual socket read/write is
|
|
47
|
+
# bounded by the timeout. The streaming `#take_jobs` endpoint uses
|
|
48
|
+
# `stream_idle_timeout` because the server sends heartbeats at
|
|
49
|
+
# periodic intervals which keeps the connection alive.
|
|
50
|
+
#
|
|
45
51
|
# @rbs url: String
|
|
46
52
|
# @rbs format: Zizq::format
|
|
47
53
|
# @rbs ssl_context: OpenSSL::SSL::SSLContext?
|
|
54
|
+
# @rbs read_timeout: Numeric
|
|
55
|
+
# @rbs stream_idle_timeout: Numeric
|
|
48
56
|
# @rbs return: void
|
|
49
|
-
def initialize: (url: String, ?format: Zizq::format, ?ssl_context: OpenSSL::SSL::SSLContext?) -> void
|
|
57
|
+
def initialize: (url: String, ?format: Zizq::format, ?ssl_context: OpenSSL::SSL::SSLContext?, ?read_timeout: Numeric, ?stream_idle_timeout: Numeric) -> void
|
|
50
58
|
|
|
51
59
|
# Close all thread-local HTTP clients and release connections.
|
|
52
60
|
def close: () -> untyped
|
|
@@ -22,18 +22,26 @@ module Zizq
|
|
|
22
22
|
# Logger instance to which to write log messages.
|
|
23
23
|
attr_accessor logger: Logger
|
|
24
24
|
|
|
25
|
-
#
|
|
25
|
+
# Per-operation socket I/O timeout (seconds) for regular API calls
|
|
26
|
+
# (enqueue, queries, mutations). Each socket read/write is bounded
|
|
27
|
+
# by this value. A request whose handshake or any single read exceeds
|
|
28
|
+
# this raises `IO::TimeoutError`.
|
|
26
29
|
#
|
|
27
|
-
#
|
|
30
|
+
# Default: 30.
|
|
31
|
+
attr_accessor read_timeout: Numeric
|
|
32
|
+
|
|
33
|
+
# Per-operation socket I/O timeout (seconds) for the long-lived
|
|
34
|
+
# `#take_jobs` stream. The server sends heartbeats every ~3 seconds,
|
|
35
|
+
# so each read returns within that window and keeps the connection
|
|
36
|
+
# alive; the connection only times out if the server falls silent for
|
|
37
|
+
# longer than this. The Worker catches the resulting error and
|
|
38
|
+
# reconnects with backoff.
|
|
28
39
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# client_cert: "path/to/client-cert.pem", # Client certificate for mTLS
|
|
32
|
-
# client_key: "path/to/client-key.pem", # Client private key for mTLS
|
|
33
|
-
# }
|
|
40
|
+
# Should be comfortably above the server's heartbeat interval to
|
|
41
|
+
# avoid false-positive disconnects.
|
|
34
42
|
#
|
|
35
|
-
#
|
|
36
|
-
attr_accessor
|
|
43
|
+
# Default: 30.
|
|
44
|
+
attr_accessor stream_idle_timeout: Numeric
|
|
37
45
|
|
|
38
46
|
# Middleware chain for enqueue. Each middleware receives an
|
|
39
47
|
# `EnqueueRequest` and a chain to continue.
|
|
@@ -45,6 +53,42 @@ module Zizq
|
|
|
45
53
|
|
|
46
54
|
def initialize: () -> untyped
|
|
47
55
|
|
|
56
|
+
# TLS settings for connecting to the server over HTTPS.
|
|
57
|
+
#
|
|
58
|
+
# Configure via the `c.tls` accessors inside a `Zizq.configure`
|
|
59
|
+
# block:
|
|
60
|
+
#
|
|
61
|
+
# Zizq.configure do |c|
|
|
62
|
+
# c.tls.ca = "/path/to/server-ca-cert.pem"
|
|
63
|
+
# c.tls.client_cert = "/path/to/client-cert.pem"
|
|
64
|
+
# c.tls.client_key = "/path/to/client-key.pem"
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# All values may be PEM-encoded strings or file paths. Set
|
|
68
|
+
# `c.tls = nil` to explicitly disable TLS.
|
|
69
|
+
#
|
|
70
|
+
# Note: Mutual TLS support requires a Zizq Pro license on the
|
|
71
|
+
# server.
|
|
72
|
+
def tls: () -> untyped
|
|
73
|
+
|
|
74
|
+
def tls=: (untyped value) -> untyped
|
|
75
|
+
|
|
76
|
+
# Defaults for `Zizq::Worker` instances. Apps populate this in
|
|
77
|
+
# their `Zizq.configure` block:
|
|
78
|
+
#
|
|
79
|
+
# Zizq.configure do |c|
|
|
80
|
+
# c.worker.queues = ["emails"]
|
|
81
|
+
# c.worker.fiber_count = 25
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# Anything left unset here falls through to the Worker's
|
|
85
|
+
# hardcoded defaults; anything explicitly passed to `Worker.new`
|
|
86
|
+
# (or set via `zizq-worker` CLI flags / env vars) overrides
|
|
87
|
+
# whatever is configured here.
|
|
88
|
+
def worker: () -> untyped
|
|
89
|
+
|
|
90
|
+
def worker=: (untyped value) -> untyped
|
|
91
|
+
|
|
48
92
|
# The job dispatcher.
|
|
49
93
|
# This is the terminal of the dequeue middleware chain.
|
|
50
94
|
# Defaults to `Zizq::Job` which finds and executes jobs written by mixing
|
|
@@ -79,8 +123,7 @@ module Zizq
|
|
|
79
123
|
|
|
80
124
|
private
|
|
81
125
|
|
|
82
|
-
|
|
83
|
-
def validate_tls!: (Zizq::tls_options tls) -> untyped
|
|
126
|
+
def validate_tls!: (untyped tls) -> untyped
|
|
84
127
|
|
|
85
128
|
# Load a certificate from a PEM string or file path.
|
|
86
129
|
def load_cert: (untyped pem_or_path) -> untyped
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Generated from lib/zizq/tls_configuration.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Zizq
|
|
4
|
+
# TLS settings for connecting to the Zizq server over HTTPS.
|
|
5
|
+
#
|
|
6
|
+
# Set inside a `Zizq.configure` block via the `c.tls` accessors:
|
|
7
|
+
#
|
|
8
|
+
# Zizq.configure do |c|
|
|
9
|
+
# c.tls.ca = "/path/to/ca-cert.pem"
|
|
10
|
+
# c.tls.client_cert = "/path/to/client-cert.pem"
|
|
11
|
+
# c.tls.client_key = "/path/to/client-key.pem"
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# All values may be PEM-encoded strings or file paths.
|
|
15
|
+
#
|
|
16
|
+
# Note: Mutual TLS support requires a Zizq Pro license on the server.
|
|
17
|
+
class TlsConfiguration < Struct[String?]
|
|
18
|
+
attr_accessor ca(): String?
|
|
19
|
+
|
|
20
|
+
attr_accessor client_cert(): String?
|
|
21
|
+
|
|
22
|
+
attr_accessor client_key(): String?
|
|
23
|
+
|
|
24
|
+
def self.new: (?ca: String?, ?client_cert: String?, ?client_key: String?) -> instance
|
|
25
|
+
| ({ ?ca: String?, ?client_cert: String?, ?client_key: String? }) -> instance
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -46,11 +46,6 @@ module Zizq
|
|
|
46
46
|
# pipeline full while ack round-trips are in flight.
|
|
47
47
|
attr_reader prefetch: Integer
|
|
48
48
|
|
|
49
|
-
# Proc to derive a worker ID string for each thread and fiber.
|
|
50
|
-
#
|
|
51
|
-
# When not present, the Zizq server assigns a random worker ID.
|
|
52
|
-
attr_reader worker_id_proc: (^(Integer, Integer) -> String?)?
|
|
53
|
-
|
|
54
49
|
# An instance of a Logger to be used for worker logging.
|
|
55
50
|
attr_reader logger: Logger
|
|
56
51
|
|
|
@@ -62,18 +57,25 @@ module Zizq
|
|
|
62
57
|
# their own `Zizq::Middleware::Chain` if middleware needs to be applied.
|
|
63
58
|
attr_reader dispatcher: ^(Resources::Job) -> void
|
|
64
59
|
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
60
|
+
# All keyword arguments default to `nil` and follow a three-level
|
|
61
|
+
# fallback chain:
|
|
62
|
+
#
|
|
63
|
+
# 1. Explicit kwarg passed to `Worker.new`.
|
|
64
|
+
# 2. `Zizq.configuration.worker.<field>` set in the app's
|
|
65
|
+
# `Zizq.configure` block.
|
|
66
|
+
# 3. The Worker's hardcoded `DEFAULT_*` constants.
|
|
67
|
+
#
|
|
68
|
+
# @rbs queues: Array[String]?
|
|
69
|
+
# @rbs thread_count: Integer?
|
|
70
|
+
# @rbs fiber_count: Integer?
|
|
68
71
|
# @rbs prefetch: Integer?
|
|
69
|
-
# @rbs retry_min_wait: (Float | Integer)
|
|
70
|
-
# @rbs retry_max_wait: (Float | Integer)
|
|
71
|
-
# @rbs retry_multiplier: (Float | Integer)
|
|
72
|
-
# @rbs worker_id: (^(Integer, Integer) -> String?)?
|
|
72
|
+
# @rbs retry_min_wait: (Float | Integer)?
|
|
73
|
+
# @rbs retry_max_wait: (Float | Integer)?
|
|
74
|
+
# @rbs retry_multiplier: (Float | Integer)?
|
|
73
75
|
# @rbs logger: Logger?
|
|
74
76
|
# @rbs dispatcher: (^(Resources::Job) -> void)?
|
|
75
77
|
# @rbs return: void
|
|
76
|
-
def initialize: (?queues: Array[String]
|
|
78
|
+
def initialize: (?queues: Array[String]?, ?thread_count: Integer?, ?fiber_count: Integer?, ?prefetch: Integer?, ?retry_min_wait: (Float | Integer)?, ?retry_max_wait: (Float | Integer)?, ?retry_multiplier: (Float | Integer)?, ?logger: Logger?, ?dispatcher: (^(Resources::Job) -> void)?) -> void
|
|
77
79
|
|
|
78
80
|
# Request a graceful shutdown.
|
|
79
81
|
#
|
|
@@ -136,7 +138,7 @@ module Zizq
|
|
|
136
138
|
#
|
|
137
139
|
# Delegates to the configured dispatcher (default: `Zizq::Job.dispatch`)
|
|
138
140
|
# and reports success or failure.
|
|
139
|
-
def dispatch: (untyped job
|
|
141
|
+
def dispatch: (untyped job) -> untyped
|
|
140
142
|
|
|
141
143
|
# @rbs job_id: String
|
|
142
144
|
# @rbs return: void
|
|
@@ -146,7 +148,5 @@ module Zizq
|
|
|
146
148
|
# @rbs error: Exception
|
|
147
149
|
# @rbs return: void
|
|
148
150
|
def push_nack: (String job_id, Exception error) -> void
|
|
149
|
-
|
|
150
|
-
def resolve_worker_id: (untyped thread_idx, untyped fiber_idx) -> untyped
|
|
151
151
|
end
|
|
152
152
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Generated from lib/zizq/worker_configuration.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Zizq
|
|
4
|
+
# Defaults for `Zizq::Worker` instances. Accessed via
|
|
5
|
+
# `Zizq.configuration.worker` and typically populated inside an
|
|
6
|
+
# application's `Zizq.configure` block:
|
|
7
|
+
#
|
|
8
|
+
# Zizq.configure do |c|
|
|
9
|
+
# c.url = "https://..."
|
|
10
|
+
# c.worker.queues = ["emails", "webhooks"]
|
|
11
|
+
# c.worker.thread_count = 1
|
|
12
|
+
# c.worker.fiber_count = 25
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# Every field defaults to `nil`, meaning "use the Worker's own
|
|
16
|
+
# hardcoded default." Anything explicitly passed to `Worker.new` —
|
|
17
|
+
# or set via CLI flag / env var when launching `zizq-worker` —
|
|
18
|
+
# overrides whatever is set here.
|
|
19
|
+
#
|
|
20
|
+
# See `Zizq::Worker#initialize` for the full resolution order
|
|
21
|
+
# (explicit kwarg → Zizq.configuration.worker → `Worker::DEFAULT_*`).
|
|
22
|
+
#
|
|
23
|
+
# Fields:
|
|
24
|
+
#
|
|
25
|
+
# * `queues` — Queues to consume. `[]` means all queues.
|
|
26
|
+
# * `thread_count` — Number of worker threads.
|
|
27
|
+
# * `fiber_count` — Number of fibers per worker thread.
|
|
28
|
+
# * `prefetch` — Server-side prefetch limit. Defaults to
|
|
29
|
+
# `2 * threads * fibers`.
|
|
30
|
+
# * `retry_min_wait` — Minimum reconnect backoff in seconds.
|
|
31
|
+
# * `retry_max_wait` — Maximum reconnect backoff in seconds.
|
|
32
|
+
# * `retry_multiplier` — Multiplicative backoff factor between
|
|
33
|
+
# reconnect attempts.
|
|
34
|
+
class WorkerConfiguration < Struct[Array[String]? | Integer? | (Float | Integer)?]
|
|
35
|
+
attr_accessor queues(): Array[String]?
|
|
36
|
+
|
|
37
|
+
attr_accessor thread_count(): Integer?
|
|
38
|
+
|
|
39
|
+
attr_accessor fiber_count(): Integer?
|
|
40
|
+
|
|
41
|
+
attr_accessor prefetch(): Integer?
|
|
42
|
+
|
|
43
|
+
attr_accessor retry_min_wait(): (Float | Integer)?
|
|
44
|
+
|
|
45
|
+
attr_accessor retry_max_wait(): (Float | Integer)?
|
|
46
|
+
|
|
47
|
+
attr_accessor retry_multiplier(): (Float | Integer)?
|
|
48
|
+
|
|
49
|
+
def self.new: (?queues: Array[String]?, ?thread_count: Integer?, ?fiber_count: Integer?, ?prefetch: Integer?, ?retry_min_wait: (Float | Integer)?, ?retry_max_wait: (Float | Integer)?, ?retry_multiplier: (Float | Integer)?) -> instance
|
|
50
|
+
| ({ ?queues: Array[String]?, ?thread_count: Integer?, ?fiber_count: Integer?, ?prefetch: Integer?, ?retry_min_wait: (Float | Integer)?, ?retry_max_wait: (Float | Integer)?, ?retry_multiplier: (Float | Integer)? }) -> instance
|
|
51
|
+
end
|
|
52
|
+
end
|
data/sig/zizq.rbs
CHANGED
|
@@ -67,7 +67,8 @@ module Zizq
|
|
|
67
67
|
?paused: bool?
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
# TLS options
|
|
70
|
+
# TLS options hash accepted by `Configuration#tls=` for back-compat.
|
|
71
|
+
# Values are PEM strings or file paths.
|
|
71
72
|
type tls_options = { ?ca: String, ?client_cert: String, ?client_key: String }
|
|
72
73
|
|
|
73
74
|
# Any object that can dispatch a job. Must respond to #call(job).
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zizq
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Corbyn <chris@zizq.io>
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: async-http
|
|
@@ -38,11 +38,27 @@ dependencies:
|
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '1.7'
|
|
41
|
-
description:
|
|
41
|
+
description: |-
|
|
42
42
|
This is the official Ruby client for the Zizq job queue server.
|
|
43
43
|
|
|
44
44
|
Zizq is a simple, single binary, zero dependency, language agnostic job queue.
|
|
45
45
|
|
|
46
|
+
Features:
|
|
47
|
+
|
|
48
|
+
- Enqueue and process jobs across programming languages
|
|
49
|
+
- Persistent/journalled
|
|
50
|
+
- Multi-thread and/or multi-fiber
|
|
51
|
+
- Scheduled jobs
|
|
52
|
+
- Prioritized queues
|
|
53
|
+
- Optional ActiveJob integration
|
|
54
|
+
- Unique jobs
|
|
55
|
+
- Cron scheduling (recurring jobs)
|
|
56
|
+
- Job introspection and management, including `jq` filters
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
This client supports multi-threaded and/or multi-fiber concurrency and is very fast. The Zizq server provides everything needed. There are no separate external storage dependencies to configure such as Redis or a RDBMS.
|
|
60
|
+
|
|
61
|
+
See https://zizq.io for full details and documentation.
|
|
46
62
|
email:
|
|
47
63
|
executables:
|
|
48
64
|
- zizq-worker
|
|
@@ -84,8 +100,10 @@ files:
|
|
|
84
100
|
- lib/zizq/resources/job_template.rb
|
|
85
101
|
- lib/zizq/resources/page.rb
|
|
86
102
|
- lib/zizq/resources/resource.rb
|
|
103
|
+
- lib/zizq/tls_configuration.rb
|
|
87
104
|
- lib/zizq/version.rb
|
|
88
105
|
- lib/zizq/worker.rb
|
|
106
|
+
- lib/zizq/worker_configuration.rb
|
|
89
107
|
- sig/generated/zizq.rbs
|
|
90
108
|
- sig/generated/zizq/ack_processor.rbs
|
|
91
109
|
- sig/generated/zizq/active_job_config.rbs
|
|
@@ -115,8 +133,10 @@ files:
|
|
|
115
133
|
- sig/generated/zizq/resources/job_template.rbs
|
|
116
134
|
- sig/generated/zizq/resources/page.rbs
|
|
117
135
|
- sig/generated/zizq/resources/resource.rbs
|
|
136
|
+
- sig/generated/zizq/tls_configuration.rbs
|
|
118
137
|
- sig/generated/zizq/version.rbs
|
|
119
138
|
- sig/generated/zizq/worker.rbs
|
|
139
|
+
- sig/generated/zizq/worker_configuration.rbs
|
|
120
140
|
- sig/zizq.rbs
|
|
121
141
|
homepage: https://zizq.io
|
|
122
142
|
licenses:
|
|
@@ -145,4 +165,3 @@ signing_key:
|
|
|
145
165
|
specification_version: 4
|
|
146
166
|
summary: The official Ruby client for the Zizq job queue
|
|
147
167
|
test_files: []
|
|
148
|
-
...
|