js/load
js/load.ts
fino:load — bounded HTTP and scripted protocol load generation.
Runs repeatable HTTP/1.1, HTTP/2, or HTTP/3 workloads through
fino:net/http/client. The runner separates physical connections from
multiplexed stream concurrency, warms the same client before measurement,
streams every response body, and retains only bounded histogram/counter
state. Use it for local throughput and latency checks where exercising the
Fino client stack is part of the measurement.
Response lifecycle
consume is the default response policy. It reads each response chunk,
counts its decoded bytes, and immediately drops it; complete bodies are
never retained. cancel stops after final headers. That closes the current
HTTP/1.1 connection or resets only the HTTP/2 or HTTP/3 stream, so results
always record the selected policy and never compare the two as equivalent
workloads.
Scheduling and workloads
Without rate, scheduling is closed-loop: each worker starts its next
request after the previous response policy completes. Supplying rate
switches to a bounded open-loop scheduler. Arrivals retain their intended
timestamp while queued, so latency includes scheduler delay rather than
hiding coordinated omission. Excess arrivals are counted as dropped.
Each HTTP run uses one static request so the saturation path does no workload selection or data generation. Exactly one measured duration or operation count can be selected. Defaults remain 10 seconds, 10 connections, one stream per connection, a 30-second request timeout, and a 1 MiB unread-response bound. Use a scripted scenario when request behavior must vary over time.
Stateful workloads use runLoadScenario(). Each virtual user receives a
controlled client that opens HTTP, SSE, WebSocket, WebTransport, and raw
QUIC resources through Fino's public protocol clients. The context provides
cancellation, bounded custom histograms, and byte/message counters;
scenarios define their own inputs, framing, and success rules.
Latencies use monotonic performance.now() timestamps and fixed-size
logarithmic histograms. Response byte counts are decoded application bytes
when decompression is enabled and encoded bytes when it is disabled.
import { formatLoadResult, runLoad } from 'fino:load';
const result = await runLoad({
url: 'https://localhost:3000/health',
protocol: 'h2',
connections: 10,
streams: 20,
durationMs: 30_000,
warmupMs: 5_000,
});
console.log(formatLoadResult(result));
Useful references:
- HTTP semantics: https://www.rfc-editor.org/rfc/rfc9110
- HTTP/2: https://www.rfc-editor.org/rfc/rfc9113
- HTTP/3: https://www.rfc-editor.org/rfc/rfc9114
- Fetch bodies: https://fetch.spec.whatwg.org/#concept-body
- Server-sent events: https://html.spec.whatwg.org/multipage/server-sent-events.html
- WebSocket: https://www.rfc-editor.org/rfc/rfc6455
- WebTransport: https://www.w3.org/TR/webtransport/
- QUIC: https://www.rfc-editor.org/rfc/rfc9000
Types
type LoadProtocol = 'http/1.1' | 'h2' | 'h3'
HTTP version pinned for every operation in one load run.
type LoadResponsePolicy = 'consume' | 'cancel'
How each response is released after final headers arrive.
type LoadBody = string | Uint8Array | ArrayBuffer
Static, replayable request bodies accepted by the load runner.
type LoadRate = number | { readonly start: number; readonly end: number }
A constant rate or a linear start-to-end arrival-rate ramp, in operations per second.
type LoadScenarioProtocol = 'http' | 'sse' | 'websocket' | 'webtransport' | 'quic'
Protocol selected by a scripted load scenario.
Interfaces
interface LoadBailoutOptions {
Failure thresholds that stop a run early after the threshold is reached.
Readonly Properties
readonly failures?: number
Maximum status/body expectation failures before stopping.
readonly errors?: number
Maximum timeout or transport failures before stopping.
interface LoadTlsOptions {
TLS identity and verification options applied to every connection.
Properties
ca?: string
Certificate authority PEM file used to verify the server.
rejectUnauthorized?: boolean
Whether peer certificate verification is required. Defaults to true.
cert?: string
Client certificate PEM file for mutual TLS.
key?: string
Client private-key PEM file for mutual TLS.
interface LoadOptions {
HTTP configuration for runLoad().
When neither durationMs nor requests is supplied, the measured phase
lasts 10 seconds. Supplying both throws. HTTP/1.1 requires
streams: 1; stream concurrency greater than one is meaningful only for
multiplexed HTTP/2 and HTTP/3 connections. H2 and H3 require https: URLs;
the current client does not offer h2c load generation.
Properties
url: string | URL
Absolute HTTP(S) target URL.
protocol?: LoadProtocol
HTTP version to require. Defaults to 'http/1.1'.
method?: string
Request method. Defaults to GET.
headers?: HttpHeadersInit
Headers applied to every request.
body?: LoadBody
Static request body replayed for every operation.
connections?: number
Physical connections maintained for the target origin. Defaults to 10.
streams?: number
Concurrent streams per HTTP/2 or HTTP/3 connection. Defaults to 1.
durationMs?: number
Measured phase duration in milliseconds. Mutually exclusive with requests.
requests?: number
Exact measured operation count. Mutually exclusive with durationMs.
warmupMs?: number
Unmeasured warmup duration using the same client and connections. Defaults to 0.
rate?: LoadRate
Fixed or linearly ramped offered rate in operations per second. Omit for closed-loop.
maxQueuedOperations?: number
Maximum open-loop arrivals waiting for a worker. Defaults to the concurrency.
reconnectAfter?: number
Recreate pooled sessions after this many started operations.
responsePolicy?: LoadResponsePolicy
Response release policy. Defaults to 'consume'.
expectedStatus?: number | readonly number[]
Status code or codes considered successful. Defaults to the 200-399 range.
expectedBody?: string | Uint8Array
Exact response body expected, matched while streaming.
bailout?: LoadBailoutOptions
Optional failure thresholds that abort the measured phase early.
timeouts?: HttpClientTimeouts
Request deadline policy. Total timeout defaults to 30 seconds.
maxPendingRequests?: number
Maximum requests queued inside HttpClient. Defaults to the worker count.
maxBufferedResponseBytes?: number
Maximum unread bytes per multiplexed response. Defaults to 1 MiB.
retryAttempts?: number
Total replay-safe attempts per request. Defaults to 1.
decompress?: boolean
Whether to decode compressed responses before counting bytes. Defaults to true.
redirect?: HttpRequestInit['redirect']
Redirect behavior forwarded to HttpClient. Defaults to follow.
tls?: LoadTlsOptions
TLS policy applied to every physical connection.
title?: string
Optional title included in text and JSON output.
signal?: AbortSignal
Signal that stops scheduling and aborts active requests.
interface LoadHistogramSnapshot {
Bounded distribution summary, expressed in milliseconds.
Readonly Properties
readonly count: number
Number of recorded observations.
readonly min: number | null
Smallest observation, or null when empty.
readonly mean: number | null
Arithmetic mean, or null when empty.
readonly stddev: number | null
Population standard deviation, or null when empty.
readonly p50: number | null
50th percentile, or null when empty.
readonly p75: number | null
75th percentile, or null when empty.
readonly p90: number | null
90th percentile, or null when empty.
readonly p95: number | null
95th percentile, or null when empty.
readonly p99: number | null
99th percentile, or null when empty.
readonly p999: number | null
99.9th percentile, or null when empty.
readonly max: number | null
Largest observation, or null when empty.
interface LoadLatencySummary {
Latency distributions captured for completed or partially completed operations.
Readonly Properties
readonly queue: LoadHistogramSnapshot
Time waiting for local client capacity.
readonly ttfb: LoadHistogramSnapshot
Time from scheduling to final response headers.
readonly download: LoadHistogramSnapshot
Time from final headers to response policy completion.
readonly total: LoadHistogramSnapshot
Time from scheduling to response policy completion.
readonly connect: LoadHistogramSnapshot
DNS and new-connection time when exposed by the transport.
readonly tls: LoadHistogramSnapshot
TLS or QUIC secure-handshake time when exposed by the transport.
interface LoadCounters {
Outcome counters for the measured phase.
Readonly Properties
readonly offered: number
Operations offered to the scheduler.
readonly started: number
Operations whose request attempt started.
readonly completed: number
Operations that finished the selected response policy.
readonly successful: number
Completed operations whose final status matched the expectation.
readonly statusFailed: number
Completed operations whose final status did not match the expectation.
readonly timedOut: number
Operations stopped by a request timeout.
readonly cancelled: number
Operations stopped by the run signal or measured-phase deadline.
readonly transportFailed: number
Operations rejected by DNS, connection, protocol, or body transport errors.
readonly schedulerDropped: number
Open-loop arrivals dropped before starting because the scheduler queue was full.
readonly bodyFailed: number
Completed responses whose body did not match the exact streaming expectation.
readonly headersOnly: number
Responses deliberately stopped after final headers under cancel policy.
readonly responseBytes: number
Body bytes streamed and dropped under consume policy.
interface LoadResultTlsConfig {
Non-secret TLS policy recorded with a load result.
Readonly Properties
readonly rejectUnauthorized: boolean
Whether peer certificate verification was required.
readonly customCa: boolean
Whether a custom certificate-authority file was configured.
readonly clientCertificate: boolean
Whether a client certificate and key were configured.
interface LoadResultConfig {
Effective, normalized configuration recorded with a load result.
Readonly Properties
readonly url: string
Absolute target URL.
readonly method: string
Uppercase request method.
readonly headerNames: readonly string[]
Request header names, without potentially secret values.
readonly requestBodyBytes: number
Static request body size in bytes.
readonly protocol: LoadProtocol
Required HTTP protocol.
readonly connections: number
Physical connection count.
readonly streams: number
Streams per connection; always 1 for HTTP/1.1.
readonly concurrency: number
Total operation worker count.
readonly rate: LoadRate | null
Arrival-rate policy, or null for closed-loop scheduling.
readonly maxQueuedOperations: number
Maximum open-loop scheduler queue depth.
readonly reconnectAfter: number | null
Started-operation reconnect cadence, or null when disabled.
readonly durationMs: number | null
Requested measured duration, or null for an exact request-count run.
readonly requests: number | null
Requested operation count, or null for a duration run.
readonly warmupMs: number
Unmeasured warmup duration.
readonly responsePolicy: LoadResponsePolicy
Selected response release policy.
readonly expectedStatus: readonly number[] | null
Explicit acceptable status codes, or null for the 200-399 default.
readonly expectedBody: boolean
Whether an exact streaming body expectation was configured.
readonly decompress: boolean
Whether response content was transparently decoded.
readonly maxBufferedResponseBytes: number
Maximum unread response bytes per multiplexed stream.
readonly maxPendingRequests: number
Maximum requests allowed to wait inside HttpClient.
readonly retryAttempts: number
Total replay-safe attempts allowed per operation.
readonly redirect: NonNullable<HttpRequestInit['redirect']>
Redirect policy applied to every request.
readonly tls: LoadResultTlsConfig
Non-secret TLS identity and verification summary.
readonly timeouts: Readonly<HttpClientTimeouts>
Per-request deadline policy after defaults were applied.
interface LoadConnectionSummary {
Physical-connection observations from measured responses.
Readonly Properties
readonly unique: number
Distinct physical connection IDs observed.
readonly reusedResponses: number
Responses marked as reusing an established connection.
readonly reconnects: number
Connections beyond the configured initial slot count.
readonly maxActiveOperations: number
Highest number of simultaneously active operations.
interface LoadResult {
Versioned, JSON-safe output returned by runLoad().
Readonly Properties
readonly schemaVersion: 2
Result schema version. Currently 2.
readonly title: string | null
Optional user-supplied run title.
readonly startedAt: string
Wall-clock ISO timestamp at the beginning of measurement.
readonly durationMs: number
Actual measured elapsed time, including cancellation/drain settlement.
readonly config: LoadResultConfig
Effective run configuration.
readonly counters: LoadCounters
Measured operation and byte counters.
readonly requestsPerSecond: number
Completed operations per second.
readonly bytesPerSecond: number
Consumed response body bytes per second.
readonly statusCodes: Readonly<Record<string, number>>
Final HTTP status distribution keyed by decimal status.
readonly protocols: Readonly<Record<string, number>>
Negotiated protocol distribution.
readonly errors: Readonly<Record<string, number>>
Bounded error-class distribution.
readonly connections: LoadConnectionSummary
Physical connection observations.
readonly latency: LoadLatencySummary
Bounded latency distributions in milliseconds.
readonly bailout: string | null
Reason a bailout threshold stopped the run, or null.
interface LoadScenarioOptions {
Options controlling bounded virtual-user scenario execution.
Readonly Properties
readonly users?: number
Number of concurrent scenario sessions. Defaults to 1.
readonly durationMs?: number
Measured duration in milliseconds. Mutually exclusive with sessions.
readonly sessions?: number
Exact scenario-session count. Mutually exclusive with durationMs.
readonly rate?: LoadRate
Optional fixed or ramped session arrival rate.
readonly maxQueuedSessions?: number
Maximum scheduled sessions waiting for a virtual user. Defaults to users.
readonly maxMetrics?: number
Maximum distinct custom metric names. Defaults to 64.
readonly maxLogs?: number
Maximum total log calls retained as counters. Defaults to 1000.
readonly signal?: AbortSignal
Signal that stops scheduling and notifies active scenario hooks.
interface LoadScenarioMetricResult extends LoadHistogramSnapshot {
A custom scenario metric snapshot. Values are scenario-defined.
Readonly Properties
readonly total: number
Sum of all observations.
interface LoadScenarioResult {
Versioned result from runLoadScenario().
Readonly Properties
readonly schemaVersion: 1
Scenario result schema version.
readonly protocol: LoadScenarioProtocol
Declared scenario protocol.
readonly startedAt: string
Wall-clock ISO timestamp at the start of measurement.
readonly durationMs: number
Measured elapsed time.
readonly offered: number
Sessions offered to the scheduler.
readonly started: number
Sessions that began executing.
readonly completed: number
Sessions that completed without throwing.
readonly failed: number
Sessions that threw or were rejected.
readonly dropped: number
Open-loop sessions dropped from a full scheduler queue.
readonly maxActive: number
Highest concurrently active session count.
readonly bytes: { readonly sent: number; readonly received: number }
Application bytes reported by scenario helpers.
readonly messages: { readonly sent: number; readonly received: number }
Application messages reported by scenario helpers.
readonly errors: Readonly<Record<string, number>>
Bounded error-name distribution.
readonly metrics: Readonly<Record<string, LoadScenarioMetricResult>>
Bounded custom histogram snapshots.
readonly logs: { readonly accepted: number; readonly dropped: number }
Number of scenario log calls accepted and dropped by the safety limit.
interface LoadScenarioClient {
Controlled protocol constructors supplied to a scenario session.
Methods
request(input: string | URL, init?: HttpRequestInit): Promise<HttpResponse>
Send one streaming HTTP request. The scenario owns response disposal.
sse(input: string | URL, options?: SseOptions): EventSource
Open an auto-reconnecting SSE stream.
websocket(input: string | URL, options?: HttpWebSocketOptions): Promise<WebSocketConnection>
Open an HTTP/1.1 WebSocket and wait for OPEN.
webtransport(input: string | URL, options?: HttpWebTransportOptions): Promise<WebTransport>
Open an HTTP/3 WebTransport session and wait for ready.
quic(options: {
readonly endpoint?: ConstructorParameters<typeof QuicEndpoint>[0];
readonly connect: QuicConnectOptions;
}): Promise<QuicConnection>
Open a raw QUIC connection owned by this scenario session.
interface LoadScenarioContext {
Per-session deterministic state and bounded metric surface.
Readonly Properties
readonly sequence: number
Zero-based session sequence assigned before asynchronous execution.
readonly userId: number
Stable virtual-user index.
readonly signal: AbortSignal
Signal canceled when the measured phase or parent run ends.
Methods
metric(name: string, value: number): void
Record a finite non-negative custom metric observation.
bytes(direction: 'sent' | 'received', count: number): void
Add application byte counts to the result.
messages(direction: 'sent' | 'received', count?: number): void
Add application message counts to the result.
log(...values: unknown[]): void
Emit a bounded diagnostic log entry. Excess calls are counted and dropped.
interface LoadScenario {
Scripted stateful workload executed once per scheduled scenario session.
Readonly Properties
readonly protocol: LoadScenarioProtocol
Transport family used by the scenario.
readonly options?: LoadScenarioOptions
Optional default scheduler configuration, overridden by runLoadScenario() options.
Methods
session(client: LoadScenarioClient, context: LoadScenarioContext): void | Promise<void>
Execute one bounded virtual-user session.
Functions
function runLoad(options: LoadOptions): Promise<LoadResult>
Run one bounded closed-loop or open-loop HTTP load test.
The promise resolves after warmup, measurement, active-request settlement,
and client shutdown. Configuration errors throw before network work begins.
Transport errors are counted in the returned result rather than rejecting
the whole run. If signal aborts, scheduling stops and the partial measured
result is returned after active requests settle.
function runLoadScenario(
scenario: LoadScenario,
options: LoadScenarioOptions = {},
): Promise<LoadScenarioResult>
Run a scripted stateful workload through bounded Fino protocol clients.
Scenario modules define application framing and success. The runner owns and
closes every resource created through client, records thrown exceptions,
and caps scheduler queues, custom metric names, and log volume. A scenario
must still close or finish protocol-level streams it creates before its
session() hook resolves.
function formatLoadResult(result: LoadResult): string
Render a stable human-readable summary for a completed load result.
function formatLoadScenarioResult(result: LoadScenarioResult): string
Render a stable human-readable summary for a scripted scenario result.