rollbridge 0.1.23 → 0.1.25

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.
package/AGENTS.md ADDED
@@ -0,0 +1,55 @@
1
+ # Rollbridge contributor guidance
2
+
3
+ Rollbridge is the process supervisor and local traffic switcher. Keep deployment
4
+ tool concerns outside this repository: Rampway owns activation transactions,
5
+ deploy locks, release-retention metadata, and on-disk cleanup; Rollbridge owns
6
+ process lifecycle, retained generations, ports, and recovery state.
7
+
8
+ ## Background-jobs lifecycle invariant
9
+
10
+ - One runtime generation is one release-scoped `background-jobs-main` plus its
11
+ worker pool. Start the complete candidate generation before activation.
12
+ - After activation, retire the old generation as one unit. Its main stops
13
+ schedules, new dispatch, and new ordinary worker handoffs; its workers stop
14
+ accepting handoffs. The old main remains running with those workers and owns
15
+ their connections, lease fencing, report acceptance and acknowledgement, and
16
+ durable store transitions. The worker/reporting side durably retries terminal
17
+ reports, tracks outstanding report promises, enforces per-job execution
18
+ timeouts, and owns and reaps child runners. Main and workers remain one release
19
+ generation until every accepted handoff settles.
20
+ - Returned or retried work may be dispatched by the new active generation. A
21
+ retired main never dispatches it again. Old workers never reconnect or hand
22
+ over to the new main during a normal deploy.
23
+ - Jobs generations may overlap for hours on release-scoped endpoints. Beacon may
24
+ remain shared on `7330`; jobs-main must use a per-release port range.
25
+ - Deploy success is candidate activation and health. Deploy completion must not
26
+ wait for retired jobs generations, workers, jobs, HTTP/WebSocket connections,
27
+ or other retained services. HTTP and jobs drains are independent; an HTTP
28
+ drain finishing or timing out must not stop a live jobs generation.
29
+ - Persist and recover retired-generation ownership across later deploys and
30
+ supervisor/host recovery. Multiple generations may drain concurrently. Report
31
+ release references so the deployment tool can keep them pinned until no
32
+ retained process uses them.
33
+ - Runtime-owner replacement transfers or preserves this durable supervision and
34
+ returns once the replacement is healthy. It is never a synchronous full
35
+ shutdown. Per-job timeouts remain valid, but a normal worker-shutdown timeout
36
+ is not the deploy solution; legitimate multi-hour drains are valid.
37
+
38
+ Documentation must distinguish required architecture from behavior not yet
39
+ implemented. Do not claim production compliance when source/config still uses a
40
+ fixed jobs-main, worker adoption by a new main, destructive orphan recovery, or
41
+ synchronous cleanup.
42
+
43
+ ## Validation and publication
44
+
45
+ The project is ESM JavaScript with JSDoc type checking. Package scripts are the
46
+ source of truth: `npm run typecheck`, `npm run lint`, `npm test`, and the combined
47
+ `npm run all-checks`. Run focused checks for the files changed; documentation-only
48
+ work requires at least `git diff --check` plus any existing repository-owned
49
+ Markdown/link check, without installing dependencies.
50
+
51
+ Work on a feature branch and open a normal pull request against `master`; never
52
+ push feature work directly to `master`. Releases are maintainer-only and use
53
+ `npm run release:patch` from an up-to-date clean default branch after
54
+ `npm run all-checks`; do not edit package versions or publish during ordinary PR
55
+ work. See `docs/releasing.md` for the complete release checklist.
package/README.md CHANGED
@@ -2,7 +2,22 @@
2
2
 
3
3
  Rollbridge is a Node.js process supervisor and local traffic switcher for zero-downtime deploys.
4
4
 
