@molecule/api-upstream-balancer
Core interface · upstream-balancer · API (Node) · v1.1.2 · Apache-2.0
Upstream-balancer core interface for molecule.dev: choose which instance of a multi-instance deployment one request goes to — health-aware round-robin, failure cooldown, retry elsewhere, drain-aware.
npm install @molecule/api-upstream-balancerHow it works
@molecule/api-upstream-balancer is the upstream-balancer core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/api-upstream-balancer-default.
import { setProvider, pickUpstream, noteUpstreamFailure } from '@molecule/api-upstream-balancer'
import { provider } from '@molecule/api-upstream-balancer-default'
setProvider(provider)
const target = pickUpstream('app.example.com', [primary, replica1, replica2])
try {
await forward(target)
} catch (error) {
noteUpstreamFailure(target)
throw error
}Providers (1): @molecule/api-upstream-balancer-default
Works with: @molecule/api-bond
Reference
Upstream-balancer core interface for molecule.dev.
A public host resolves to a SET of upstreams; this contract decides which member one request goes to. It is the selection policy an edge uses — health-aware round-robin, a passive failure cooldown, a retry that lands on a different member, and drain-awareness — not a proxy and not a load-balancer appliance.
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, pickUpstream, noteUpstreamFailure } from '@molecule/api-upstream-balancer'
import { provider } from '@molecule/api-upstream-balancer-default'
setProvider(provider)
const target = pickUpstream('app.example.com', [primary, replica1, replica2])
try {
await forward(target)
} catch (error) {
noteUpstreamFailure(target)
throw error
}
Type
core
Installation
npm install @molecule/api-upstream-balancer @molecule/api-bond
API
Interfaces
UpstreamMember
One upstream an edge may dial.
host and port are the member's identity: the balancer's round-robin cursor
and its failure marks are keyed on host:port, so two members that differ only
in metadata are the same member.
interface UpstreamMember {
/** Upstream hostname — a loopback address (Docker) or the provider's host. */
host: string
/** Upstream port. */
port: number
/**
* Set when the deploy/lifecycle marked this instance as draining: it must not
* receive NEW requests, while requests already in flight finish. Optional; a
* balancer also accepts the same mark through `markDraining()`.
*/
draining?: boolean
}
UpstreamBalancerPolicy
How a balancer bounds itself.
interface UpstreamBalancerPolicy {
/**
* How long a member stays skipped after a failed attempt. The mark is a
* cooldown, not a verdict: after this long the member is eligible again even if
* nothing succeeded against it, so an instance that recovered without another
* request reaching it is not skipped forever.
*
* Defaults to 10 000 ms.
*/
failureCooldownMs?: number
}
UpstreamBalancerProvider
What an edge needs from an upstream balancer.
Every selection method is scoped to ONE host: the round-robin cursor advances per public host, so two apps sharing an edge do not interleave each other's order.
interface UpstreamBalancerProvider {
/** A human-readable provider name, for diagnostics. */
readonly name: string
/**
* Choose the upstream for one request.
*
* Members marked suspect (a recent failure) or draining are skipped while any
* other member is eligible. When EVERY member is suspect or draining the cursor's
* own answer is returned anyway: a stale mark must not turn into "the whole app
* is down" while every address is merely suspect.
*
* @param host - The public host the request arrived for.
* @param members - The host's upstreams, primary first (never empty).
* @returns The member to dial.
*/
pick<U extends UpstreamMember>(host: string, members: readonly U[]): U
/**
* Choose the upstream for a RETRY: a different member when one exists, preferring
* one that is not currently suspect.
*
* A retried idempotent request that lands on the same broken instance gains
* nothing. With a single member there is nothing to choose, and it is returned
* unchanged.
*
* @param host - The public host the request arrived for.
* @param avoid - The member the failed attempt addressed.
* @param members - The host's upstreams, primary first.
* @returns The member for the next attempt.
*/
pickRetry<U extends UpstreamMember>(host: string, avoid: U, members: readonly U[]): U
/**
* Record that an attempt against this member failed, starting its cooldown.
*
* @param upstream - The member the attempt addressed.
*/
noteFailure(upstream: UpstreamMember): void
/**
* Record that this member answered, clearing any failure mark.
*
* @param upstream - The member that answered.
*/
noteSuccess(upstream: UpstreamMember): void
/**
* Whether a member was marked failed recently enough to skip.
*
* @param upstream - The candidate.
* @returns `true` while its failure mark is fresh.
*/
isSuspect(upstream: UpstreamMember): boolean
/**
* Mark a member as draining by key (`host:port`), so new requests skip it.
*
* @param key - The member's `host:port`.
*/
markDraining(key: string): void
/**
* Clear a draining mark.
*
* @param key - The member's `host:port`.
*/
clearDraining(key: string): void
/**
* Whether a member is draining, from its own flag or from `markDraining()`.
*
* @param upstream - The candidate.
* @returns `true` while it must receive no new request.
*/
isDraining(upstream: UpstreamMember): boolean
/**
* Drop every cursor and mark. For tests that exercise several edges in one
* process; application code never calls it.
*/
reset(): void
}
Functions
setProvider(provider)
Registers an upstream-balancer provider as the active singleton. Called by a bond package during application startup.
function setProvider(provider: UpstreamBalancerProvider): void
provider— The provider implementation to bond.
getProvider()
Retrieves the bonded upstream-balancer provider, throwing if none is wired.
function getProvider(): UpstreamBalancerProvider
Returns: The bonded provider.
hasProvider()
Checks whether an upstream-balancer provider is currently bonded.
function hasProvider(): boolean
Returns: true if a provider is bonded.
pickUpstream(host, members)
Chooses the upstream for one request through the bonded provider.
function pickUpstream<U extends UpstreamMember>(host: string, members: readonly U[]): U
host— The public host the request arrived for.members— The host's upstreams, primary first.
Returns: The member to dial.
pickRetryUpstream(host, avoid, members)
Chooses the upstream for a retry through the bonded provider.
function pickRetryUpstream<U extends UpstreamMember>(
host: string,
avoid: U,
members: readonly U[],
): U
host— The public host the request arrived for.avoid— The member the failed attempt addressed.members— The host's upstreams, primary first.
Returns: The member for the next attempt.
noteUpstreamFailure(upstream)
Records a failed attempt against one upstream.
function noteUpstreamFailure(upstream: UpstreamMember): void
upstream— The member the attempt addressed.
noteUpstreamSuccess(upstream)
Records that one upstream answered.
function noteUpstreamSuccess(upstream: UpstreamMember): void
upstream— The member that answered.
upstreamSuspect(upstream)
Reports whether an upstream's failure mark is still fresh.
function upstreamSuspect(upstream: UpstreamMember): boolean
upstream— The candidate.
Returns: true while it should be skipped.
markUpstreamDraining(key)
Marks an upstream as draining by key (host:port).
function markUpstreamDraining(key: string): void
key— The member'shost:port.
clearUpstreamDraining(key)
Clears an upstream's draining mark.
function clearUpstreamDraining(key: string): void
key— The member'shost:port.
upstreamDraining(upstream)
Reports whether an upstream must receive no new request.
function upstreamDraining(upstream: UpstreamMember): boolean
upstream— The candidate.
Returns: true while it is draining.
resetUpstreamBalancerForTest()
Drops every cursor and mark. Test-only; application code never calls it.
function resetUpstreamBalancerForTest(): void
Available Providers
| Provider | Package |
|---|---|
| Default | @molecule/api-upstream-balancer-default |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1
Runtime Dependencies
-
@molecule/api-bond -
A single-member set is returned unchanged. This is what keeps a one-instance deployment on exactly its previous code path.
-
A failure mark is a cooldown, not a verdict. After the policy's cooldown the member is eligible again even if nothing succeeded against it.
-
Never blank the set. When every member is suspect or draining, one is still returned — answering every request with an error is worse than one more try.
-
Draining means no NEW requests. A deploy marks a retiring instance so the edge stops choosing it while its in-flight requests finish.