@molecule/api-deploy-rollout
Core interface · deploy-rollout · API (Node) · v1.1.2 · Apache-2.0
Deploy-rollout core interface for molecule.dev: replace a deployment's running instances without downtime, grow-then-shrink or in place, with a capacity floor, soak, reverse rollback and idempotent resume.
npm install @molecule/api-deploy-rolloutHow it works
@molecule/api-deploy-rollout is the deploy-rollout core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 3 providers: @molecule/api-deploy-rollout-default, @molecule/api-deploy-rollout-fly, @molecule/api-deploy-rollout-sandbox.
import { setProvider, runRollout } from '@molecule/api-deploy-rollout'
import { provider } from '@molecule/api-deploy-rollout-default'
setProvider(provider)
const outcome = await runRollout({
provider: myInstanceProvider,
current: oldInstances,
policy: { replicas: 2, soakMs: 30_000, drainMs: 25_000 },
})
// outcome.status === 'completed' when the new set is servingProviders (3): @molecule/api-deploy-rollout-default, @molecule/api-deploy-rollout-fly, @molecule/api-deploy-rollout-sandbox
Works with: @molecule/api-bond
Reference
Deploy-rollout core interface for molecule.dev.
Replaces the running instances of a deployment with new ones without taking the
deployment down — grow-then-shrink (create the new set, gate it ready, join it,
then retire the old one at a time) and in-place (change the same instances one at
a time, with a restore path). The package carries the contract only:
@molecule/api-deploy-rollout-default is the algorithm, -fly and -sandbox
adapt concrete instance providers.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, runRollout } from '@molecule/api-deploy-rollout'
import { provider } from '@molecule/api-deploy-rollout-default'
setProvider(provider)
const outcome = await runRollout({
provider: myInstanceProvider,
current: oldInstances,
policy: { replicas: 2, soakMs: 30_000, drainMs: 25_000 },
})
// outcome.status === 'completed' when the new set is serving
Type
core
Installation
npm install @molecule/api-deploy-rollout @molecule/api-bond
API
Interfaces
RolloutInstance
One instance a rollout can act on.
id is the provider's stable handle. upstreamUrl is the routable address the
deployment's edge dials, when the provider has one — a rollout that only
restarts instances in place does not need it.
interface RolloutInstance {
/** Provider instance id. */
id: string
/** The routable upstream URL, when the provider exposes one. */
upstreamUrl?: string
}
InstanceProvider
What a grow-then-shrink rollout needs from whatever hosts the instances.
The caller implements this over its hosting provider; the -sandbox bond does it
over @molecule/api-code-sandbox, and tests drive it with a fake that records the
order and can fail any step.
interface InstanceProvider<I extends RolloutInstance = RolloutInstance> {
/** A human-readable provider name, for progress lines. */
readonly name?: string
/**
* Every instance the deployment currently owns, when the provider can enumerate
* them. Used to resume an interrupted rollout idempotently: an instance already
* ready and joined is not created twice.
*/
list?(): Promise<I[]>
/**
* Create the instance at `index` (0-based, above any primary the caller created
* itself). It must not be routable until `InstanceProvider.join`.
*
* @param index - Which instance this is.
* @returns The created instance.
*/
create(index: number): Promise<I>
/**
* Resolve once the instance may take traffic.
*
* @param instance - The instance to gate.
* @param timeoutMs - The policy's readiness budget, for providers whose probe
* accepts one.
* @returns `false` when it never became ready (the rollout aborts).
*/
waitReady(instance: I, timeoutMs?: number): Promise<boolean>
/**
* A single, fast readiness probe, for the soak window. Optional: a caller that
* sets `soakMs` must provide it, because a blocking readiness wait cannot tell a
* warm instance from a recovering one.
*
* @param instance - The instance to probe.
* @returns Whether it is serving right now.
*/
isServing?(instance: I): Promise<boolean>
/**
* Add the instance to the routable set, so the edge may send it new requests.
*
* @param instance - A ready instance.
*/
join(instance: I): Promise<void>
/**
* Remove the instance from the routable set, so no NEW request is routed to it.
* Requests already in flight are untouched.
*
* @param instance - The instance being retired.
*/
leave(instance: I): Promise<void>
/**
* Tell the instance to drain: finish in-flight work and stop accepting new
* connections. Resolve when it may be destroyed, and never later than `drainMs`.
*
* @param instance - The retired instance.
* @param drainMs - The bounded wait.
*/
drain(instance: I, drainMs: number): Promise<void>
/**
* Destroy the instance, releasing whatever it costs.
*
* @param instance - The instance to remove.
*/
destroy(instance: I): Promise<void>
}
InPlaceInstanceProvider
What an in-place rollout needs from whatever hosts the instances.
Every instance keeps its identity; replace changes it to the new build and
restore puts it back on the old one. The caller supplies list so a
resumed rollout can see what is already there.
interface InPlaceInstanceProvider<I extends RolloutInstance = RolloutInstance> {
/** A human-readable provider name, for progress lines. */
readonly name?: string
/**
* Every instance the rollout may act on, in roll order.
*
* @returns The instances.
*/
list(): Promise<I[]>
/**
* Whether the instance is serving requests right now.
*
* @param instance - The instance to probe.
* @returns `true` while it answers its readiness probe.
*/
isServing(instance: I): Promise<boolean>
/**
* Change the instance to the new build and leave it serving.
*
* @param instance - The instance to change.
*/
replace(instance: I): Promise<void>
/**
* Put the instance back on the old build. Called only for instances this run
* already replaced.
*
* @param instance - The instance to restore.
*/
restore(instance: I): Promise<void>
}
RolloutPolicy
How a rollout bounds itself.
Every field is optional on the caller's side; the bond fills in the defaults documented here.
interface RolloutPolicy {
/**
* How many instances the NEW set holds, for a grow-then-shrink rollout. The
* caller's own primary instance is not created here, so the bond creates
* `replicas - 1`. Defaults to `current.length` (a like-for-like replacement).
*/
replicas?: number
/**
* The most instances that may be unavailable at once. When set, the bond derives
* `minHealthy = max(1, instances - maxUnavailable)` and refuses a step that would
* go below it. Default: `minHealthy` is 1 — never blank the deployment.
*/
maxUnavailable?: number
/**
* An absolute floor on healthy instances. Overrides the value derived from
* `RolloutPolicy.maxUnavailable`. Defaults to 1.
*/
minHealthy?: number
/**
* How long a changed instance must keep answering without a gap before the next
* step starts. Passing one probe is not proof of a warm instance; a gap restarts
* the window. Defaults to 0 (no soak).
*/
soakMs?: number
/** Probe cadence during the soak. Defaults to 2000 ms. */
soakProbeMs?: number
/**
* How long the soak may keep retrying before the rollout aborts. Defaults to
* `soakMs + 60 s`.
*/
soakGraceMs?: number
/** How long a retired instance may take to drain. Defaults to 25 s. */
drainMs?: number
/** The readiness budget handed to `waitReady`. Defaults to 120 s. */
readinessTimeoutMs?: number
/**
* What an abort does to instances this run already changed: `reverse` restores
* them newest first (the default), `none` leaves them as they are.
*/
rollback?: 'reverse' | 'none'
/**
* Whether a rollout reconciles against the provider's current instances instead
* of starting from the caller's list. Defaults to true; a provider without
* `list()` falls back to the caller's list.
*/
resume?: boolean
}
RolloutFailure
A step that failed but did not stop the rollout (a retirement or rollback step).
interface RolloutFailure<I extends RolloutInstance = RolloutInstance> {
/** The instance the step addressed. */
instance: I
/** The step that failed. */
step: RolloutStep
/** Whatever was thrown, for the caller's log. */
error: unknown
}
RolloutOutcome
What a completed or aborted rollout did.
interface RolloutOutcome<I extends RolloutInstance = RolloutInstance> {
/** `completed` when the target set is serving; `aborted` otherwise. */
status: 'completed' | 'aborted'
/** Every instance this run created and joined, in creation order. */
created: I[]
/** Every new instance that is healthy and joined. */
upstreams: I[]
/** Old instances this run removed from the set and destroyed. */
retired: I[]
/** Steps that failed but did not stop the rollout. */
failures: RolloutFailure<I>[]
/** Instances this run restored, newest first (an in-place abort). */
rolledBack: string[]
/** Restores that themselves failed. */
rollbackFailures: RolloutFailure<I>[]
}
InPlaceRolloutOutcome
What an in-place rollout did.
interface InPlaceRolloutOutcome<I extends RolloutInstance = RolloutInstance> {
/** The instances changed successfully, in roll order. */
applied: string[]
/** Every instance that passed its gate. */
instances: I[]
}
RolloutOptions
What InstanceProvider needs for one grow-then-shrink rollout.
interface RolloutOptions<I extends RolloutInstance = RolloutInstance> {
/** The instance provider. */
provider: InstanceProvider<I>
/** The instances serving right now (empty on a first deploy). */
current?: readonly I[]
/** Rollout bounds; the bond fills the defaults. */
policy?: RolloutPolicy
/** Optional progress sink — plain sentences. */
log?: (line: string) => Promise<void> | void
/** Consulted between steps; `true` aborts and keeps the old set serving. */
isCancelled?: () => Promise<boolean> | boolean
/**
* Set when the caller has already created and joined ONE instance of the new
* set (the primary), outside this rollout. It counts toward healthy capacity.
*/
primaryServing?: boolean
/** Injectable clock, for tests. Defaults to `Date.now`. */
now?: () => number
/** Injectable sleep, for tests. Defaults to a real timer. */
sleep?: (ms: number) => Promise<void>
}
DeployRolloutProvider
The contract every deploy-rollout bond implements.
The two methods are the two real deployment topologies; a bond that supports only one rejects the other with a clear error rather than silently doing nothing.
interface DeployRolloutProvider {
/** A human-readable provider name, for diagnostics. */
readonly name: string
/**
* Run a grow-then-shrink replacement of a deployment's instances.
*
* @param options - The set to build, the old set, and the instance provider.
* @returns What the rollout did.
*/
run<I extends RolloutInstance>(options: RolloutOptions<I>): Promise<RolloutOutcome<I>>
/**
* Run an in-place replacement (the same instances changed one at a time).
*
* @param options - The instances to roll and the provider or callbacks.
* @returns What the rollout did.
*/
runInPlace<I extends RolloutInstance>(
options: InPlaceRolloutOptions<I>,
): Promise<InPlaceRolloutOutcome<I>>
}
InPlaceRolloutOptions
What an in-place rollout needs.
interface InPlaceRolloutOptions<I extends RolloutInstance = RolloutInstance> {
/**
* The instances to roll. When absent, the provider's own list is used.
*/
machines?: readonly I[]
/**
* A provider that owns the operations. Alternatively pass `apply`/`rollback`/
* `probeServing` directly; the provider is a convenience for callers that
* already model their hosting this way.
*/
provider?: InPlaceInstanceProvider<I>
/** Change one instance and leave it serving on the NEW build. */
apply?: (instance: I) => Promise<void>
/** Put one instance back on the OLD build. */
rollback?: (instance: I) => Promise<void>
/** Whether an instance is serving right now. */
probeServing?: (instanceId: string) => Promise<boolean>
/** Rollout bounds; the bond fills the defaults. */
policy?: RolloutPolicy
/** Pause between instances. Defaults to 0. */
betweenMs?: number
/** Optional progress sink — plain sentences. */
log?: (line: string) => Promise<void> | void
/** Consulted before each instance; `true` aborts and rolls back. */
isCancelled?: () => Promise<boolean> | boolean
/** Injectable clock, for tests. Defaults to `Date.now`. */
now?: () => number
/** Injectable sleep, for tests. Defaults to a real timer. */
sleep?: (ms: number) => Promise<void>
}
Types
RolloutStep
The step of a rollout that produced a result or a failure.
type RolloutStep =
| 'create'
| 'ready'
| 'join'
| 'leave'
| 'drain'
| 'destroy'
| 'replace'
| 'restore'
| 'capacity'
| 'cancelled'
Functions
setProvider(provider)
Registers a deploy-rollout provider as the active singleton. Called by a bond package during application startup.
function setProvider(provider: DeployRolloutProvider): void
provider— The provider implementation to bond.
getProvider()
Retrieves the bonded deploy-rollout provider, throwing if none is wired.
function getProvider(): DeployRolloutProvider
Returns: The bonded provider.
hasProvider()
Checks whether a deploy-rollout provider is currently bonded.
function hasProvider(): boolean
Returns: true if a provider is bonded.
runRollout(options)
Runs a grow-then-shrink rollout through the bonded provider.
function runRollout<I extends RolloutInstance>(
options: RolloutOptions<I>,
): Promise<RolloutOutcome<I>>
options— The set to build, the old set, and the instance provider.
Returns: What the rollout did.
runInPlaceRollout(options)
Runs an in-place rollout (the same instances changed one at a time) through the bonded provider.
function runInPlaceRollout<I extends RolloutInstance>(
options: InPlaceRolloutOptions<I>,
): Promise<InPlaceRolloutOutcome<I>>
options— The instances to roll and the provider or callbacks.
Returns: What the rollout did.
Available Providers
| Provider | Package |
|---|---|
| Default | @molecule/api-deploy-rollout-default |
| Fly Machines | @molecule/api-deploy-rollout-fly |
| Sandbox | @molecule/api-deploy-rollout-sandbox |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1
Runtime Dependencies
-
@molecule/api-bond -
The core has no implementation. The state machine, the soak, the capacity floor and the reverse rollback live in a bond, so a caller can swap in a provider whose rules differ without touching its own code.
-
Never reduces healthy capacity below the floor. A step that would leave fewer healthy instances than the policy allows is refused, not attempted.
-
An abort keeps the old set serving. Every instance created or changed by the run is undone, newest first, before the error is thrown.
-
Idempotent resume. Re-running a rollout after an interruption reconciles against the provider's current instances instead of creating duplicates.