5
- Nginx points at one stable Rollbridge proxy port. Deploy tooling asks Rollbridge to start a new release, health-check it, switch new traffic to it, and drain old HTTP/WebSocket connections before stopping the previous release.
5
+ Nginx points at one stable Rollbridge proxy port. Deploy tooling asks Rollbridge to start a new release, health-check it, and switch new traffic to it. Retirement then continues asynchronously: HTTP/WebSocket connections and retained process generations drain independently after the deploy command returns.
6
+
7
+ > **Required jobs-generation contract:** a release-scoped background-jobs
8
+ > runtime is its own `background-jobs-main` plus worker pool. Rollbridge starts a
9
+ > complete candidate generation before activation. After activation the retired
10
+ > main stops schedules, new dispatch, and new worker handoffs, but remains with
11
+ > its old workers to supervise every handoff it already made until all jobs and
12
+ > workers settle. Old workers never move to the new main. Generations may overlap
13
+ > for hours on separate ports, and neither deploy completion nor HTTP drain
14
+ > completion waits for or kills them. See [Background-job worker
15
+ > deployment](docs/workers.md) and the [Velocious deployment
16
+ > guide](docs/velocious.md).
17
+
18
+ This is the required architecture, not evidence that every released runtime or
19
+ consumer config already implements durable recovery and release-reference
20
+ reporting; verify source and config before claiming compliance.
6
21
 
7
22
  ## Install
8
23
 
