zizq 0.3.1 → 0.3.3

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: 8f79a2e4601f42c8c9334601383a3659ffa5063a5df3ce2575b1d56b4df17a89
4
- data.tar.gz: 730dbf9a98a6bb877234f3821e099e0363c5ebfad4905622106fa19b17db6610
3
+ metadata.gz: 4ab68c867c5342352c99cccc1c244215a12256c8957be422c38c1446fbcc317e
4
+ data.tar.gz: 58651399c191747b0e1fab0b0751e53de4eb415dcb0ae5845b76eb35590cb5eb
5
5
  SHA512:
6
- metadata.gz: 49f4a260cfbc35570e10e8fa0811e922be0c826ac0126f20e530353ae4c58df13e5f1cf8637d0a2f7a799ec3a0c64d9082c1a66d859de21b748db05e2db22802
7
- data.tar.gz: 0e09b0848550330989701c703c5640e3df46e9e8a4229e3e15de99f033975356e24abee2e99fe537ed5b80c8a6edbfc9e302b518844ca9a6d99ce60766af5ccb
6
+ metadata.gz: 4b35bd73a1e4c8c66113b7f1b0c4efbb1eef97e0b304730510a76a35498e6fcc38c15016b53b7507206e558535d07b1a0dbd377d7e5472247f9fffd5ac5a79ec
7
+ data.tar.gz: 6fbdbe994579189e68bd37e5ce4167a4c074593609cb7938f411e956c073b11d43c0863e8c8038bb2e77cfe0246263e3c7b55e83d3961f5da99e133164da766a
data/README.md CHANGED
@@ -1,11 +1,11 @@
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
  [![CI](https://github.com/zizq-labs/zizq-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/zizq-labs/zizq-ruby/actions/workflows/ci.yml)
10
10
  [![Gem Version](https://img.shields.io/gem/v/zizq.svg)](https://rubygems.org/gems/zizq)
11
11
 
@@ -32,13 +32,13 @@ This is the official Zizq client library for Ruby.
32
32
  Add it to your application's `Gemfile`:
33
33
 
34
34
  ```ruby
35
- gem 'zizq', '~> 0.3.1'
35
+ gem 'zizq', '~> 0.3.3'
36
36
  ```
37
37
 
38
38
  Or install it manually:
39
39
 
40
40
  ```shell
41
- $ gem install zizq -v 0.3.1
41
+ $ gem install zizq -v 0.3.3
42
42
  ```
43
43
 
44
44
  Ruby **3.2.8 or newer** is required. Client and server share version
@@ -55,12 +55,19 @@ require 'zizq'
55
55
 
56
56
  Zizq.configure do |c|
57
57
  c.url = 'https://zizq.your.network:7890'
58
- c.tls = { ca: '/path/to/server-ca-cert.pem' }
59
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
60
67
  end
61
68
  ```
62
69
 
63
- For mutual TLS, add `client_cert:` and `client_key:` to the `tls` hash.
70
+ For mutual TLS, also set `c.tls.client_cert` and `c.tls.client_key`.
64
71
 
65
72
  > [!CAUTION]
66
73
  > If your server is exposed directly to the internet, it should require
@@ -149,20 +156,29 @@ end
149
156
  ### Running a worker
150
157
 
151
158
  Jobs are processed by a worker, typically in a separate process. The simplest
152
- way is the `zizq-worker` executable bundled with the gem pass your
153
- application's entrypoint so it can load your job classes:
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:
154
162
 
155
163
  ```shell
156
- $ bundle exec zizq-worker --threads 5 --fibers 2 config/environment.rb
157
- I, [...] INFO -- : Zizq worker starting: 5 threads, 2 fibers, prefetch=20
164
+ $ bundle exec zizq-worker
165
+ I, [...] INFO -- : Zizq worker starting: 1 threads, 25 fibers, prefetch=50
158
166
  I, [...] INFO -- : Connected. Listening for jobs.
159
167
  ```
160
168
 
161
- A worker runs `threads × fibers` handlers concurrently. Leave `--fibers 1`
162
- if your application isn't fiber-safe — no `Async` context is loaded in that
163
- case. Restrict to specific queues with `--queue`. `INT` / `TERM` trigger a
164
- graceful shutdown (drains in-flight jobs up to `--shutdown-timeout`, default
165
- 30s).
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).
166
182
 
167
183
  For more control — for example running the worker in-process alongside a
168
184
  Rack app — construct `Zizq::Worker` directly:
@@ -170,11 +186,9 @@ Rack app — construct `Zizq::Worker` directly:
170
186
  ```ruby
171
187
  require 'zizq'
172
188
 
173
- worker = Zizq::Worker.new(
174
- thread_count: 5,
175
- fiber_count: 2,
176
- queues: ['emails', 'payments'],
177
- )
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'])
178
192
 
179
193
  Signal.trap('INT') { worker.stop }
180
194
  worker.run # blocks until the worker stops
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, falling back to hardcoded defaults ---
15
-
16
- thread_count = Integer(ENV.fetch("ZIZQ_THREADS", Zizq::Worker::DEFAULT_THREADS))
17
- fiber_count = Integer(ENV.fetch("ZIZQ_FIBERS", Zizq::Worker::DEFAULT_FIBERS))
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 = Float(ENV.fetch("ZIZQ_RETRY_MIN_WAIT", Zizq::Worker::DEFAULT_RETRY_MIN_WAIT))
21
- retry_max_wait = Float(ENV.fetch("ZIZQ_RETRY_MAX_WAIT", Zizq::Worker::DEFAULT_RETRY_MAX_WAIT))
22
- retry_multiplier = Float(ENV.fetch("ZIZQ_RETRY_MULTIPLIER", Zizq::Worker::DEFAULT_RETRY_MULTIPLIER))
23
-
24
- queues = if ENV.key?("ZIZQ_QUEUES")
25
- ENV["ZIZQ_QUEUES"].split(",").map(&:strip).reject(&:empty?)
26
- else
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] <ENTRYPOINT>"
44
+ opts.banner = "Usage: zizq-worker [OPTIONS] [ENTRYPOINT]"
34
45
 
35
46
  opts.separator ""
36
- opts.separator "Start a Zizq worker process. The ENTRYPOINT is a Ruby file that loads your"
37
- opts.separator "application (e.g. config/environment.rb for a Rails app)."
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 "Client configuration (url, format, logger) should be set in the"
40
- opts.separator "entrypoint via Zizq.configure."
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 -q flag replaces the env var default entirely
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
- # --- Validate and load entrypoint ---
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: missing required ENTRYPOINT argument."
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
- # --- Start the worker ---
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 = Zizq::Worker.new(
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(1)
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(1)
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
@@ -4,6 +4,7 @@
4
4
  # rbs_inline: enabled
5
5
  # frozen_string_literal: true
6
6
 
7
+ require "delegate"
7
8
  require "logger"
8
9
  require "openssl"
9
10
 
@@ -29,19 +30,6 @@ module Zizq
29
30
  # Logger instance to which to write log messages.
30
31
  attr_accessor :logger #: Logger
31
32
 
32
- # TLS options for connecting to the server over HTTPS.
33
- #
34
- # All values may be PEM-encoded strings or file paths.
35
- #
36
- # {
37
- # ca: "path/to/ca-cert.pem", # CA certificate for server verification
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
- # }
41
- #
42
- # Note: Mutual TLS support requires a Zizq Pro license on the server.
43
- attr_accessor :tls #: Zizq::tls_options?
44
-
45
33
  # Per-operation socket I/O timeout (seconds) for regular API calls
46
34
  # (enqueue, queries, mutations). Each socket read/write is bounded
47
35
  # by this value. A request whose handshake or any single read exceeds
@@ -74,14 +62,81 @@ module Zizq
74
62
  def initialize #: () -> void
75
63
  @url = "http://localhost:7890"
76
64
  @format = :msgpack
77
- @logger = Logger.new($stdout, level: Logger::INFO)
65
+ @logger = Logger.new(FlushingIO.new($stdout), level: Logger::INFO)
78
66
  @tls = nil
67
+ @worker = nil
79
68
  @read_timeout = 30
80
69
  @stream_idle_timeout = 30
81
70
  @enqueue_middleware = Middleware::Chain.new(Identity.new)
82
71
  @dequeue_middleware = Middleware::Chain.new(Zizq::Job)
83
72
  end
84
73
 
74
+ # TLS settings for connecting to the server over HTTPS.
75
+ #
76
+ # Configure via the `c.tls` accessors inside a `Zizq.configure`
77
+ # block:
78
+ #
79
+ # Zizq.configure do |c|
80
+ # c.tls.ca = "/path/to/server-ca-cert.pem"
81
+ # c.tls.client_cert = "/path/to/client-cert.pem"
82
+ # c.tls.client_key = "/path/to/client-key.pem"
83
+ # end
84
+ #
85
+ # All values may be PEM-encoded strings or file paths. Set
86
+ # `c.tls = nil` to explicitly disable TLS.
87
+ #
88
+ # Note: Mutual TLS support requires a Zizq Pro license on the
89
+ # server.
90
+ def tls #: () -> TlsConfiguration
91
+ @tls ||= TlsConfiguration.new
92
+ end
93
+
94
+ def tls=(value) #: ((Hash[Symbol, String?] | TlsConfiguration)?) -> void
95
+ case value
96
+ when nil
97
+ @tls = nil
98
+ when TlsConfiguration
99
+ @tls = value
100
+ when Hash
101
+ @tls = TlsConfiguration.new(**value)
102
+ else
103
+ raise ArgumentError,
104
+ "Zizq.configure: tls= expects a Hash, Zizq::TlsConfiguration, or nil " \
105
+ "(got #{value.class})"
106
+ end
107
+ end
108
+
109
+ # Defaults for `Zizq::Worker` instances. Apps populate this in
110
+ # their `Zizq.configure` block:
111
+ #
112
+ # Zizq.configure do |c|
113
+ # c.worker.queues = ["emails"]
114
+ # c.worker.fiber_count = 25
115
+ # end
116
+ #
117
+ # Anything left unset here falls through to the Worker's
118
+ # hardcoded defaults; anything explicitly passed to `Worker.new`
119
+ # (or set via `zizq-worker` CLI flags / env vars) overrides
120
+ # whatever is configured here.
121
+ def worker #: () -> WorkerConfiguration
122
+ @worker ||= WorkerConfiguration.new
123
+ end
124
+
125
+ def worker=(value) #: ((Hash[Symbol, untyped] | WorkerConfiguration)?) -> void
126
+ case value
127
+ when nil
128
+ @worker = nil
129
+ when WorkerConfiguration
130
+ @worker = value
131
+ when Hash
132
+ @worker = WorkerConfiguration.new(**value)
133
+ else
134
+ raise ArgumentError,
135
+ "Zizq.configure: worker= expects a Hash, Zizq::WorkerConfiguration, or nil " \
136
+ "(got #{value.class})"
137
+ end
138
+ end
139
+
85
140
  # The job dispatcher.
86
141
  # This is the terminal of the dequeue middleware chain.
87
142
  # Defaults to `Zizq::Job` which finds and executes jobs written by mixing
@@ -130,21 +185,22 @@ module Zizq
130
185
  def ssl_context #: () -> OpenSSL::SSL::SSLContext?
131
186
  tls = @tls
132
187
  return nil unless tls
188
+ return nil if tls.to_h.values.all?(&:nil?)
133
189
 
134
190
  ctx = OpenSSL::SSL::SSLContext.new
135
191
 
136
- if (ca = tls[:ca])
192
+ if (ca = tls.ca)
137
193
  store = OpenSSL::X509::Store.new
138
194
  store.add_cert(load_cert(ca))
139
195
  ctx.cert_store = store
140
196
  ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
141
197
  end
142
198
 
143
- if (client_cert = tls[:client_cert])
199
+ if (client_cert = tls.client_cert)
144
200
  ctx.cert = load_cert(client_cert)
145
201
  end
146
202
 
147
- if (client_key = tls[:client_key])
203
+ if (client_key = tls.client_key)
148
204
  ctx.key = load_key(client_key)
149
205
  end
150
206
 
@@ -159,16 +215,40 @@ module Zizq
159
215
  end
160
216
  end
161
217
 
218
+ # @private
219
+ # IO delegator that flushes the underlying IO after every write.
220
+ # We wrap `$stdout` in this for the default logger so log lines
221
+ # appear immediately when stdout is connected to a pipe (foreman,
222
+ # systemd, k8s). Without it, the C-stdio default switches from
223
+ # line-buffered to fully-buffered for pipes and log output piles
224
+ # up in a 4–8KB buffer until the process exits.
225
+ #
226
+ # The wrapper is local to the default logger — apps supplying
227
+ # their own `c.logger = ...` retain full control over flushing.
228
+ class FlushingIO < SimpleDelegator
229
+ def write(*args) #: (*untyped) -> Integer
230
+ result = super
231
+ __getobj__.flush
232
+ result
233
+ end
234
+
235
+ # Declared explicitly so the type checker sees us satisfying
236
+ # `Logger::_WriteCloser`; SimpleDelegator forwards `close` via
237
+ # `method_missing`, but Steep doesn't follow that.
238
+ def close #: () -> void
239
+ __getobj__.close
240
+ end
241
+ end
242
+
162
243
  private
163
244
 
164
- # @rbs tls: Zizq::tls_options
165
- def validate_tls!(tls) #: (Zizq::tls_options) -> void
166
- if tls[:client_cert] && !tls[:client_key]
167
- raise ArgumentError, "Zizq.configure: tls[:client_key] is required when tls[:client_cert] is set"
245
+ def validate_tls!(tls) #: (TlsConfiguration) -> void
246
+ if tls.client_cert && !tls.client_key
247
+ raise ArgumentError, "Zizq.configure: tls.client_key is required when tls.client_cert is set"
168
248
  end
169
249
 
170
- if tls[:client_key] && !tls[:client_cert]
171
- raise ArgumentError, "Zizq.configure: tls[:client_cert] is required when tls[:client_key] is set"
250
+ if tls.client_key && !tls.client_cert
251
+ raise ArgumentError, "Zizq.configure: tls.client_cert is required when tls.client_key is set"
172
252
  end
173
253
  end
174
254
 
@@ -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
@@ -5,5 +5,5 @@
5
5
  # frozen_string_literal: true
6
6
 
7
7
  module Zizq
8
- VERSION = "0.3.1" #: String
8
+ VERSION = "0.3.3" #: String
9
9
  end
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 = 5 #: Integer
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
- # @rbs queues: Array[String]
70
- # @rbs thread_count: Integer
71
- # @rbs fiber_count: Integer
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: DEFAULT_THREADS,
83
- fiber_count: DEFAULT_FIBERS,
83
+ queues: nil,
84
+ thread_count: nil,
85
+ fiber_count: nil,
84
86
  prefetch: nil,
85
- retry_min_wait: DEFAULT_RETRY_MIN_WAIT,
86
- retry_max_wait: DEFAULT_RETRY_MAX_WAIT,
87
- retry_multiplier: DEFAULT_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
- @queues = queues
98
- @thread_count = thread_count
99
- @fiber_count = fiber_count
100
- @prefetch = prefetch || thread_count * fiber_count * 2
101
- @retry_min_wait = retry_min_wait
102
- @retry_max_wait = retry_max_wait
103
- @retry_multiplier = retry_multiplier
104
- @worker_id_proc = worker_id
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, wid)
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, worker_id) #: (Resources::Job, String?) -> void
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.
@@ -22,19 +22,6 @@ module Zizq
22
22
  # Logger instance to which to write log messages.
23
23
  attr_accessor logger: Logger
24
24
 
25
- # TLS options for connecting to the server over HTTPS.
26
- #
27
- # All values may be PEM-encoded strings or file paths.
28
- #
29
- # {
30
- # ca: "path/to/ca-cert.pem", # CA certificate for server verification
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
- # }
34
- #
35
- # Note: Mutual TLS support requires a Zizq Pro license on the server.
36
- attr_accessor tls: Zizq::tls_options?
37
-
38
25
  # Per-operation socket I/O timeout (seconds) for regular API calls
39
26
  # (enqueue, queries, mutations). Each socket read/write is bounded
40
27
  # by this value. A request whose handshake or any single read exceeds
@@ -66,6 +53,42 @@ module Zizq
66
53
 
67
54
  def initialize: () -> untyped
68
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
+
69
92
  # The job dispatcher.
70
93
  # This is the terminal of the dequeue middleware chain.
71
94
  # Defaults to `Zizq::Job` which finds and executes jobs written by mixing
@@ -98,10 +121,28 @@ module Zizq
98
121
  def call: (untyped arg) -> untyped
99
122
  end
100
123
 
124
+ # @private
125
+ # IO delegator that flushes the underlying IO after every write.
126
+ # We wrap `$stdout` in this for the default logger so log lines
127
+ # appear immediately when stdout is connected to a pipe (foreman,
128
+ # systemd, k8s). Without it, the C-stdio default switches from
129
+ # line-buffered to fully-buffered for pipes and log output piles
130
+ # up in a 4–8KB buffer until the process exits.
131
+ #
132
+ # The wrapper is local to the default logger — apps supplying
133
+ # their own `c.logger = ...` retain full control over flushing.
134
+ class FlushingIO < SimpleDelegator
135
+ def write: (*untyped args) -> untyped
136
+
137
+ # Declared explicitly so the type checker sees us satisfying
138
+ # `Logger::_WriteCloser`; SimpleDelegator forwards `close` via
139
+ # `method_missing`, but Steep doesn't follow that.
140
+ def close: () -> untyped
141
+ end
142
+
101
143
  private
102
144
 
103
- # @rbs tls: Zizq::tls_options
104
- def validate_tls!: (Zizq::tls_options tls) -> untyped
145
+ def validate_tls!: (untyped tls) -> untyped
105
146
 
106
147
  # Load a certificate from a PEM string or file path.
107
148
  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
- # @rbs queues: Array[String]
66
- # @rbs thread_count: Integer
67
- # @rbs fiber_count: Integer
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], ?thread_count: Integer, ?fiber_count: Integer, ?prefetch: Integer?, ?retry_min_wait: Float | Integer, ?retry_max_wait: Float | Integer, ?retry_multiplier: Float | Integer, ?worker_id: (^(Integer, Integer) -> String?)?, ?logger: Logger?, ?dispatcher: (^(Resources::Job) -> void)?) -> void
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, untyped worker_id) -> untyped
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 for connecting over HTTPS. Values are PEM strings or file paths.
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.1
4
+ version: 0.3.3
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-21 00:00:00.000000000 Z
11
+ date: 2026-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -100,8 +100,10 @@ files:
100
100
  - lib/zizq/resources/job_template.rb
101
101
  - lib/zizq/resources/page.rb
102
102
  - lib/zizq/resources/resource.rb
103
+ - lib/zizq/tls_configuration.rb
103
104
  - lib/zizq/version.rb
104
105
  - lib/zizq/worker.rb
106
+ - lib/zizq/worker_configuration.rb
105
107
  - sig/generated/zizq.rbs
106
108
  - sig/generated/zizq/ack_processor.rbs
107
109
  - sig/generated/zizq/active_job_config.rbs
@@ -131,8 +133,10 @@ files:
131
133
  - sig/generated/zizq/resources/job_template.rbs
132
134
  - sig/generated/zizq/resources/page.rbs
133
135
  - sig/generated/zizq/resources/resource.rbs
136
+ - sig/generated/zizq/tls_configuration.rbs
134
137
  - sig/generated/zizq/version.rbs
135
138
  - sig/generated/zizq/worker.rbs
139
+ - sig/generated/zizq/worker_configuration.rbs
136
140
  - sig/zizq.rbs
137
141
  homepage: https://zizq.io
138
142
  licenses: