@restatedev/restate-sdk-tunnel 1.15.0-rc.6 → 1.15.0
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/README.md +70 -3
- package/dist/index.cjs +344 -44
- package/dist/index.d.cts +56 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +56 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +342 -44
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -62,6 +62,21 @@ environment variables when not given (option > environment > throw):
|
|
|
62
62
|
| `signingPublicKey` | `RESTATE_INPROC_SIGNING_PUBLIC_KEY` |
|
|
63
63
|
| `authToken` | the file named by `RESTATE_INPROC_AUTH_TOKEN_FILE` (see below) |
|
|
64
64
|
|
|
65
|
+
For operator log attribution, set `tunnelWorkerId` or
|
|
66
|
+
`RESTATE_TUNNEL_WORKER_ID` to a stable worker/pod identifier. When omitted,
|
|
67
|
+
the SDK derives a process-stable, hostname-based id. Each h2 tunnel connection
|
|
68
|
+
also gets a generated `tunnel-connection-id`; both ids are sent during the
|
|
69
|
+
tunnel handshake for diagnostics only.
|
|
70
|
+
|
|
71
|
+
If your process has asynchronous startup work before it can safely execute
|
|
72
|
+
handlers, pass `startupReady` or call `connectTunnel` only after that work has
|
|
73
|
+
completed. Without `startupReady`, the tunnel dials immediately. With it, the
|
|
74
|
+
tunnel supervisor waits for the promise or callback before it dials any tunnel
|
|
75
|
+
server, so the tunnel-server cannot select the pod before the in-process
|
|
76
|
+
handler is ready. A stuck startup gate fails fatally after
|
|
77
|
+
`startupReadyTimeoutMs` (default 120s), making a broken worker visible instead
|
|
78
|
+
of silently absent from the fleet.
|
|
79
|
+
|
|
65
80
|
The [restate-operator](https://github.com/restatedev/restate-operator)
|
|
66
81
|
injects the first four into the pods of a `tunnelMode: in-process`
|
|
67
82
|
RestateDeployment — with a per-revision `tunnelName` — and registers the
|
|
@@ -80,6 +95,44 @@ connection failure, not a crash). Mount the Secret as a whole volume rather
|
|
|
80
95
|
than via `subPath` — Kubernetes does not update `subPath` mounts in place, so
|
|
81
96
|
a rotated token would never reach the file.
|
|
82
97
|
|
|
98
|
+
### Kubernetes shutdown and eviction
|
|
99
|
+
|
|
100
|
+
Client-side graceful shutdown is on by default. On `SIGTERM`,
|
|
101
|
+
`connectTunnel` calls `shutdown()`: each established tunnel session sends an
|
|
102
|
+
HTTP/2 GOAWAY immediately so Restate Cloud stops opening new streams on that
|
|
103
|
+
connection, any raced streams are refused with
|
|
104
|
+
`x-restate-tunnel-draining: true`, in-flight invocations are allowed to finish
|
|
105
|
+
for `drainGraceMs` (default 120s), then the session is closed gracefully. If
|
|
106
|
+
the grace expires first, the session/socket is force-closed. Once the
|
|
107
|
+
registered tunnels in the process finish draining, the default signal handler
|
|
108
|
+
calls `process.exit(0)`.
|
|
109
|
+
|
|
110
|
+
If a process creates multiple tunnels with default signal handling, one shared
|
|
111
|
+
process-level signal handler drains all tunnels registered for that signal
|
|
112
|
+
before exiting. Embedded applications that need to coordinate other shutdown
|
|
113
|
+
work should pass `gracefulShutdown: false`, handle signals themselves, call
|
|
114
|
+
`shutdown()` on every tunnel, and exit only after their own cleanup is complete.
|
|
115
|
+
|
|
116
|
+
Set the pod's `terminationGracePeriodSeconds` to at least
|
|
117
|
+
`ceil(drainGraceMs / 1000)` plus the longest handler drain slack you are
|
|
118
|
+
prepared to allow. Kubernetes sends `SIGTERM` first and sends `SIGKILL` when
|
|
119
|
+
the pod grace period expires; a too-short pod grace period will cut off the
|
|
120
|
+
SDK's drain.
|
|
121
|
+
|
|
122
|
+
On managed Kubernetes platforms, also account for the effective grace window
|
|
123
|
+
the platform grants for the specific eviction event. The SDK can only drain for
|
|
124
|
+
the smaller of `drainGraceMs`, the pod's `terminationGracePeriodSeconds`, and
|
|
125
|
+
the grace the platform actually grants. Some providers document event-specific
|
|
126
|
+
limits; for example,
|
|
127
|
+
[GKE Autopilot documents bounded grace for GKE-initiated evictions](https://cloud.google.com/kubernetes-engine/docs/how-to/extended-duration-pods#about_gke-initiated_pod_eviction).
|
|
128
|
+
Validate this path in your deployment environment by confirming `SIGTERM`
|
|
129
|
+
reaches Node and the process remains alive long enough to complete the drain.
|
|
130
|
+
|
|
131
|
+
Run the Node process as the container's main process with an exec-form
|
|
132
|
+
`ENTRYPOINT`/`CMD`, or use a small init wrapper such as `tini`/`dumb-init` if
|
|
133
|
+
your image starts through a shell or spawns child processes. The SDK installs
|
|
134
|
+
the `SIGTERM` handler, but wrappers still need to forward signals to Node.
|
|
135
|
+
|
|
83
136
|
## How it works
|
|
84
137
|
|
|
85
138
|
`connectTunnel` is the in-process analog of Restate Cloud's standalone
|
|
@@ -95,12 +148,15 @@ a rotated token would never reach the file.
|
|
|
95
148
|
2. **Role-flip:** Restate Cloud drives HTTP/2 as the _client_ over the
|
|
96
149
|
connection we dialed; the deployment becomes the HTTP/2 _server_ on it.
|
|
97
150
|
3. **Handshake:** the cloud opens `GET /_/start-tunnel`; we answer with the
|
|
98
|
-
environment credentials
|
|
99
|
-
|
|
151
|
+
environment credentials plus advisory diagnostic ids
|
|
152
|
+
(`tunnel-worker-id` and `tunnel-connection-id`) and receive the tunnel
|
|
153
|
+
confirmation (including the public `proxy-url`) as HTTP/2 trailers.
|
|
100
154
|
4. **Serve:** each invocation arrives as one HTTP/2 stream. The tunnel's
|
|
101
155
|
`/<scheme>/<host>/<port>` destination prefix is stripped and the request
|
|
102
156
|
is handed to the SDK's own endpoint handler (full-duplex streaming —
|
|
103
|
-
`BIDI_STREAM`), exactly as if it had arrived on a local listener.
|
|
157
|
+
`BIDI_STREAM`), exactly as if it had arrived on a local listener. For
|
|
158
|
+
`connectTunnel`, the `/http/in-process/9080/` deployment URL segment is
|
|
159
|
+
vestigial: this package does not dial a local `:9080` socket.
|
|
104
160
|
5. **Verify:** every forwarded request carries Restate's request-identity
|
|
105
161
|
JWT (`x-restate-jwt-v1`). Verification is delegated to the SDK against
|
|
106
162
|
`signingPublicKey`, so only requests signed by _your_ environment are
|
|
@@ -114,6 +170,12 @@ a rotated token would never reach the file.
|
|
|
114
170
|
immediately dials a replacement while the old connection keeps serving
|
|
115
171
|
its in-flight invocations (up to `drainGraceMs`, default 120s) — zero
|
|
116
172
|
dropped requests across cloud rollovers.
|
|
173
|
+
8. **Shut down gracefully:** the engine advertises `supports-client-drain`.
|
|
174
|
+
On `shutdown()` or the default `SIGTERM` handler, each live connection
|
|
175
|
+
sends HTTP/2 GOAWAY proactively, refuses any raced streams with the drain
|
|
176
|
+
sentinel, drains in-flight invocations up to `drainGraceMs`, and then
|
|
177
|
+
closes the h2 session gracefully. The final forced close is only used after
|
|
178
|
+
the grace window expires.
|
|
117
179
|
|
|
118
180
|
## API
|
|
119
181
|
|
|
@@ -122,6 +184,7 @@ function connectTunnel(options: ConnectTunnelOptions): TunnelConnection;
|
|
|
122
184
|
|
|
123
185
|
interface TunnelConnection {
|
|
124
186
|
close(): Promise<void>; // stop reconnecting + close
|
|
187
|
+
shutdown(opts?: { graceMs?: number }): Promise<void>; // graceful drain + close
|
|
125
188
|
readonly ready: Promise<void>; // first successful handshake (rejects on fatal)
|
|
126
189
|
readonly connectionCount: number;
|
|
127
190
|
readonly tunnelName: string | undefined; // learned from the handshake
|
|
@@ -142,11 +205,15 @@ Key options (see `ConnectTunnelOptions` for the full surface and defaults):
|
|
|
142
205
|
| `authToken` | Cloud API key (`key_...`, Full role) presented in the handshake |
|
|
143
206
|
| `signingPublicKey` | `publickeyv1_...` — request-identity verification (required) |
|
|
144
207
|
| `tunnelName` | The deployment's identity — unique per deployment, shared by its replicas |
|
|
208
|
+
| `tunnelWorkerId` | Stable SDK worker/process diagnostic id; defaults to `RESTATE_TUNNEL_WORKER_ID` or hostname-based |
|
|
209
|
+
| `startupReady` | One-shot startup readiness gate; no tunnel connections are dialed until it completes |
|
|
210
|
+
| `startupReadyTimeoutMs` | Fatal deadline for `startupReady` (120s; only used when `startupReady` is set) |
|
|
145
211
|
| `services` | Same shape `restate.serve` accepts |
|
|
146
212
|
| `tls` | Default on (system trust, ALPN `h2`); object form for CA/mTLS |
|
|
147
213
|
| `connectTimeoutMs` | TCP+TLS dial deadline (5s, mirrors the standalone client) |
|
|
148
214
|
| `reconnectInitialMs/MaxMs/Factor` | Jittered exponential backoff (10ms → 120s, reset after a stable connection) |
|
|
149
215
|
| `supportsDrain` / `drainGraceMs` | Graceful-drain handover on cloud rollovers (on, 120s grace) |
|
|
216
|
+
| `supportsClientDrain` / `gracefulShutdown` | Client shutdown drain with h2 GOAWAY and default `SIGTERM` handling |
|
|
150
217
|
| `pingIntervalMs/TimeoutMs/MaxMissed` | Liveness watchdog (75s cadence) |
|
|
151
218
|
| `maxConcurrentStreams` etc. | HTTP/2 tuning for high-concurrency serving |
|
|
152
219
|
|