@@ -47,23 +62,28 @@ export default {
47
62
  processes: [
48
63
  {
49
64
  id: "beacon",
50
- policy: "companion",
65
+ policy: "service",
51
66
  cwd: "{{releasePath}}",
52
67
  command: "env VELOCIOUS_BEACON_PORT={{port}} npx velocious beacon",
53
- port: {from: 17330, to: 17399}
68
+ port: 7330
54
69
  },
55
70
  {
56
71
  id: "background-jobs-worker",
57
72
  policy: "companion",
58
73
  cwd: "{{releasePath}}",
74
+ env: {VELOCIOUS_BACKGROUND_JOBS_PORT: "{{ports.background-jobs-main}}"},
59
75
  command: "npx velocious background-jobs-worker",
76
+ nonBlockingDrain: true,
77
+ gracefulStopMs: "indefinite",
60
78
  outputLines: 200
61
79
  },
62
80
  {
63
81
  id: "background-jobs-main",
64
82
  policy: "service",
83
+ deployStrategy: "handoff",
65
84
  cwd: "{{releasePath}}",
66
- command: "npx velocious background-jobs-main"
85
+ command: "env VELOCIOUS_BACKGROUND_JOBS_PORT={{port}} npx velocious background-jobs-main",
86
+ port: {from: 7331, to: 7399}
67
87
  },
68
88
  {
69
89
  id: "web",
@@ -118,10 +138,11 @@ See [`docs/config.md`](docs/config.md#processesmemory).
118
138
  memory: {limitBytes: 536870912, warnBytes: 402653184, checkIntervalMs: 5000}
119
139
  ```
120
140
 
121
- Set a process's `stopSignal` (default `"SIGTERM"`) to the signal it quiets on, so
122
- a worker finishes its in-flight work before exiting. Rollbridge sends `stopSignal`
123
- to gracefully stop the process and `SIGKILL`s it only if it hasn't exited within
124
- `gracefulStopMs`. For example, a job worker that drains on `SIGINT`:
141
+ Set a process's `stopSignal` (default `"SIGTERM"`) to the signal it quiets on.
142
+ This is a process-level stop mechanism, not the primary deploy-completion
143
+ mechanism for a release-scoped jobs generation. A retired generation may remain
144
+ for hours, and its old jobs-main must stay available until its workers finish.
145
+ For example, a generic worker that drains on `SIGINT`:
125
146
 
126
147
  ```js
127
148
  {id: "worker", policy: "companion", command: "…", stopSignal: "SIGINT", gracefulStopMs: 60000}
@@ -138,18 +159,19 @@ queue. See [`docs/config.md`](docs/config.md#processesreplicas).
138
159
  {id: "worker", policy: "companion", command: "npx velocious background-jobs-worker", replicas: 4}
139
160
  ```
140
161
 
141
- For workers that quiesce or drain via a command, set a `lifecycle` block —
162
+ For generic workers that quiesce or drain via a command, set a `lifecycle` block —
142
163
  Rollbridge runs `quietCommand`, then drains (`drainCommand`/`drainTimeoutMs`),
143
164
  then `stopCommand`/`stopSignal`, then `SIGKILL` after `gracefulStopMs` when
144
165
  gracefully stopping the process. Each hook is bounded so it can't wedge a stop.
145
166
 
146
- Set `nonBlockingDrain: true` on a worker companion to start its graceful stop the
147
- moment its release is retired in parallel with the proxied connection drain,
148
- not after it so new workers handle new work while the old workers finish theirs.
167
+ Set `nonBlockingDrain: true` on a jobs worker companion so it stops accepting new
168
+ handoffs as soon as its release retires, independently of the proxied connection
169
+ drain. Its release-scoped handoff jobs-main remains running to supervise existing
170
+ handoffs and exits only after the worker pool has drained.
149
171
 
150
- See [`docs/workers.md`](docs/workers.md) for the full safe background-job worker
151
- deployment pattern companion policy, `replicas`, and finishing in-flight jobs
152
- on deploy with `stopSignal`/`lifecycle` + `gracefulStopMs`.
172
+ See [`docs/workers.md`](docs/workers.md) for the full release-generation
173
+ deployment pattern: a handoff `background-jobs-main`, its companion worker pool,
174
+ independent quiescence, and durable supervision while retained generations drain.
153
175
 
154
176
  Set `releaseRetention` to bound how many stopped (drained) releases the daemon
155
177
  keeps in memory and reports in `status`. `keep` (default `10`) retains the most
@@ -270,8 +292,9 @@ A release-scoped helper (for example a background worker bound to one release).
270
292
  It starts **before** the proxied process in the same release, so release-local
271
293
  dependencies are ready before the health check, and it is auto-restarted while
272
294
  its release is active. Each release gets its own companions; a release's
273
- companions stop when that release is drained and retired after a newer release
274
- takes over.
295
+ companions normally stop when that release is drained and retired after a newer
296
+ release takes over. A jobs worker configured with `nonBlockingDrain` instead
297
+ quiesces at retirement and drains independently of HTTP/WebSocket connections.
275
298
 
276
299
  ```js
277
300
  {
@@ -279,7 +302,8 @@ takes over.
279
302
  policy: "companion",
280
303
  cwd: "{{releasePath}}",
281
304
  command: "npx velocious background-jobs-worker",
282
- gracefulStopMs: 60000
305
+ nonBlockingDrain: true,
306
+ gracefulStopMs: "indefinite"
283
307
  }
284
308
  ```
285
309
 
@@ -301,36 +325,36 @@ new copies simultaneously during a deploy would be unsafe.
301
325
 
302
326
  ### `service`
303
327
 
304
- A daemon-wide broker that should outlive individual releases — for example
305
- Velocious Beacon or `background-jobs-main`. Rollbridge starts it once (before
306
- release processes that depend on it), keeps it running across deploys, and gives
307
- it a stable port that does not change between releases. After each successful
308
- deploy its restart template is refreshed to the latest release, so if it crashes
309
- it restarts from the newest good release. It keeps restarting until the daemon
310
- shuts down.
328
+ A service can be daemon-wide and persistent, or release-scoped with
329
+ `deployStrategy: "handoff"`. Velocious Beacon is normally persistent on a stable
330
+ port. `background-jobs-main` is not: it must be a handoff service with one port
331
+ per release so old workers keep their old coordinator while new workers use the
332
+ candidate coordinator.
311
333
 
312
334
  ```js
313
335
  {
314
336
  id: "background-jobs-main",
315
337
  policy: "service",
338
+ deployStrategy: "handoff",
316
339
  cwd: "{{releasePath}}",
317
340
  command: "npx velocious background-jobs-main",
318
- port: 7331
341
+ port: {from: 7331, to: 7399}
319
342
  }
320
343
  ```
321
344
 
322
345
  ### Deploy ordering
323
346
 
324
- On `rollbridge deploy`, Rollbridge:
347
+ On `rollbridge deploy`, the required ordering is:
325
348
 
326
- 1. starts any `service` that is not already running;
349
+ 1. starts any missing persistent service and the candidate's handoff services;
327
350
  2. starts the new release's `companion`s, then its `proxied` process, and
328
351
  health-checks the proxied process;
329
352
  3. switches new traffic to the new release;
330
- 4. refreshes each `service`'s restart template to the new release;
353
+ 4. marks the previous jobs-main and worker pool retired as one generation;
331
354
  5. replaces `singleton`s (stops the old one, then starts the new one);
332
- 6. drains the previous release's connections, then stops its `proxied` and
333
- `companion` processes.
355
+ 6. returns success without waiting for the previous generation or its independent
356
+ HTTP/WebSocket drain; Rollbridge supervises all retained drains in the
357
+ background and reaps each generation only after its handoffs and workers end.
334
358
 
335
359
  If the new release fails to start or health-check, the previous release stays
336
360
  active and any service started during this deploy is rolled back.
@@ -413,13 +437,13 @@ rollbridge daemon --config /srv/ticket-server/rollbridge.js \
413
437
  ```
414
438
 
415
439
  External supervisors that need to replace a foreground owner without waiting
416
- for its workers to drain add `--takeover-owner`. The candidate starts and
417
- health-checks the exact release first. Only then does it retire the accepted
418
- owner's proxy/control listeners; the retired daemon keeps its already-accepted
419
- workers until their ordinary drain completes while the attested replacement
420
- binds the stable listeners. Candidate bootstrap failure leaves the accepted
421
- owner untouched. This is opt-in; ordinary daemon bootstrap and `shutdown` keep
422
- their existing behavior.
440
+ for retained generations to drain add `--takeover-owner`. The candidate starts
441
+ and health-checks the exact release first. Only then does it retire the accepted
442
+ owner's proxy/control listeners. Durable supervision of old jobs-main/worker
443
+ generations must be preserved or transferred while the attested replacement
444
+ binds the stable listeners; replacement does not mean full synchronous shutdown.
445
+ Candidate bootstrap failure leaves the accepted owner untouched. This is opt-in;
446
+ ordinary daemon bootstrap and `shutdown` keep their existing behavior.
423
447
 
424
448
  The four bootstrap inputs are all-or-nothing and use absolute config/release
425
449
  paths. Rollbridge binds its proxy, activates the release through the normal
@@ -600,7 +624,7 @@ daemon holds its log file open, so logrotate needs `copytruncate`.
600
624
 
601
625
  ## Deployment Notes
602
626
 
603
- Run migrations before `rollbridge deploy`, and keep migrations backwards-compatible while old and new web releases overlap. For stable local brokers such as Velocious Beacon or `background-jobs-main`, use `service` when the process should survive deploys and restart from the latest successful release if it crashes.
627
+ Run migrations before `rollbridge deploy`, and keep migrations backwards-compatible while old and new web and jobs generations overlap. Velocious Beacon may be a persistent fixed-port `service`; configure `background-jobs-main` as a release-scoped handoff service on a port range. A normal deploy may leave several retired generations draining concurrently.
604
628
 
605
629
  See [`docs/deploy-recipes.md`](docs/deploy-recipes.md) for ready-to-use shell, CI, and Capistrano recipes that drive Rollbridge through its CLI, and [`docs/troubleshooting.md`](docs/troubleshooting.md) for diagnosing health-check failures, port conflicts, stale sockets, crash loops, and stuck draining releases.
606
630
 
package/TODO.md CHANGED
@@ -30,11 +30,12 @@ This roadmap tracks planned Rollbridge features and documentation. Rollbridge sh
30
30
  - [x] Distinguish crash restarts, deploy replacements, manual restarts, and memory restarts in status/events. (Per-process `lastStartReason` + a `reason` on the `process started` event; the `memory` reason is wired and fires once memory supervision restarts a process.)
31
31
  - [x] Add a `restart` CLI command for a single process, a policy group, or all non-proxied workers.
32
32
  - [x] Keep restart behavior safe for job workers by using lifecycle hooks before termination. (Manual restart, memory restart, and deploy-drain stops all run the `lifecycle` hooks via `stop()`.)
33
- - [x] Graceful job-worker lifecycle.
33
+ - [x] Graceful process-stop controls for job workers.
34
34
  - [x] Add generic lifecycle hooks such as `quietCommand`, `drainCommand`, `drainTimeoutMs`, and `stopCommand` (per-process `lifecycle`).
35
35
  - [x] Support signal-only lifecycle steps for workers that can quiet on a Unix signal. (Per-process `stopSignal`; sent before the `SIGKILL`-after-`gracefulStopMs` fallback.)
36
- - [x] Add a non-blocking drain mode so new workers can start while old workers finish running jobs (per-process `nonBlockingDrain`; drains the worker in parallel with the connection drain).
37
- - [x] Document a Velocious background-jobs-worker recipe once the lifecycle contract is implemented (`docs/velocious.md` Worker recipe).
36
+ - [x] Add a non-blocking drain mode so a worker can quiesce at release retirement independently of the HTTP/WebSocket connection drain (`nonBlockingDrain`). This control alone does not provide durable retired-generation supervision.
37
+ - [x] Document the required Velocious release-generation contract (`docs/velocious.md` and `docs/workers.md`) without treating documentation as proof that the runtime implements it.
38
+ - [ ] Implement and verify durable release-scoped jobs-main retirement: owned-handoff supervision, recovery across daemon/host replacement, multiple concurrent retired generations, and release-reference reporting for cleanup pins.
38
39
  - [x] Replicas and stable worker indexes. (Supported on port-less `companion` processes; `proxied`/`singleton`/ported processes stay single.)
39
40
  - [x] Allow one process config to start multiple replicas (`replicas`, companion-only for now).
40
41
  - [x] Expose `ROLLBRIDGE_REPLICA_INDEX`, replica count, and per-replica template context (`{{replicaIndex}}`/`{{replicaCount}}`).
@@ -91,7 +92,7 @@ This roadmap tracks planned Rollbridge features and documentation. Rollbridge sh
91
92
  - [x] Write a CLI reference for `daemon`, `ensure-daemon`, `deploy`, `status`, `stop`, `shutdown`, and future commands (`docs/cli.md`).
92
93
  - [x] Expand process policy docs with deployment examples for `proxied`, `companion`, `singleton`, and `service`.
93
94
  - [x] Document memory checks and auto-restart behavior after the feature lands (`docs/config.md` → `processes[].memory`).
94
- - [x] Document safe background-job deployment patterns (`docs/workers.md`: companion + `replicas` + `stopSignal` + `gracefulStopMs`, old/new worker overlap).
95
+ - [x] Document the required background-job generation pattern (`docs/workers.md`: release-scoped jobs-main + worker pool, independent quiescence, durable retained supervision, and deploy completion that does not wait for drains).
95
96
  - [x] Document worker lifecycle hooks (`docs/config.md` → `processes[].lifecycle`, `docs/workers.md`).
96
97
  - [x] Add a Velocious deployment guide with Beacon, background-jobs-main, background-jobs-worker, and web process examples (`docs/velocious.md`).
97
98
  - [x] Add an Nginx guide with WebSocket headers, timeouts, and common failure modes (`docs/nginx.md`).
package/docs/cli.md CHANGED
@@ -66,10 +66,16 @@ and waits for control-socket deployments.
66
66
 
67
67
  `--takeover-owner` requires the complete bootstrap tuple. It bootstraps and
68
68
  health-checks the replacement before sending the current daemon the private
69
- retirement command. Retirement stops stable listeners and new work promptly,
70
- but does not wait for owned workers' normal drain before the replacement binds
71
- the proxy and control socket. A bootstrap failure occurs before retirement, so
72
- the previously accepted owner remains available.
69
+ retirement command. The current `performOwnerRetirement` path quiesces every
70
+ service, singleton, starting release, and retained release, releases the stable
71
+ listeners, and starts asynchronous `stop()` calls for all of them. It neither
72
+ preserves nor transfers retained-generation supervision to the replacement.
73
+ The replacement can bind before those stops finish. A bootstrap failure occurs
74
+ before retirement, so the previously accepted owner remains available.
75
+
76
+ A compliant future owner handoff must instead preserve or transfer durable
77
+ supervision of retained generations without handing old workers to a new
78
+ jobs-main. Current `--takeover-owner` does not provide that behavior.
73
79
 
74
80
  ## `ensure-daemon`
75
81
 
@@ -120,12 +126,26 @@ rollbridge deploy --release-path <path>
120
126
  [--daemon-start-timeout-ms <ms>]
121
127
  ```
122
128
 
123
- Starts the prepared release, health-checks the proxied process, switches new
124
- traffic to it, then drains and stops the previous release. Prints
129
+ Starts the complete prepared release generation, health-checks the proxied
130
+ process, and switches new traffic to it. The current daemon then starts
131
+ retirement of the previous release asynchronously, so the command does not wait
132
+ for old workers, jobs, or HTTP/WebSocket connections to finish. Prints
125
133
  `{"status": "success", "activeReleaseId": "...", "previousReleaseId": "..."}`.
126
134
  If the new release fails to start or health-check, the previous release stays
127
135
  active and the command errors.
128
136
 
137
+ After candidate activation, `Daemon.deploy()` synchronously waits for singleton
138
+ replacement before starting `drainAndPrune` and returning. A replacement failure
139
+ can therefore return a non-zero result while the candidate remains active, and a
140
+ slow replacement delays both the response and retirement of the old release.
141
+
142
+ This is a process-lifetime non-blocking drain, not durable supervision across a
143
+ daemon or host restart. It continues across later deploys only while the same
144
+ daemon remains alive. After a restart, surviving PIDs from persisted state are
145
+ advisory orphans that Rollbridge cannot re-adopt; explicit `recover --force`
146
+ stops them. Restart-surviving retained-generation ownership and recovery remain
147
+ required future behavior.
148
+
129
149
  Before each deploy, the daemon reloads the config path it was started with.
130
150
  Compatible process and lifecycle changes apply to the new release and govern
131
151
  how the previous release retires, including updated `nonBlockingDrain`,
package/docs/config.md CHANGED
@@ -75,7 +75,7 @@ to let a deploy group talk to the daemon.
75
75
  | `proxy.upstreamHost` | string | `proxy.host`, or `"127.0.0.1"` when `proxy.host` is `0.0.0.0`/`::` | Host Rollbridge uses for release health checks and proxy targets. |
76
76
  | `proxy.healthPath` | string | `"/ping"` | Default health-check path for proxied processes. |
77
77
  | `proxy.healthTimeoutMs` | number | `30000` | Default health-check timeout for proxied processes. |
78
- | `proxy.drainTimeoutMs` | number | `60000` | How long to drain open connections from a retired release before stopping it. |
78
+ | `proxy.drainTimeoutMs` | number | `60000` | How long to drain HTTP/WebSocket connections before stopping the retired proxied process. Expiry never stops an independently draining jobs generation. |
79
79
  | `proxy.forceStopTimeoutMs` | number | `10000` | Default per-process graceful-stop timeout (`SIGTERM`, then `SIGKILL`). |
80
80
 
81
81
  ## `releaseRetention`
@@ -212,6 +212,20 @@ range** so old and new instances can run at the same time:
212
212
 
213
213
  Reference it from same-release processes with `{{ports.background-jobs-main}}`.
214
214
  During a deploy, old workers keep the old port and new workers get the new port.
215
+ For background jobs, the required compliant architecture makes the handoff
216
+ service and its workers one release generation. Immediately after activation,
217
+ retirement must quiesce the old jobs-main's scheduling, dispatch, and new worker
218
+ handoffs while keeping it with its workers until their accepted work settles.
219
+ Workers are not adopted by the new service.
220
+
221
+ That immediate old-main quiescence is **required future compliance behavior**,
222
+ not current `deployStrategy: "handoff"` behavior. Today the release group starts
223
+ `stop()` for `nonBlockingDrain` companions at retirement but sends no retirement
224
+ or quiescence notice to the handoff service. It waits for the connection drain,
225
+ stops other dependent processes, waits for the non-blocking companion stops, and
226
+ only then stops the handoff service. Consequently, old and new jobs-main
227
+ instances can overlap scheduling and dispatch ownership. Do not treat the
228
+ configuration above alone as compliance with the background-jobs contract.
215
229
 
216
230
  ### `processes[].lifecycle`
217
231
 
@@ -220,6 +234,11 @@ deploy's drain, a `rollbridge restart`, a memory restart, or shutdown. They let
220
234
  job worker quiesce and finish in-flight work before it is terminated. Omit
221
235
  `lifecycle` for the default behavior (just `stopSignal` then `SIGKILL`).
222
236
 
237
+ These hooks describe an individual process stop. They do not define when a
238
+ deploy completes and must not impose a short normal-drain deadline on a jobs
239
+ generation. Per-job timeouts remain the correct bound for genuinely hung jobs;
240
+ legitimate hours-long generation drains are valid.
241
+
223
242
  | Field | Type | Default | Description |
224
243
  | --- | --- | --- | --- |
225
244
  | `lifecycle.quietCommand` | string | unset | Run first to tell the process to stop accepting new work. |
@@ -253,15 +272,16 @@ That keeps a worker alive in case the draining web process still depends on it
253
272
  but it also holds a background worker open for the whole connection drain.
254
273
 
255
274
  Set `nonBlockingDrain: true` on a `companion` whose work is independent of the
256
- proxied process (a job worker on a shared queue). Its graceful stop — `lifecycle`
257
- hooks, or `stopSignal` then `SIGKILL` after `gracefulStopMs` then starts **as
258
- soon as the release is retired**, in parallel with the connection drain, rather
259
- than after it. The new release's workers (started before traffic switches) handle
260
- new work while the retired release's workers finish their in-flight jobs. The
261
- whole drain stays non-blocking the deploy returns immediately.
275
+ proxied process (a job worker on a shared queue). Rollbridge starts that
276
+ companion's configured stop sequence **as soon as the release is retired**, in
277
+ parallel with the connection drain, rather than after it. Its quiet command or
278
+ signal must make the worker stop accepting new handoffs. The asynchronous
279
+ release drain continues after the deploy response. As described above, current
280
+ Rollbridge does not simultaneously quiesce the handoff service, so this setting
281
+ alone does not prevent overlapping jobs-main scheduling or dispatch ownership.
262
282
 
263
283
  ```js
264
- {id: "worker", policy: "companion", command: "…", nonBlockingDrain: true, stopSignal: "SIGINT", gracefulStopMs: 60000}
284
+ {id: "worker", policy: "companion", command: "…", nonBlockingDrain: true, stopSignal: "SIGINT", gracefulStopMs: "indefinite"}
265
285
  ```
266
286
 
267
287
  ### `processes[].restart`
package/docs/nginx.md CHANGED
@@ -67,10 +67,11 @@ Related Rollbridge timeouts (configured in `rollbridge.js`, not Nginx):
67
67
 
68
68
  - `proxy.healthTimeoutMs` gates how long a new release has to become healthy
69
69
  before a deploy aborts — it does not affect request timeouts.
70
- - `proxy.drainTimeoutMs` is how long Rollbridge keeps an old release alive for
71
- in-flight connections during a deploy. Keep Nginx's `proxy_read_timeout` for
72
- WebSocket locations comfortably above it so the front end doesn't cut
73
- connections Rollbridge is still draining.
70
+ - `proxy.drainTimeoutMs` is how long Rollbridge keeps an old proxied web process
71
+ alive for in-flight connections during a deploy. Keep Nginx's
72
+ `proxy_read_timeout` for WebSocket locations comfortably above it so the front
73
+ end doesn't cut connections Rollbridge is still draining. Retained jobs
74
+ generations drain independently and are not stopped when this timeout expires.
74
75
 
75
76
  ## Forwarded headers
76
77