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:

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.