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 +55 -0
- package/README.md +63 -39
- package/TODO.md +5 -4
- package/docs/cli.md +26 -6
- package/docs/config.md +28 -8
- package/docs/nginx.md +5 -4
- package/docs/tensorbuzz-runbook.md +91 -125
- package/docs/troubleshooting.md +18 -9
- package/docs/velocious.md +93 -170
- package/docs/workers.md +81 -103
- package/package.json +1 -1
- package/src/daemon.js +3 -2
- package/test/shutdown-completion.test.js +57 -3
|
@@ -1,129 +1,95 @@
|
|
|
1
|
-
# TensorBuzz
|
|
1
|
+
# TensorBuzz Rollbridge runbook
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[`examples/tensorbuzz.com.js`](../examples/tensorbuzz.com.js)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[Running under systemd](../README.md#running-under-systemd)). For the general
|
|
8
|
-
Velocious topology and the worker recipe, see [`docs/velocious.md`](velocious.md).
|
|
3
|
+
This runbook defines the required TensorBuzz backend topology. The example at
|
|
4
|
+
[`examples/tensorbuzz.com.js`](../examples/tensorbuzz.com.js) is illustrative;
|
|
5
|
+
verify the deployed consumer config and Rollbridge implementation before
|
|
6
|
+
asserting production compliance.
|
|
9
7
|
|
|
10
|
-
## Ports
|
|
8
|
+
## Ports and generations
|
|
11
9
|
|
|
12
|
-
| Port | Process |
|
|
10
|
+
| Port | Process | Contract |
|
|
13
11
|
| --- | --- | --- |
|
|
14
|
-
| `4500` | Rollbridge proxy |
|
|
15
|
-
| `7330` |
|
|
16
|
-
| `7331` | `background-jobs-main`
|
|
17
|
-
| `14500`–`14599` |
|
|
18
|
-
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
rollbridge
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
C=/etc/rollbridge/tensorbuzz.com.js
|
|
101
|
-
|
|
102
|
-
rollbridge status --config "$C" # active release, ports, per-process state
|
|
103
|
-
rollbridge logs --config "$C" --process web # recent stdout/stderr of a process
|
|
104
|
-
rollbridge events --config "$C" # deploys, switches, crashes, restarts
|
|
105
|
-
rollbridge doctor --config "$C" # pre-flight: socket, proxy port, state
|
|
106
|
-
rollbridge restart --config "$C" --process background-jobs-worker # bounce the worker
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Restarting `beacon` or `background-jobs-main` bounces a shared broker and briefly
|
|
110
|
-
disrupts everything that depends on it; prefer `deploy`/`rollback` for code
|
|
111
|
-
changes. See [`docs/troubleshooting.md`](troubleshooting.md) for health-check
|
|
112
|
-
failures, port conflicts, stale sockets, crash loops, and stuck draining
|
|
113
|
-
releases.
|
|
114
|
-
|
|
115
|
-
## Crash recovery
|
|
116
|
-
|
|
117
|
-
Set [`statePath`](config.md#statepath) in the config to have the daemon persist
|
|
118
|
-
its state. After a daemon crash or reboot, `rollbridge doctor` reports any
|
|
119
|
-
**orphaned** processes still alive from the previous daemon. To clean them up
|
|
120
|
-
before restarting the daemon, run `rollbridge recover` (a dry run that lists
|
|
121
|
-
them), then `rollbridge recover --force` to stop them:
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
rollbridge recover --config /etc/rollbridge/tensorbuzz.com.js # list leftovers
|
|
125
|
-
rollbridge recover --config /etc/rollbridge/tensorbuzz.com.js --force # stop them
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
A machine reboot kills every process, so there are usually no orphans afterward —
|
|
129
|
-
the daemon just starts fresh.
|
|
12
|
+
| `4500` | Rollbridge proxy | Stable public upstream for Nginx. |
|
|
13
|
+
| `7330` | Beacon | Shared persistent service; fixed port is allowed. |
|
|
14
|
+
| `7331`–`7399` | `background-jobs-main` | One allocated endpoint per release generation; never one fixed persistent coordinator. |
|
|
15
|
+
| `14500`–`14599` | web | One proxied endpoint per release. |
|
|
16
|
+
| none | workers | Same-release companions connected only to their generation's jobs-main. |
|
|
17
|
+
|
|
18
|
+
Each jobs generation contains one jobs-main and its complete worker pool, all
|
|
19
|
+
running the same release code. Several old generations may continue draining
|
|
20
|
+
while a newer generation is active.
|
|
21
|
+
|
|
22
|
+
## Required deploy order
|
|
23
|
+
|
|
24
|
+
1. Prepare the candidate release and run backwards-compatible migrations.
|
|
25
|
+
2. Start the candidate jobs-main on a new port, then its worker pool and web
|
|
26
|
+
process. Health-check web before activation.
|
|
27
|
+
3. Activate the candidate release and switch new traffic.
|
|
28
|
+
4. Retire the previous jobs-main and workers as one generation. Jobs-main stops
|
|
29
|
+
schedule ownership, new dispatch, and new handoffs; workers stop accepting
|
|
30
|
+
handoffs.
|
|
31
|
+
5. Return deploy success and release the deploy lock. Do not wait for old jobs,
|
|
32
|
+
workers, jobs-main, HTTP/WebSocket connections, or other retained services.
|
|
33
|
+
|
|
34
|
+
The retired jobs-main remains running on its old endpoint with its old workers.
|
|
35
|
+
For accepted handoffs it owns worker connections and heartbeats, lease fencing,
|
|
36
|
+
terminal-report acceptance and acknowledgement, and durable store transitions.
|
|
37
|
+
The old worker/reporting side durably retries terminal reports, tracks
|
|
38
|
+
outstanding report promises, enforces per-job execution timeouts, and owns and
|
|
39
|
+
reaps child runners. Returned or retried work becomes eligible for the new active
|
|
40
|
+
generation and is never redispatched by the retired main. Old workers do not
|
|
41
|
+
reconnect to or transfer their handoffs to the new main.
|
|
42
|
+
|
|
43
|
+
Old main and workers remain one release generation until every accepted handoff
|
|
44
|
+
settles. Only then, after every old worker exits, may jobs-main exit and
|
|
45
|
+
Rollbridge reap the generation. The referenced release directory stays pinned
|
|
46
|
+
against Rampway cleanup until that point.
|
|
47
|
+
|
|
48
|
+
## Independent drains
|
|
49
|
+
|
|
50
|
+
Set the worker companion to `nonBlockingDrain: true` so it quiesces when its
|
|
51
|
+
generation retires. HTTP/WebSocket connection drain continues independently.
|
|
52
|
+
Finishing or timing out the HTTP drain must never stop a still-draining jobs
|
|
53
|
+
generation. A legitimate multi-hour job and generation drain are valid.
|
|
54
|
+
|
|
55
|
+
Per-job timeouts remain the backstop for genuinely hung jobs. Do not use a short
|
|
56
|
+
worker-shutdown or supervisor timeout as the primary deploy solution; deployment
|
|
57
|
+
has already completed after candidate activation and health.
|
|
58
|
+
|
|
59
|
+
## Runtime-owner and recovery requirements
|
|
60
|
+
|
|
61
|
+
Required compliant behavior durably supervises every retired generation after
|
|
62
|
+
the deploy command returns, across later deploys and supervisor/host recovery.
|
|
63
|
+
Runtime-owner or version handoff must preserve or transfer that supervision and
|
|
64
|
+
return once the replacement is healthy. It must not perform full synchronous
|
|
65
|
+
shutdown, kill retained generations, or make the new jobs-main adopt old workers.
|
|
66
|
+
|
|
67
|
+
Recovery must reconstruct retained generation ownership, endpoints, release
|
|
68
|
+
paths, and process references. Do not treat them as generic orphans to force-stop
|
|
69
|
+
merely because a supervisor restarted. Cleanup becomes eligible only after the
|
|
70
|
+
last retained process exits.
|
|
71
|
+
|
|
72
|
+
Current Rollbridge does not yet meet those recovery and owner-handoff
|
|
73
|
+
requirements. Its non-blocking release drains last only for the current daemon's
|
|
74
|
+
lifetime; after restart it reports surviving PIDs as advisory, non-adoptable
|
|
75
|
+
orphans, and forced recovery stops them. `--takeover-owner` quiesces and starts
|
|
76
|
+
asynchronous stops for every managed process instead of transferring retained
|
|
77
|
+
generations.
|
|
78
|
+
|
|
79
|
+
## Operator checks
|
|
80
|
+
|
|
81
|
+
Use `rollbridge status`, logs, and events to confirm:
|
|
82
|
+
|
|
83
|
+
- the active jobs generation uses the active release and a unique jobs-main port;
|
|
84
|
+
- every retired generation retains its own jobs-main, workers, endpoint, and
|
|
85
|
+
pinned release path;
|
|
86
|
+
- the deploy command has returned while long drains continue;
|
|
87
|
+
- no retired jobs-main is dispatching new ordinary queued work;
|
|
88
|
+
- HTTP drain completion or timeout did not stop a jobs generation; and
|
|
89
|
+
- completed generations are reaped and Rampway is told their release references
|
|
90
|
+
ended so it can release the pins.
|
|
91
|
+
|
|
92
|
+
Do not restart Beacon or a jobs-main casually. Never use `shutdown`, forced
|
|
93
|
+
orphan recovery, or process signals as a substitute for the normal retained-
|
|
94
|
+
generation lifecycle. See [`docs/velocious.md`](velocious.md) and
|
|
95
|
+
[`docs/workers.md`](workers.md) for the architecture details.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -109,12 +109,21 @@ stays active.
|
|
|
109
109
|
in `state: "draining"` with non-zero `connections` (often `websocket`).
|
|
110
110
|
|
|
111
111
|
**Diagnose.** Long-lived connections (WebSockets, SSE, streaming responses) keep
|
|
112
|
-
the retired
|
|
113
|
-
`status` shows the release's
|
|
114
|
-
`drainStartedAt`.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`proxy.drainTimeoutMs
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
the retired proxied web process alive until they close or
|
|
113
|
+
`proxy.drainTimeoutMs` elapses. `status` shows the release's
|
|
114
|
+
`connections.http`/`connections.websocket` and `drainStartedAt`. A retained jobs
|
|
115
|
+
generation has an independent lifecycle and may remain after the web drain ends.
|
|
116
|
+
|
|
117
|
+
**Fix.** The connection drain ends automatically when those connections close,
|
|
118
|
+
or after `proxy.drainTimeoutMs`. Rollbridge then stops the retired proxied web
|
|
119
|
+
process and other connection-dependent processes, including ordinary companions
|
|
120
|
+
with `nonBlockingDrain: false`. Lower the timeout only to shorten
|
|
121
|
+
HTTP/WebSocket retention, or make clients reconnect (for example, have the front
|
|
122
|
+
end close idle WebSockets on deploy). In the documented compliant jobs topology,
|
|
123
|
+
jobs companions use `nonBlockingDrain: true`, so timeout expiry affects only the
|
|
124
|
+
web side and must not stop a still-draining jobs generation.
|
|
125
|
+
|
|
126
|
+
Reporting release references to Rampway and pinning release directories against
|
|
127
|
+
on-disk cleanup are required future behavior, not implemented today. Current
|
|
128
|
+
Rollbridge `status` and `releaseRetention` govern only its in-memory release
|
|
129
|
+
records and do not fence Rampway cleanup.
|
package/docs/velocious.md
CHANGED
|
@@ -1,37 +1,35 @@
|
|
|
1
1
|
# Velocious deployment guide
|
|
2
2
|
|
|
3
|
-
A Velocious backend
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
A Velocious backend normally runs Beacon, `background-jobs-main`, a
|
|
4
|
+
`background-jobs-worker` pool, and the web/API server. For deploy lifecycle
|
|
5
|
+
purposes, jobs-main and its workers are one release-scoped **jobs generation**.
|
|
6
|
+
This topology is required; a single persistent fixed-port jobs-main is not a
|
|
7
|
+
safe coordinator for workers that may drain across releases.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
This page states the architecture contract. It does not by itself assert that a
|
|
10
|
+
particular Rollbridge release or consumer production config implements durable
|
|
11
|
+
retired-generation recovery; verify source and config before claiming compliance.
|
|
11
12
|
|
|
12
13
|
## Process mapping
|
|
13
14
|
|
|
14
|
-
| Velocious process |
|
|
15
|
+
| Velocious process | Rollbridge policy | Lifecycle |
|
|
15
16
|
| --- | --- | --- |
|
|
16
|
-
| `beacon` | `service` |
|
|
17
|
-
| `background-jobs-main` | `service` with `deployStrategy: "handoff"` |
|
|
18
|
-
| `background-jobs-worker` | `companion` | Release-scoped
|
|
19
|
-
| `web` | `proxied` |
|
|
20
|
-
|
|
21
|
-
See [README → Process Policies](../README.md#process-policies) for the full
|
|
22
|
-
semantics of each policy and [`docs/config.md`](config.md) for every field.
|
|
17
|
+
| `beacon` | persistent `service` | Shared broker; it may remain daemon-wide on fixed port `7330`. |
|
|
18
|
+
| `background-jobs-main` | `service` with `deployStrategy: "handoff"` | One endpoint per release; owns worker connections, lease fencing, report acceptance/acknowledgement, and durable store transitions. |
|
|
19
|
+
| `background-jobs-worker` | `companion` with `nonBlockingDrain: true` | Release-scoped pool; executes accepted work, owns child runners and execution timeouts, and durably retries terminal reports while tracking their promises. |
|
|
20
|
+
| `web` | `proxied` | Health-gated active HTTP/WebSocket target with a per-release port. |
|
|
23
21
|
|
|
24
22
|
## Example `rollbridge.js`
|
|
25
23
|
|
|
26
24
|
```js
|
|
27
|
-
// rollbridge.js
|
|
28
25
|
export default {
|
|
29
26
|
application: "tensorbuzz",
|
|
30
27
|
control: {path: "/tmp/rollbridge-tensorbuzz.sock"},
|
|
28
|
+
statePath: "/var/lib/rollbridge/tensorbuzz.json",
|
|
31
29
|
|
|
32
30
|
proxy: {
|
|
33
31
|
host: "127.0.0.1",
|
|
34
|
-
port: 4500,
|
|
32
|
+
port: 4500,
|
|
35
33
|
healthPath: "/ping",
|
|
36
34
|
healthTimeoutMs: 30000,
|
|
37
35
|
drainTimeoutMs: 60000,
|
|
@@ -39,7 +37,6 @@ export default {
|
|
|
39
37
|
},
|
|
40
38
|
|
|
41
39
|
processes: [
|
|
42
|
-
// Shared broker — one daemon-wide instance on a stable port.
|
|
43
40
|
{
|
|
44
41
|
id: "beacon",
|
|
45
42
|
policy: "service",
|
|
@@ -48,8 +45,6 @@ export default {
|
|
|
48
45
|
command: "npx velocious beacon",
|
|
49
46
|
port: 7330
|
|
50
47
|
},
|
|
51
|
-
|
|
52
|
-
// Job coordinator — one release-scoped service instance per deploy.
|
|
53
48
|
{
|
|
54
49
|
id: "background-jobs-main",
|
|
55
50
|
policy: "service",
|
|
@@ -63,23 +58,20 @@ export default {
|
|
|
63
58
|
command: "wait-for-it 127.0.0.1:{{ports.beacon}} --strict -- npx velocious background-jobs-main",
|
|
64
59
|
port: {from: 7331, to: 7399}
|
|
65
60
|
},
|
|
66
|
-
|
|
67
|
-
// Workers — one set per release; raise gracefulStopMs to let in-flight
|
|
68
|
-
// jobs finish during a deploy.
|
|
69
61
|
{
|
|
70
62
|
id: "background-jobs-worker",
|
|
71
63
|
policy: "companion",
|
|
64
|
+
nonBlockingDrain: true,
|
|
72
65
|
cwd: "{{releasePath}}/backend",
|
|
73
66
|
env: {
|
|
74
67
|
NODE_ENV: "production",
|
|
75
68
|
VELOCIOUS_BEACON_PORT: "{{ports.beacon}}",
|
|
76
69
|
VELOCIOUS_BACKGROUND_JOBS_PORT: "{{ports.background-jobs-main}}"
|
|
77
70
|
},
|
|
78
|
-
command: "wait-for-it 127.0.0.1:{{ports.
|
|
71
|
+
command: "wait-for-it 127.0.0.1:{{ports.background-jobs-main}} --strict -- npx velocious background-jobs-worker",
|
|
72
|
+
replicas: 4,
|
|
79
73
|
gracefulStopMs: "indefinite"
|
|
80
74
|
},
|
|
81
|
-
|
|
82
|
-
// Web/API — the one proxied process.
|
|
83
75
|
{
|
|
84
76
|
id: "web",
|
|
85
77
|
policy: "proxied",
|
|
@@ -89,7 +81,7 @@ export default {
|
|
|
89
81
|
VELOCIOUS_BEACON_PORT: "{{ports.beacon}}",
|
|
90
82
|
VELOCIOUS_BACKGROUND_JOBS_PORT: "{{ports.background-jobs-main}}"
|
|
91
83
|
},
|
|
92
|
-
command: "wait-for-it 127.0.0.1:{{ports.
|
|
84
|
+
command: "wait-for-it 127.0.0.1:{{ports.background-jobs-main}} --strict -- npx velocious server --host 127.0.0.1 --port {{port}}",
|
|
93
85
|
port: {from: 14500, to: 14599},
|
|
94
86
|
health: {path: "/ping", timeoutMs: 30000, intervalMs: 500}
|
|
95
87
|
}
|
|
@@ -97,146 +89,77 @@ export default {
|
|
|
97
89
|
}
|
|
98
90
|
```
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
`
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### Worker recipe
|
|
175
|
-
|
|
176
|
-
A complete `background-jobs-worker` entry that runs a pool and finishes in-flight
|
|
177
|
-
jobs across a deploy:
|
|
178
|
-
|
|
179
|
-
```js
|
|
180
|
-
{
|
|
181
|
-
id: "background-jobs-worker",
|
|
182
|
-
policy: "companion",
|
|
183
|
-
cwd: "{{releasePath}}/backend",
|
|
184
|
-
env: {
|
|
185
|
-
NODE_ENV: "production",
|
|
186
|
-
VELOCIOUS_ENV: "production",
|
|
187
|
-
VELOCIOUS_BEACON_PORT: "{{ports.beacon}}",
|
|
188
|
-
VELOCIOUS_BACKGROUND_JOBS_PORT: "{{ports.background-jobs-main}}"
|
|
189
|
-
},
|
|
190
|
-
command: "wait-for-it 127.0.0.1:{{ports.beacon}} --strict -- wait-for-it 127.0.0.1:{{ports.background-jobs-main}} --strict -- npx velocious background-jobs-worker",
|
|
191
|
-
replicas: 4,
|
|
192
|
-
gracefulStopMs: "indefinite"
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
- `replicas: 4` runs four worker instances (`background-jobs-worker#0` … `#3`),
|
|
197
|
-
each with `ROLLBRIDGE_REPLICA_INDEX`/`ROLLBRIDGE_REPLICA_COUNT` if you shard work.
|
|
198
|
-
- On deploy the new release's workers start before traffic switches; the old
|
|
199
|
-
release's workers receive `SIGTERM` (the default `stopSignal`) when the old
|
|
200
|
-
release is retired, then wait to exit. With `gracefulStopMs: "indefinite"`,
|
|
201
|
-
Rollbridge does not send a `SIGKILL` fallback.
|
|
202
|
-
|
|
203
|
-
If your worker quiesces on a command or a non-default signal, add a `lifecycle`
|
|
204
|
-
block — Rollbridge runs `quietCommand`, drains for up to `drainTimeoutMs`, then
|
|
205
|
-
stops. For example, send a quiet signal to the worker's process group before the
|
|
206
|
-
drain:
|
|
207
|
-
|
|
208
|
-
```js
|
|
209
|
-
lifecycle: {quietCommand: "kill -TSTP -$ROLLBRIDGE_PID", drainTimeoutMs: 60000}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Choosing the jobs-main policy
|
|
213
|
-
|
|
214
|
-
`background-jobs-main` coordinates workers, so choose its lifecycle deliberately:
|
|
215
|
-
|
|
216
|
-
- **`service` with `deployStrategy: "handoff"`** — starts one coordinator per
|
|
217
|
-
release on a port from a range. New workers and web get the new release's port;
|
|
218
|
-
old workers keep the old release's port while they drain. This is the safest
|
|
219
|
-
default when the coordinator should run the same code version as its workers.
|
|
220
|
-
- **`service` with the default `deployStrategy: "persistent"`** — keeps one
|
|
221
|
-
daemon-wide coordinator on a stable port. Workers from every release talk to the
|
|
222
|
-
same coordinator, but it keeps running the release it was started from and only
|
|
223
|
-
adopts the latest template if it restarts later.
|
|
224
|
-
- **`singleton`** — stops the old instance and then starts the new one on each
|
|
225
|
-
deploy, so it always runs the latest release's code and two copies never
|
|
226
|
-
overlap. The trade-off: a brief coordination gap while it restarts.
|
|
227
|
-
|
|
228
|
-
Beacon is a broker rather than code that changes per release, so `service` is
|
|
229
|
-
almost always right for it.
|
|
230
|
-
|
|
231
|
-
## Verifying
|
|
232
|
-
|
|
233
|
-
After a deploy, `rollbridge status` should show `beacon` as a long-lived service
|
|
234
|
-
with an unchanged port, `background-jobs-main` as the active release's handoff
|
|
235
|
-
service, one `background-jobs-worker` for the active release, and the `web`
|
|
236
|
-
process `proxied` with its connection counts. Use
|
|
237
|
-
[`rollbridge logs --process <id>`](cli.md) to read recent output from any
|
|
238
|
-
process, and [`docs/troubleshooting.md`](troubleshooting.md) for health-check,
|
|
239
|
-
port, and draining problems.
|
|
240
|
-
|
|
241
|
-
For the front end, point Nginx at the stable `proxy.port` (here `4500`), never at
|
|
242
|
-
a release's web port — see [`docs/nginx.md`](nginx.md).
|
|
92
|
+
Beacon keeps its fixed port because it is intentionally shared. Jobs-main uses a
|
|
93
|
+
range because every release gets its own coordinator. Same-release
|
|
94
|
+
`{{ports.background-jobs-main}}` expansion ensures that old workers retain the
|
|
95
|
+
old endpoint while candidate workers use the candidate endpoint.
|
|
96
|
+
|
|
97
|
+
## Deploy and activation
|
|
98
|
+
|
|
99
|
+
Run backwards-compatible migrations before activation, then invoke
|
|
100
|
+
`rollbridge deploy` with the prepared release. Rollbridge starts the candidate
|
|
101
|
+
jobs-main, its complete worker pool, and the web process before health gating and
|
|
102
|
+
activation. A candidate startup or health failure leaves the previous release
|
|
103
|
+
active.
|
|
104
|
+
|
|
105
|
+
After successful activation, the deploy returns without waiting for any retired
|
|
106
|
+
generation or HTTP/WebSocket connection to finish. The old and new release code
|
|
107
|
+
may therefore overlap for hours. Keep schema, queue payloads, and external side
|
|
108
|
+
effects compatible across that window.
|
|
109
|
+
|
|
110
|
+
## Retired jobs-generation contract
|
|
111
|
+
|
|
112
|
+
Retire jobs-main and its workers as one unit:
|
|
113
|
+
|
|
114
|
+
- jobs-main relinquishes recurring schedule ownership and stops dispatching
|
|
115
|
+
queued work or making new worker handoffs;
|
|
116
|
+
- workers stop advertising or accepting new handoffs;
|
|
117
|
+
- jobs-main remains running on the old endpoint and owns worker connections and
|
|
118
|
+
heartbeats, lease fencing, terminal-report acceptance and acknowledgement, and
|
|
119
|
+
durable store transitions for its accepted handoffs;
|
|
120
|
+
- the old worker/reporting side durably retries terminal reports, tracks
|
|
121
|
+
outstanding report promises, enforces per-job execution timeouts, and owns and
|
|
122
|
+
reaps child runners;
|
|
123
|
+
- a job returned or retried to the shared queue becomes eligible for the new
|
|
124
|
+
active generation, and the retired main never dispatches it again;
|
|
125
|
+
- old workers never reconnect to or transfer their handoffs to the new jobs-main;
|
|
126
|
+
- old main and workers remain one release generation until all accepted work
|
|
127
|
+
settles; jobs-main exits only after that and after all workers drain and exit,
|
|
128
|
+
after which Rollbridge may reap the generation.
|
|
129
|
+
|
|
130
|
+
HTTP/WebSocket and jobs drains are independent. `proxy.drainTimeoutMs` bounds the
|
|
131
|
+
connection drain only; reaching it must not stop a still-draining jobs generation.
|
|
132
|
+
`nonBlockingDrain: true` starts worker quiescence at retirement rather than after
|
|
133
|
+
the HTTP drain.
|
|
134
|
+
|
|
135
|
+
The process supervisor must retain multiple old generations concurrently,
|
|
136
|
+
persist their ownership across later deploys and supervisor/host recovery, and
|
|
137
|
+
report every referenced release directory so Rampway can pin it against cleanup.
|
|
138
|
+
A runtime owner/version handoff preserves or transfers that supervision and
|
|
139
|
+
returns after the replacement is healthy; it is not a full synchronous shutdown.
|
|
140
|
+
|
|
141
|
+
These are target requirements. Current Rollbridge drains releases
|
|
142
|
+
asynchronously only while the same daemon remains alive, cannot re-adopt
|
|
143
|
+
surviving PIDs after restart, and stops rather than transfers all managed
|
|
144
|
+
processes during `--takeover-owner`; see [`docs/cli.md`](cli.md#daemon).
|
|
145
|
+
|
|
146
|
+
## Timeouts
|
|
147
|
+
|
|
148
|
+
Velocious per-job timeouts remain responsible for genuinely hung work. Rollbridge
|
|
149
|
+
stop signals, lifecycle hooks, and graceful-stop bounds remain emergency/process
|
|
150
|
+
controls. Do not use a short normal worker-shutdown timeout to make deployment
|
|
151
|
+
complete: deployment is already complete after healthy activation, and a
|
|
152
|
+
legitimate hours-long job makes an hours-long generation drain valid.
|
|
153
|
+
|
|
154
|
+
## Verification
|
|
155
|
+
|
|
156
|
+
After a deploy, status must be able to show the active generation and every
|
|
157
|
+
retired generation still draining, including each jobs-main endpoint, worker
|
|
158
|
+
pool, release path, and retention reference. Beacon may keep `7330`; active and
|
|
159
|
+
retired jobs-main instances must use different ports. Confirm that the deploy
|
|
160
|
+
command has returned even while retained generations remain and that an HTTP
|
|
161
|
+
drain timeout does not terminate them.
|
|
162
|
+
|
|
163
|
+
See [`docs/workers.md`](workers.md) for the focused lifecycle,
|
|
164
|
+
[`docs/config.md`](config.md) for configuration fields, and
|
|
165
|
+
[`docs/tensorbuzz-runbook.md`](tensorbuzz-runbook.md) for the consumer runbook.
|