|
65 | 65 | - v2/actor-runs |
66 | 66 | - v2/actor-runs/:runId |
67 | 67 | - v2/actor-runs/:runId/abort |
| 68 | + - v2/actor-runs/:runId/reboot |
68 | 69 | - v2/actor-runs/:runId/log |
69 | 70 | - Datasets |
70 | 71 | - v2/datasets |
|
145 | 146 | reject exactly the same inputs with the same outcomes. |
146 | 147 | - **`GET /actor-runtime/events/:runId`** - a websocket upgrade, reachable at exactly this one path on |
147 | 148 | the fixed API port (`system.md`). It carries the run's platform events: `systemInfo` once a second |
148 | | - (`actor-driver.md`), plus a one-off `aborting` frame under `?gracefully=` (below). Each frame is a |
| 149 | + (`actor-driver.md`), a one-off `aborting`-plus-`persistState` pair under `?gracefully=` (below), and a |
| 150 | + one-off `migrating` frame when a migration is triggered ("Migration emulation" below). Each frame is a |
149 | 151 | single text message, `{"name": "...", "data": {...}}`. |
150 | 152 | - The endpoint has no authentication. The run id in the path is the only thing it scopes on, and a |
151 | 153 | connection only ever receives that run's own frames; one run never sees another's. |
|
154 | 156 | as fatal to the Actor. |
155 | 157 | - A connection to a live run stays open until the run ends, when the server closes it with `1000`. It |
156 | 158 | is never dropped while healthy, except that a graceful runtime shutdown terminates every open |
157 | | - connection along with the rest of the server. |
158 | | - - `persistState` is never sent over this channel; both SDKs generate it themselves. |
| 159 | + connection along with the rest of the server. A migration/reboot restart is not the run ending: the |
| 160 | + restarted container reconnects to the same path. |
| 161 | + - The _periodic_ `persistState` is never sent over this channel; both SDKs generate it themselves. |
| 162 | + The server sends `persistState` exactly once per graceful abort, alongside `aborting` (matching the |
| 163 | + platform), and never alongside `migrating` (the SDKs synthesize that one). |
159 | 164 |
|
160 | 165 | ## Graceful abort (`?gracefully=`) |
161 | 166 |
|
162 | 167 | - `POST /v2/actor-runs/:runId/abort` accepts an optional `?gracefully=` boolean. |
163 | 168 | - Omitted or `false`: the run aborts immediately. |
164 | | -- `true` on a running run: the record moves to `ABORTING` at once, an `aborting` frame with an empty |
165 | | - payload is published on the run's events channel, and the container is stopped 30 seconds later. The |
166 | | - request stays open until then. |
| 169 | +- `true` on a running run: the record moves to `ABORTING` at once, an `aborting` frame plus a |
| 170 | + `persistState {"isMigrating": false}` frame (in that order, matching the platform) are published on |
| 171 | + the run's events channel, and the container is stopped 30 seconds later. The request stays open until |
| 172 | + then. |
167 | 173 | - `true` on a run with no container (still `READY`, or already terminal): behaves as if omitted. |
168 | 174 | - A second abort arriving during an open window: another `?gracefully=true` joins that window and neither |
169 | 175 | restarts it nor stops the container early; a non-graceful one escalates and stops the container at once. |
170 | 176 |
|
| 177 | +## Migration emulation (`POST /actor-runtime/migrate/:runId`) and reboot |
| 178 | + |
| 179 | +A platform migration is not a run status: the run stays `RUNNING` while its container is killed and a |
| 180 | +new one starts for the same run - same run id, env vars, and default storages, in-memory state gone. |
| 181 | +This runtime emulates that observable experience on demand: |
| 182 | + |
| 183 | +- **`POST /actor-runtime/migrate/:runId`** (also at `/v2/actor-runtime/migrate/:runId`) - authenticated |
| 184 | + like the rest of this namespace, scoped to the caller's own runs. The console's run detail view |
| 185 | + exposes the same trigger as a Migrate button (`console.md`). |
| 186 | + - Publishes a `migrating` frame (empty payload) on the run's events channel immediately, stops the |
| 187 | + container 5 seconds later (the platform promises only "a few seconds"), then restarts the same |
| 188 | + run. Status stays `RUNNING`; `startedAt`, `finishedAt`, `exitCode`, the default storage ids, and |
| 189 | + the container env are unchanged. `stats.migrationCount` increments once per performed stop. |
| 190 | + - Responds immediately with the run object (same shape as `abort`/`reboot`). A second call during |
| 191 | + the open window joins it: same response, no second frame or window. |
| 192 | + - Errors: unknown/foreign run `404` `record-not-found`; finished run `403` `job-finished`; |
| 193 | + `READY`/`ABORTING` `400` `invalid-request`. |
| 194 | + - The timeout budget is per run, not per container: a restarted container gets only the remaining |
| 195 | + `timeoutSecs`. |
| 196 | + - An abort (graceful or hard) landing during the window or restart wins: the run ends `ABORTED`, |
| 197 | + never restarted. |
| 198 | +- **`POST /v2/actor-runs/:runId/reboot`** - the real platform endpoint the SDKs call from their default |
| 199 | + `migrating` handler. Stops and restarts the run's container immediately (no warning frame), cancels an |
| 200 | + open migration window, and increments `stats.rebootCount`. A finished run is `403` `job-finished`; a |
| 201 | + non-terminal run with no container (`READY`, `ABORTING`) gets the count bump but no restart. |
| 202 | +- The run object's `stats` carries `migrationCount`, `rebootCount`, `restartCount`, and `resurrectCount` |
| 203 | + (the latter two always `0` here), initialized to `0` at run creation like the platform. |
| 204 | +- The run's log is cumulative across restarts, with a one-line marker between the incarnations' output. |
| 205 | + |
171 | 206 | ## Upstream fallback (opt-in, off by default, all HTTP methods) |
172 | 207 |
|
173 | 208 | - Two independent booleans, `fallbackUnimplementedEnabled` and `fallbackNotFoundEnabled`, gate whether |
|
0 commit comments