@tetsujs/lifecycle stops a server without cutting the requests it is still
serving, then closes what the server was using. It works with any Bun.serve
server, not only a Tetsu application. The
Health checks and shutdown guide puts it
in context.
aborts the draining signal, stops accepting connections and waits up to
graceMs for the requests in flight;
cuts whatever is left, waiting up to forceMs;
runs the close functions in order;
exits with 0 if all went cleanly, or 1 if connections had to be cut or
a closer or a server’s stop threw.
Closers run after the server has stopped, so a request in flight never loses
the pool it is using. A closer that throws is reported, and the rest still run.
A second signal skips the pre-stop delay and the grace period, but still cuts
what is left and runs the closers. A third ends the process at once, even
with exit: false: it is the way out of a shutdown that hangs, such as a
closer that never returns.
To run the sequence without signal handling, call shutdown(). It never
rejects: failures are collected into the result.
import { shutdown } from"@tetsujs/lifecycle";
const {
constforced:boolean
Whether the grace period ran out and connections had to be cut — on
any of the servers, when there are several.
forced,
constfailures:readonlyunknown[]
Whatever a server's stop and the closers threw, in the order they
threw it.
failures } =awaitshutdown(
constserver:Bun.Server<unknown>
server, {
close?: readonly Closer[] |undefined
Resources to release, in order, after the server has stopped.
After, never before: a request still in flight may reach for the pool
that closing it early would have taken away.
A process with more than one server, such as a public API and an admin API on
their own ports, passes them all at once:
const
constapi:Bun.Server<unknown>
api= Bun.serve({ ...publicApp,
port?: string | number |undefined
The port the server listens on
@default ― process.env.PORT || "3000"
port: 3000 });
const
constadmin:Bun.Server<unknown>
admin= Bun.serve({ ...adminApp,
port?: string | number |undefined
The port the server listens on
@default ― process.env.PORT || "3000"
port: 3001 });
onShutdownSignals([
constapi:Bun.Server<unknown>
api,
constadmin:Bun.Server<unknown>
admin], {
close?: readonly Closer[] |undefined
Resources to release, in order, after the server has stopped.
After, never before: a request still in flight may reach for the pool
that closing it early would have taken away.
close: [() =>
constpool: {
end():Promise<void>;
}
pool.end()] });
They drain side by side within one graceMs, and the closers run once, after
the last server has stopped. Do not call onShutdownSignals once per server:
the closers would run twice, and the process would exit when the first server
is done.
A balancer keeps sending requests for a few seconds after the signal, and
stopping at once cuts exactly those requests. Set preStopDelayMs and fail
the readiness check while stopping is aborted, so traffic moves away
before the server stops. Use both or neither: a delay without a failing
readiness check only postpones the cut.
Health checks and shutdown wires the
two together.
An event stream, a long poll or a WebSocket never finishes on its own, and
holds every stop for the whole of graceMs. End it on draining: sse(),
stream() and ws() take it as until, and a long poll passes it to what
it waits on. Use draining, not stopping: during the pre-stop delay a
client that reconnects at once would land on this server again. See
Streams and sockets.
stopping is a standard AbortSignal, so scheduled work can stop with the
server, and a closer can wait for a run in progress before the pool it uses
goes away. Background jobs shows both.
stopping aborts as soon as a stop is asked for. A readiness check reads
it, and anything that takes an AbortSignal can use it.
draining aborts when the server starts to stop: after the pre-stop delay,
or together with stopping when there is none.
detach removes the signal handlers, for a process that outlives the
server, such as a test suite.
shutdown returns { forced, failures }: whether connections had to be cut
on any server, and what the servers’ stop and the closers threw, in order.
The package also exports the types ShutdownOptions, SignalOptions,
ShutdownHandle, ShutdownResult, ShutdownFailure, Closer, Stoppable
(anything with a stop method) and Servers.