@mark1russell7/service 0.5.11 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -1
- package/build/{builder-Dn0cby6Q.d.ts → builder-DawJhq4p.d.ts} +2 -2
- package/build/core-BPLRDcUJ.d.ts +226 -0
- package/build/dial-DA8vJfh2.d.ts +16 -0
- package/build/grpc.d.ts +307 -0
- package/build/grpc.js +859 -0
- package/build/grpc.js.map +1 -0
- package/build/http-client.d.ts +248 -0
- package/build/http-client.js +612 -0
- package/build/http-client.js.map +1 -0
- package/build/http.d.ts +1 -1
- package/build/index.d.ts +8 -149
- package/build/index.js +155 -11
- package/build/index.js.map +1 -1
- package/build/{lifecycle-CGEegMI0.d.ts → lifecycle-fgYfxrHK.d.ts} +17 -2
- package/build/machine-BhTRu3u9.js +552 -0
- package/build/machine-BhTRu3u9.js.map +1 -0
- package/build/mongodb.d.ts +307 -0
- package/build/mongodb.js +688 -0
- package/build/mongodb.js.map +1 -0
- package/build/pg.d.ts +230 -0
- package/build/pg.js +531 -0
- package/build/pg.js.map +1 -0
- package/build/queue-snRHK3Jm.d.ts +150 -0
- package/build/rabbitmq.d.ts +294 -0
- package/build/rabbitmq.js +748 -0
- package/build/rabbitmq.js.map +1 -0
- package/build/redis.d.ts +24 -143
- package/build/redis.js +76 -315
- package/build/redis.js.map +1 -1
- package/build/testing.d.ts +2 -2
- package/build/websocket.d.ts +275 -0
- package/build/websocket.js +780 -0
- package/build/websocket.js.map +1 -0
- package/package.json +46 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-BhTRu3u9.js","names":["dialTcp"],"sources":["../src/client/meets.ts","../src/client/core.ts","../src/client/dial.ts","../src/client/machine.ts"],"sourcesContent":["// The manifestations that a client can meet, and their detectors (E9). The shapes, the two queries,\n// the detectors of the kernel and the words of each manifestation are the same for each client.\n// Each client entry gives its own list of meetings, because the timers of each driver differ.\n\nimport type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\n\n/** A situation is what the connection did when the failure started: a command in flight, or idle. */\nexport type Situation = \"in flight\" | \"idle\";\n\n/** A detector is a mechanism that finds a failure, and the most time that it takes. */\nexport interface Detector {\n /** This name identifies the mechanism: a knob of the declaration, or the kernel. */\n readonly by: string;\n /** This time is the most that the detector takes, after the failure starts. \"infinite\" tells that it does not find the failure. */\n readonly within: Extent;\n readonly situation: Situation;\n /** This text tells what the detector does, for example that it fails a command without a change of state. */\n readonly effect: string;\n}\n\n/**\n * A meeting is a manifestation that the client can meet, and its detectors (E9). The names of the\n * manifestations are the names of the vocabulary of the harness and the corpus. `app-stall` is a\n * server whose process is alive and does not answer, a proposal of the vocabulary.\n */\nexport interface Meeting {\n readonly manifestation: string;\n /** This text tells the failure in words. */\n readonly what: string;\n readonly detectors: readonly Detector[];\n}\n\n/** These are the manifestations that each client gives a meeting for, in this sequence. */\nexport const MANIFESTATIONS = [\"rst\", \"fin\", \"blackhole\", \"freeze-host\", \"freeze-process\", \"app-stall\"] as const;\n\n/** A manifestation is one name of `MANIFESTATIONS`. */\nexport type Manifestation = (typeof MANIFESTATIONS)[number];\n\n/**\n * These words tell each manifestation that does not depend on the driver. The words of `app-stall`\n * name an example, and the example is a fact of each server, thus each client gives them.\n */\nexport const WHAT: Readonly<Record<Exclude<Manifestation, \"app-stall\">, string>> = {\n rst: \"the server answers each segment with a reset, and refuses each new connection\",\n fin: \"the process of the server ends, and its kernel closes the connection\",\n blackhole: \"the network drops each segment in the two directions\",\n \"freeze-host\": \"the host of the server stops, without a close: the death without a close\",\n \"freeze-process\": \"the process of the server stops, and its kernel stays up\",\n};\n\n/** This amount is a detector that does not find the failure. */\nexport const NEVER: Extent = { value: \"infinite\", unit: \"s\" };\n\nconst s = (value: number): Quantity => quantity(Math.round(value * 1e6) / 1e6, \"s\");\nconst seconds = (amount: Extent): number =>\n amount.value === \"infinite\" ? Number.POSITIVE_INFINITY : valueIn(amount as Quantity, \"s\");\n\n/** Node gives keepalive 10 probes, 1 s apart, after its idle time: a silent peer is found 10 s after the idle time. */\nconst NODE_PROBES = 10;\nconst NODE_INTERVAL = 1;\n\n/** This function gives the time at which Node's TCP keepalive gives up a silent peer: the idle time, then 10 probes 1 s apart. */\nexport const keepaliveGiveUp = (idle: Extent): Extent => (idle.value === \"infinite\" ? NEVER : s(seconds(idle) + NODE_PROBES * NODE_INTERVAL));\n\n/** This detector is a segment that arrives at once: a reset or a FIN. */\nexport const immediate = (situation: Situation, effect: string): Detector => ({ by: \"the next segment\", within: s(0), situation, effect });\n\n/** This detector is TCP keepalive on a silent peer. It gives up after its probes, and the socket closes. */\nexport const keepaliveSilent = (idle: Extent): Detector => ({\n by: \"keepalive_idle\",\n within: keepaliveGiveUp(idle),\n situation: \"idle\",\n effect: \"TCP keepalive gives up, and the socket closes\",\n});\n\n/** This detector is TCP keepalive on a peer whose kernel stays up. That kernel answers each probe, thus keepalive does not find the failure. */\nexport const keepaliveAnswered = (): Detector => ({\n by: \"keepalive_idle\",\n within: NEVER,\n situation: \"idle\",\n effect: \"the kernel of the server answers each probe\",\n});\n\n/** This detector is TCP keepalive on a peer that resets: the first probe gets the reset. */\nexport const keepaliveReset = (idle: Extent): Detector => ({\n by: \"keepalive_idle\",\n within: idle.value === \"infinite\" ? NEVER : idle,\n situation: \"idle\",\n effect: \"the first probe gets a reset\",\n});\n\n/** This detector is the kernel, which gives up the retransmissions of a segment in flight. A driver can add what follows. */\nexport const kernelGiveUp = (within: Extent, after?: string): Detector => ({\n by: \"retrans_giveup_time\",\n within,\n situation: \"in flight\",\n effect: `the kernel gives up the retransmissions and closes the socket${after === undefined ? \"\" : `, and ${after}`}`,\n});\n\n/** This function gives the first detector of a manifestation in a situation, or null when no detector has an end (C3). */\nexport const firstDetector = (meeting: Meeting, situation: Situation): Detector | null => {\n const ranked = meeting.detectors\n .filter((detector) => detector.situation === situation && detector.within.value !== \"infinite\")\n .sort((a, b) => seconds(a.within) - seconds(b.within));\n return ranked[0] ?? null;\n};\n\n/** This function gives each manifestation and situation that no detector of the client finds: the standing rule, as a query (C3, E9). */\nexport const unwatched = (meets: readonly Meeting[]): Array<{ readonly manifestation: string; readonly situation: Situation }> =>\n meets.flatMap((meeting) =>\n ([\"in flight\", \"idle\"] as const).flatMap((situation) =>\n firstDetector(meeting, situation) === null ? [{ manifestation: meeting.manifestation, situation }] : [],\n ),\n );\n","// The parts of a client wrapper that do not change from one client to the next. Each client entry\n// (`./redis`, `./pg`, `./http-client`, `./websocket`, and each later client of S1) fills them in with its driver. The core gives\n// the shapes of a client, the outcome of a call in three values, the rule that sends a call again,\n// the record of the calls in flight, the bounded close, the checks of the steps of a connection,\n// and the problems of a spec and of a client in a description. It loads only quantity.\n// The section \"Unified or separate\" of docs/S6.md gives each part, and the reason for each part that stays in a driver.\n\nimport type { Extent, Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Backoff } from \"../backoff.ts\";\nimport { backoffDelay } from \"../backoff.ts\";\nimport type { ServiceBuilder } from \"../builder.ts\";\nimport type { Cancel, Clock } from \"../clock.ts\";\nimport type { Check, Dependency, Gate, ServiceDescription, Step } from \"../description.ts\";\nimport type { Recorder } from \"../events.ts\";\nimport type { Outcome } from \"../latch.ts\";\nimport type { DependencyHandle } from \"../lifecycle.ts\";\nimport type { Operation } from \"../operation.ts\";\nimport { mayReissue } from \"../operation.ts\";\nimport type { CheckFn } from \"../poller.ts\";\nimport type { Poll } from \"../timing.ts\";\nimport type { Meeting } from \"./meets.ts\";\n\n/**\n * A sent result is the outcome of a call, in three values (C8):\n * - `ok`: the server answered.\n * - `error`: the call did not apply. The server answered with an error (`reply`), or the call did\n * not leave the client.\n * - `indeterminate`: the call can have reached the server, and nothing tells if it applied.\n */\nexport type Sent<T> =\n | { readonly outcome: \"ok\"; readonly value: T; readonly attempts: number }\n | { readonly outcome: \"error\"; readonly error: string; readonly reply: boolean; readonly attempts: number }\n | { readonly outcome: \"indeterminate\"; readonly error: string; readonly attempts: number };\n\n/** This type is the outcome of one try of a call, before the core counts the tries. */\nexport type Attempt<T = unknown> =\n | { readonly outcome: \"ok\"; readonly value: T }\n | { readonly outcome: \"error\"; readonly error: string; readonly reply: boolean }\n | { readonly outcome: \"indeterminate\"; readonly error: string };\n\n/** Send options tell how the client sends one call. */\nexport interface SendOptions {\n /** This signal abandons the call. A call that left the client is then indeterminate. */\n readonly signal?: AbortSignal | undefined;\n /** False stops the client: it does not send the call again. A check that must fail at once uses it. */\n readonly reissue?: boolean | undefined;\n}\n\n/** A dial function opens a new connection to the server, and reads one reply of the protocol. The `dial` check uses it. */\nexport type DialFn = (host: string, port: number, signal: AbortSignal) => Promise<void>;\n\n/** The steps of a connection that each client gives a check for (B14). A client can add more steps. */\nexport type ClientStep = Extract<Step, \"dial\" | \"round-trip\" | \"local-state\">;\n\n/** Check options tell which checks the client gives the health runtime, and which policy each one controls. */\nexport interface CheckOptions<S extends Step = ClientStep> {\n readonly poll: Poll;\n /** For each step, the policy that its check controls, or null for a check that only reports. A step that is absent has no check. */\n readonly steps: Partial<Record<S, Gate | null>>;\n}\n\n/** A step check is the function of one step, and if it uses the connection of the application. */\nexport interface StepCheck {\n readonly ownConnection: boolean;\n readonly run: CheckFn;\n}\n\n/** This function gives an amount in milliseconds. \"infinite\" is `Infinity`. */\nexport const ms = (amount: Extent): number =>\n amount.value === \"infinite\" ? Number.POSITIVE_INFINITY : valueIn(amount as Quantity, \"ms\");\n\n/** This function gives an amount in milliseconds, or undefined for \"infinite\". Each driver then writes its own form of \"no timer\". */\nexport const finiteMs = (amount: Extent): number | undefined => {\n const value = ms(amount);\n return Number.isFinite(value) ? value : undefined;\n};\n\n/** This function gives the text of an error. */\nexport const message = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/**\n * These are the knobs that each client has, by the names of the ordering law. Each driver adds its\n * own knobs. The comments of each driver tell which option of the driver a knob sets.\n */\nexport interface ClientKnobs {\n /** This time bounds a new connection. */\n readonly connect_bound: Quantity;\n /** This time bounds each call on the connection of the application. */\n readonly request_timeout: Quantity;\n /** This time is the idle time of TCP keepalive. Node then sends 10 probes 1 s apart. */\n readonly keepalive_idle: Extent;\n /** This time is the age at which the client replaces a connection. */\n readonly max_connection_age: Extent;\n /** This time bounds the close of the client at the shutdown. */\n readonly close_bound: Quantity;\n /**\n * This time is when the kernel gives up a segment that it retransmits. Node cannot set it, thus\n * the declaration states it for the ordering law (O17), from the platform.\n */\n readonly retrans_giveup_time: Extent;\n}\n\n/** A client spec is the declaration of one client: where it connects, and its knobs. Each driver adds its own fields. */\nexport interface ClientSpec<K extends ClientKnobs = ClientKnobs> {\n readonly host: string;\n readonly port: number;\n readonly password?: string | undefined;\n /** This identifier is the component in the corpus, for example `systems.component.redis`, or null. */\n readonly system: string | null;\n readonly knobs: K;\n /** These operations add to the table of the client, or change it. */\n readonly operations?: Readonly<Record<string, Operation>> | undefined;\n}\n\n/** A factory makes the connection or the pool of a client from the options of its driver. */\nexport type Factory<O, C> = (options: O) => C | Promise<C>;\n\n/** These options make a client. Without them, a client uses its driver, the real clock, and the dial of its protocol. */\nexport interface ClientOptions<F> {\n readonly create?: F | undefined;\n readonly clock?: Clock | undefined;\n /** This function receives each transition of the client, for the prober (D3). */\n readonly record?: Recorder | undefined;\n readonly dial?: DialFn | undefined;\n}\n\n/** A client socket is the part of the socket of a connection that the join of the prober and the bounded close read. */\nexport interface ClientSocket {\n readonly localAddress?: string | undefined;\n readonly localPort?: number | undefined;\n readonly remoteAddress?: string | undefined;\n readonly remotePort?: number | undefined;\n /** This method closes the socket at once. A close of the driver only half-closes it, and a silent peer does not answer that. */\n destroy?(): void;\n}\n\n/**\n * A client join is one connection, at the time when it can serve (D1, D2). The prober joins its\n * kernel records to the server's view of the connection through it. Each driver adds the server's id of the connection.\n */\nexport interface ClientJoin {\n readonly generation: number;\n /** This time is when the connection became ready, in milliseconds on the monotonic clock. */\n readonly at: number;\n readonly localAddress: string | null;\n readonly localPort: number | null;\n readonly remoteAddress: string | null;\n readonly remotePort: number | null;\n}\n\n/** This function gives the addresses and the ports of a socket for a join. Each one is null when the socket does not give it. */\nexport const socketEnds = (\n socket: ClientSocket | undefined,\n): Pick<ClientJoin, \"localAddress\" | \"localPort\" | \"remoteAddress\" | \"remotePort\"> => ({\n localAddress: socket?.localAddress ?? null,\n localPort: socket?.localPort ?? null,\n remoteAddress: socket?.remoteAddress ?? null,\n remotePort: socket?.remotePort ?? null,\n});\n\n/** A client is a dependency of a service: its declaration, its handle and its checks. Each driver adds the method that sends a call. */\nexport interface Client<S extends Step = ClientStep, J extends ClientJoin = ClientJoin> {\n readonly name: string;\n /** This declaration goes to `svc.dependency(name, declaration, handle)`. */\n readonly declaration: Omit<Dependency, \"name\">;\n readonly handle: DependencyHandle;\n /** These are the manifestations that the client can meet, and the detectors of each (E9). */\n readonly meets: readonly Meeting[];\n /** This text is the state of the client, as the machine document of the client names it. */\n readonly status: string;\n /** These are the connections of the client, the latest last. */\n readonly joins: readonly J[];\n /** This method gives the checks of the client and their functions, for `svc.check(check, run)`. */\n checks(options: CheckOptions<S>): ReadonlyArray<readonly [Check, CheckFn]>;\n /** This method declares the client in a service: its dependency, its handle and its checks. */\n attach(svc: ServiceBuilder, options: CheckOptions<S>): ServiceBuilder;\n /** This method gives the problems of the client in a description: a close that does not fit in its phase, for example. */\n problems(description: ServiceDescription): string[];\n}\n\n/** This function declares a client in a service: its dependency, its handle, and each of its checks. */\nexport const attachClient = (\n svc: ServiceBuilder,\n name: string,\n declaration: Omit<Dependency, \"name\">,\n handle: DependencyHandle,\n checks: ReadonlyArray<readonly [Check, CheckFn]>,\n): ServiceBuilder => {\n svc.dependency(name, declaration, handle);\n for (const [check, run] of checks) svc.check(check, run);\n return svc;\n};\n\n/** This function gives the table of operations of a client: the defaults of its driver, then the operations of its spec. */\nexport const operationTable = (\n defaults: Readonly<Record<string, Operation>>,\n more: Readonly<Record<string, Operation>> | undefined,\n): Readonly<Record<string, Operation>> => ({ ...defaults, ...more });\n\n/** A client recorder gives one transition of a client to the recorder of the options, for the prober (D3). */\nexport type ClientRecorder = (from: string, to: string, on: string, detail?: string) => void;\n\n/** This function makes the recorder of a client. Each event has the machine \"client\", and the name of the client as its instance. */\nexport const clientRecorder =\n (record: Recorder | undefined, clock: Clock, name: string): ClientRecorder =>\n (from, to, on, detail) =>\n record?.({ at: clock.now(), machine: \"client\", instance: name, from, to, on, ...(detail === undefined ? {} : { detail }) });\n\n/** This function gives the outcome of a call that did not leave the client. It is certain: the call did not apply. */\nexport const notSent = (error: string): Attempt<never> => ({ outcome: \"error\", error, reply: false });\n\n/** A settle function gives the outcome of a call in flight. A driver can give a cause, for example the error that ends a client of a pool. */\nexport type Settle<T = unknown> = (value: Attempt<T>, cause?: Error) => void;\n\n/**\n * This record holds the calls in flight of a client. Each call settles one time. A call that the\n * caller abandons after it left the client is indeterminate, because a sent call cannot be\n * cancelled (principle 9). The close and a closed connection settle each call that stays (C10).\n */\nexport interface InFlight {\n /** This number is the count of the calls in flight. */\n readonly size: number;\n /**\n * This method sends one call, and records it until it settles. `start` sends the call, and gives\n * its outcome to `settle`. `after` operates one time, after the call settles and before its promise resolves.\n */\n track<T>(\n signal: AbortSignal | undefined,\n abandoned: string,\n start: (settle: Settle<T>) => void,\n after?: (value: Attempt<T>, cause: Error | undefined) => void,\n ): Promise<Attempt<T>>;\n /** This method settles each call in flight as indeterminate, with this error. */\n settleAll(error: string): void;\n /** This method resolves when no call is in flight. A close that waits for its calls uses it in its bound. */\n idle(): Promise<void>;\n}\n\n/** This function makes the record of the calls in flight of a client. */\nexport const inFlight = (): InFlight => {\n const closes = new Set<(error: string) => void>();\n const waiters = new Set<() => void>();\n const wake = (): void => {\n if (closes.size > 0) return;\n for (const waiter of [...waiters]) waiter();\n waiters.clear();\n };\n return {\n get size() {\n return closes.size;\n },\n idle: () =>\n new Promise<void>((resolve) => {\n if (closes.size === 0) resolve();\n else waiters.add(resolve);\n }),\n track<T>(\n signal: AbortSignal | undefined,\n abandoned: string,\n start: (settle: Settle<T>) => void,\n after?: (value: Attempt<T>, cause: Error | undefined) => void,\n ): Promise<Attempt<T>> {\n return new Promise<Attempt<T>>((resolve) => {\n let settled = false;\n const settle: Settle<T> = (value, cause) => {\n if (settled) return;\n settled = true;\n closes.delete(close);\n signal?.removeEventListener(\"abort\", aborted);\n after?.(value, cause);\n resolve(value);\n wake();\n };\n const close = (error: string): void => settle({ outcome: \"indeterminate\", error }, new Error(error));\n const aborted = (): void => close(abandoned);\n closes.add(close);\n signal?.addEventListener(\"abort\", aborted, { once: true });\n start(settle);\n });\n },\n settleAll(error) {\n for (const close of [...closes]) close(error);\n },\n };\n};\n\n/**\n * This function sends one call through the record of the calls in flight, with a timer of the\n * wrapper. A driver with no timer for its call uses it: amqplib, and the agent of Node. At `bound`\n * milliseconds the call is indeterminate with the error `timedOut`, because it left the client.\n * The timer stops when the call settles, before `after` operates.\n */\nexport const trackWithTimer = <T>(\n flight: InFlight,\n clock: Clock,\n bound: number,\n signal: AbortSignal | undefined,\n abandoned: string,\n timedOut: string,\n start: (settle: Settle<T>) => void,\n after?: (value: Attempt<T>, cause: Error | undefined) => void,\n): Promise<Attempt<T>> => {\n let stop: Cancel = () => {};\n return flight.track<T>(\n signal,\n abandoned,\n (settle) => {\n stop = clock.after(bound, () => settle({ outcome: \"indeterminate\", error: timedOut }));\n start(settle);\n },\n (value, cause) => {\n stop();\n after?.(value, cause);\n },\n );\n};\n\n/**\n * This function waits for the work of a close, for `bound` milliseconds at most. It gives true when\n * the work ended in its bound, with a success or a failure. It gives false at the bound. The driver\n * then destroys each socket that stays, because a silent server does not close its half.\n */\nexport const withinBound = (clock: Clock, bound: number, work: Promise<unknown>): Promise<boolean> => {\n const done = work.then(\n () => true,\n () => true,\n );\n let cancel: Cancel = () => {};\n const timer = new Promise<boolean>((resolve) => {\n cancel = clock.after(bound, () => resolve(false));\n });\n return Promise.race([done, timer]).then((ended) => {\n cancel();\n return ended;\n });\n};\n\n/**\n * A ready wait holds the calls that wait for a ready connection before their second try. A client\n * that connects again by itself uses it: Redis and RabbitMQ.\n */\nexport interface ReadyWait {\n /** This method waits until the client is ready, for `within` milliseconds at most. It gives true when the client is ready. */\n wait(within: number, signal?: AbortSignal): Promise<boolean>;\n /** This method ends each wait with true. The client uses it when its connection is ready. */\n wake(): void;\n}\n\n/** This function makes the ready wait of a client. The function `ready` tells if the client is ready. */\nexport const readyWait = (clock: Clock, ready: () => boolean): ReadyWait => {\n const waiters = new Set<() => void>();\n return {\n wait: (within, signal) =>\n new Promise((resolve) => {\n if (ready()) {\n resolve(true);\n return;\n }\n let cancel: Cancel = () => {};\n const done = (value: boolean): void => {\n waiters.delete(wake);\n cancel();\n signal?.removeEventListener(\"abort\", aborted);\n resolve(value);\n };\n const wake = (): void => done(true);\n const aborted = (): void => done(false);\n waiters.add(wake);\n cancel = clock.after(within, () => done(ready()));\n signal?.addEventListener(\"abort\", aborted, { once: true });\n }),\n wake() {\n for (const wake of [...waiters]) wake();\n },\n };\n};\n\n/** These are the knobs of a client that connects again by itself, after a delay that doubles. */\nexport interface RetryKnobs {\n /** This time is the first delay before a new try to connect. The delay doubles after each try. */\n readonly retry_base: Quantity;\n /** This time is the most that one delay can be, before its jitter. */\n readonly retry_cap: Quantity;\n}\n\n/**\n * This function gives the backoff of the tries to connect: from `retry_base`, doubling to\n * `retry_cap`, each with a jitter up to the base. The client rounds each delay to a whole millisecond.\n */\nexport const retryBackoff = (knobs: RetryKnobs): Backoff => ({\n base: ms(knobs.retry_base),\n factor: 2,\n cap: ms(knobs.retry_cap),\n jitter: { kind: \"add\", amount: ms(knobs.retry_base) },\n});\n\n/**\n * A retry schedule holds the next try to connect, for a client that connects again by itself. It\n * counts the tries since the last join. Each delay comes from the backoff, rounded to a whole millisecond.\n */\nexport interface RetrySchedule {\n /** This count is the number of tries since the last reset. */\n readonly tries: number;\n /** This method counts one more try, and starts `due` after the delay of that try. It stops a try that waits. */\n schedule(due: () => void): void;\n /** This method stops the try that waits. The close uses it. */\n cancel(): void;\n /** This method sets the count of tries to zero. The client uses it at each join. */\n reset(): void;\n}\n\n/** This function makes the retry schedule of a client, from its backoff and its random numbers. RabbitMQ and WebSocket use it. */\nexport const retrySchedule = (clock: Clock, backoff: Backoff, random: () => number): RetrySchedule => {\n let tries = 0;\n let timer: Cancel | null = null;\n return {\n get tries() {\n return tries;\n },\n schedule(due) {\n tries += 1;\n const delay = Math.round(backoffDelay(backoff, tries, random));\n timer?.();\n timer = clock.after(delay, () => {\n timer = null;\n due();\n });\n },\n cancel() {\n timer?.();\n timer = null;\n },\n reset() {\n tries = 0;\n },\n };\n};\n\n/** This function gives the problems of the retry knobs: the cap must not be below the base. */\nexport const retryProblems = (name: string, knobs: RetryKnobs): string[] =>\n ms(knobs.retry_cap) < ms(knobs.retry_base) ? [`${name}'s retry_cap is below its retry_base`] : [];\n\n/**\n * This function gives the problem of a count of misses that is not a whole number above zero. A\n * client that closes its connection after a count of heartbeat misses uses it. `mechanism` names the heartbeat, for example \"watchdog\".\n */\nexport const missesProblems = (name: string, misses: number, mechanism: string): string[] =>\n Number.isInteger(misses) && misses >= 1 ? [] : [`${name}'s ${mechanism} closes the connection after ${misses} misses, not a whole number above zero`];\n\n/** This function gives the problems of the knobs of a spec that each client has: each knob must be a time of zero or more. */\nexport const timeProblems = (name: string, knobs: object): string[] => {\n const problems: string[] = [];\n for (const [knob, amount] of Object.entries(knobs) as Array<[string, Extent]>) {\n if (amount.value !== \"infinite\" && !(ms(amount) >= 0)) problems.push(`${name}'s ${knob} is not a time of zero or more`);\n }\n return problems;\n};\n\n/** This function writes a bound for a driver that reads 0 as no timer: \"infinite\" is 0. */\nexport const orZero = (amount: Extent): number => finiteMs(amount) ?? 0;\n\n/** This function tells if an amount is a whole number of milliseconds. \"infinite\" is whole. */\nexport const wholeMs = (amount: Extent): boolean => amount.value === \"infinite\" || Number.isInteger(ms(amount));\n\n/** This function tells if an amount is a whole number of seconds. \"infinite\" is whole. */\nexport const wholeSeconds = (amount: Extent): boolean =>\n amount.value === \"infinite\" || Number.isInteger(valueIn(amount as Quantity, \"s\"));\n\n/** These are the knobs of a spec that `zeroProblems` and `wholeProblems` examine, by their kind. */\nexport interface KnobKinds {\n /** These knobs must be a finite time above zero, because the driver reads 0 as no bound. */\n readonly bounded?: readonly string[] | undefined;\n /** These knobs can be \"infinite\", which the driver writes as 0. Thus 0 itself is not a bound. */\n readonly open?: readonly string[] | undefined;\n /** These knobs go to the driver in whole milliseconds. */\n readonly millis?: readonly string[] | undefined;\n /** These knobs go to the driver or to Node in whole seconds. */\n readonly seconds?: readonly string[] | undefined;\n}\n\nconst knobOf = (knobs: object, knob: string): Extent => (knobs as Record<string, Extent>)[knob]!;\n\n/**\n * This function gives the problems of the knobs of a driver that reads 0 as no timer. pg and the\n * MongoDB driver read 0 so. A bounded knob must be a finite time above zero. An open knob can be\n * \"infinite\", and 0 is then a mistake.\n */\nexport const zeroProblems = (name: string, driver: string, knobs: object, kinds: KnobKinds): string[] => {\n const problems: string[] = [];\n for (const knob of kinds.bounded ?? []) {\n const value = ms(knobOf(knobs, knob));\n if (!(value > 0) || !Number.isFinite(value)) problems.push(`${name}'s ${knob} is not a finite time above zero: ${driver} reads 0 as no bound`);\n }\n for (const knob of kinds.open ?? []) {\n const amount = knobOf(knobs, knob);\n if (amount.value !== \"infinite\" && !(ms(amount) > 0)) problems.push(`${name}'s ${knob} is zero: give \"infinite\" for no bound`);\n }\n return problems;\n};\n\n/** This function gives the problems of the knobs that a driver takes in whole milliseconds or in whole seconds. */\nexport const wholeProblems = (name: string, knobs: object, kinds: KnobKinds): string[] => [\n ...(kinds.millis ?? []).filter((knob) => !wholeMs(knobOf(knobs, knob))).map((knob) => `${name}'s ${knob} is not a whole number of milliseconds`),\n ...(kinds.seconds ?? []).filter((knob) => !wholeSeconds(knobOf(knobs, knob))).map((knob) => `${name}'s ${knob} is not a whole number of seconds`),\n];\n\n/** This function gives the problems of the sizes of a pool: at most `max` connections, and `min` from 0 to `max`. */\nexport const poolProblems = (name: string, pool: { readonly max: number; readonly min: number }): string[] => {\n const problems: string[] = [];\n if (!Number.isInteger(pool.max) || pool.max < 1) problems.push(`${name}'s pool has a max of ${pool.max}, not a whole number above zero`);\n if (!Number.isInteger(pool.min) || pool.min < 0 || pool.min > pool.max)\n problems.push(`${name}'s pool has a min of ${pool.min}, not a whole number from 0 to its max`);\n return problems;\n};\n\n/**\n * This function gives the problem O07 of a client whose server has a deadline, or null. The client\n * must wait longer than the deadline of the server. Then the server ends a slow call with an error\n * first. Otherwise the client gives up first, and the server goes on with the call. `call` names\n * the call of the server, for example a statement.\n */\nexport const deadlineProblem = (name: string, request: Quantity, deadline: Extent, call: string): string | null =>\n deadline.value !== \"infinite\" && ms(request) <= ms(deadline)\n ? `${name}'s request_timeout is not above its statement_timeout: the server goes on with a ${call} that the client gave up (O07)`\n : null;\n\n/**\n * This function gives the problem O01 of a client pool, or null. The pool must close an idle\n * connection before its server does. Otherwise a request can go on a connection that the server\n * closed. A server that keeps an idle connection with no bound gives no problem. `knob` names the\n * idle time of the client: `pool_idle_expiry` of a pool, or `client_idle_timeout` of a gRPC channel.\n */\nexport const idleCloseProblem = (name: string, expiry: Extent, close: Extent, knob = \"pool_idle_expiry\"): string | null =>\n close.value !== \"infinite\" && !(ms(expiry) < ms(close))\n ? `${name}'s ${knob} is not below the idle close of its server: a request can go on a connection that the server closed (O01)`\n : null;\n\n/**\n * This function gives the problems of the timers that the wrapper has, because its driver has none.\n * Each one must be a finite time above zero. `bounds` gives what each knob bounds, for example \"a call\".\n */\nexport const wrapperTimerProblems = (name: string, knobs: object, bounds: Readonly<Record<string, string>>): string[] =>\n Object.entries(bounds)\n .filter(([knob]) => {\n const value = ms(knobOf(knobs, knob));\n return !(value > 0) || !Number.isFinite(value);\n })\n .map(([knob, what]) => `${name}'s ${knob} is not a finite time above zero: the timer of the wrapper is the only bound of ${what}`);\n\n/** This function gives the problem of a finite connection age, for a client that does not replace a connection at an age. `reason` tells why. */\nexport const ageProblems = (name: string, knobs: Pick<ClientKnobs, \"max_connection_age\">, reason: string): string[] =>\n knobs.max_connection_age.value === \"infinite\" ? [] : [`${name}'s max_connection_age is not \"infinite\": ${reason}`];\n\n/** This function gives the problem of a keepalive that is not a finite time above zero, for a driver that always turns keepalive on. */\nexport const keepaliveOnProblems = (name: string, knobs: Pick<ClientKnobs, \"keepalive_idle\">, driver: string): string[] => {\n const value = ms(knobs.keepalive_idle);\n return value > 0 && Number.isFinite(value) ? [] : [`${name}'s keepalive_idle is not a finite time above zero: ${driver} always turns keepalive on`];\n};\n\n/** These are the parts of one call that the core uses to send it, and to send it again. */\nexport interface Reissue<T> {\n readonly clock: Clock;\n /** This time bounds the call and its second try, in milliseconds from its start. */\n readonly bound: number;\n /** This operation is the declaration of the call, or undefined when the table does not name it. */\n readonly operation: Operation | undefined;\n readonly options: SendOptions;\n /** This function makes one try. Its signal ends the try. */\n readonly attempt: (signal: AbortSignal | undefined) => Promise<Attempt<T>>;\n /** This function operates before the second try, with the time that is left. False stops the second try. */\n readonly beforeAgain?: ((left: number, signal: AbortSignal | undefined) => Promise<boolean>) | undefined;\n /** This function receives the start of a second try, for the record of the client. */\n readonly onReissue: () => void;\n}\n\n/**\n * This function sends a call, and gives its outcome in three values. It sends again only an\n * idempotent operation, one time at most, after an outcome that is not certain. A reply of the\n * server is certain. The second try ends at the bound of the call, not one bound after its own start.\n */\nexport const sendWithReissue = async <T>(call: Reissue<T>): Promise<Sent<T>> => {\n const { clock, bound } = call;\n const { signal } = call.options;\n const operation = call.options.reissue === false ? undefined : call.operation;\n const started = clock.now();\n const first = await call.attempt(signal);\n if (first.outcome === \"ok\") return { outcome: \"ok\", value: first.value, attempts: 1 };\n const again =\n !(first.outcome === \"error\" && first.reply) &&\n operation !== undefined &&\n signal?.aborted !== true &&\n mayReissue(operation, quantity((clock.now() - started) / 1000, \"s\"));\n const left = bound - (clock.now() - started);\n if (!again || left <= 0) return { ...first, attempts: 1 };\n if (call.beforeAgain !== undefined && !(await call.beforeAgain(left, signal))) return { ...first, attempts: 1 };\n call.onReissue();\n const end = new AbortController();\n const stop = clock.after(Math.max(0, bound - (clock.now() - started)), () => end.abort());\n const forward = (): void => end.abort();\n signal?.addEventListener(\"abort\", forward, { once: true });\n const second = await call.attempt(end.signal);\n stop();\n signal?.removeEventListener(\"abort\", forward);\n return second.outcome === \"ok\" ? { outcome: \"ok\", value: second.value, attempts: 2 } : { ...second, attempts: 2 };\n};\n\n/**\n * This function gives the checks of a client, for `svc.check(check, run)`. It gives a check only for\n * each step that the options name, and only when the client has a function for that step.\n */\nexport const stepChecks = <S extends Step>(\n name: string,\n options: CheckOptions<S>,\n runs: Readonly<Partial<Record<S, StepCheck>>>,\n): ReadonlyArray<readonly [Check, CheckFn]> => {\n const made: Array<readonly [Check, CheckFn]> = [];\n for (const [step, run] of Object.entries(runs) as Array<[S, StepCheck | undefined]>) {\n if (run === undefined || !(step in options.steps)) continue;\n made.push([\n { name: `${name}.${step}`, dependency: name, step, ownConnection: run.ownConnection, gates: options.steps[step] ?? null, poll: options.poll },\n run.run,\n ]);\n }\n return made;\n};\n\n/** This function makes a check outcome from a sent result: only `ok` passes. */\nexport const passes = (sent: Sent<unknown>): Outcome => (sent.outcome === \"ok\" ? \"ok\" : \"error\");\n\n/** This check gives the state of the client, without a round trip. */\nexport const localStateCheck = (ready: () => boolean): StepCheck => ({ ownConnection: true, run: () => (ready() ? \"ok\" : \"error\") });\n\n/**\n * This check sends one call on the connection of the application. A check measures the step at this time.\n * Thus it does not send the call again, and only `ok` passes.\n */\nexport const roundTripCheck = (send: (options: SendOptions) => Promise<Sent<unknown>>): StepCheck => ({\n ownConnection: true,\n run: async (signal) => passes(await send({ signal, reissue: false })),\n});\n\n/**\n * This check opens a new connection each time. It finds a server that refuses connections, but not\n * a stalled connection of the application. A failed dial throws, and the poller counts the throw as an error.\n */\nexport const dialCheck = (dial: DialFn, host: string, port: number): StepCheck => ({\n ownConnection: false,\n run: async (signal) => {\n await dial(host, port, signal);\n return \"ok\" as const;\n },\n});\n\n/** These are the bounds of a client that the core examines in a description. */\nexport interface ClientBounds {\n /** This time bounds the close of the client, in milliseconds. */\n readonly close: number;\n /** This time bounds a call on the connection of the application, in milliseconds. */\n readonly request: number;\n}\n\n/** This function gives the bounds of a client that `clientProblems` examines, from its knobs. */\nexport const clientBounds = (knobs: ClientKnobs): ClientBounds => ({ close: ms(knobs.close_bound), request: ms(knobs.request_timeout) });\n\n/**\n * This function gives the problems of a client in a description:\n * - The client needs the phase \"tear down dependencies\", and its close must fit in the cap of that phase.\n * - A round-trip check must end in one jittered poll (B7, O13).\n */\nexport const clientProblems = (name: string, description: ServiceDescription, bounds: ClientBounds): string[] => {\n const found: string[] = [];\n const teardown = description.shutdown.phases.find((phase) => phase.name === \"tear down dependencies\");\n if (teardown === undefined) found.push(`${name} needs the phase \"tear down dependencies\" to close`);\n else if (bounds.close >= valueIn(teardown.cap, \"ms\"))\n found.push(`${name}'s close_bound of ${bounds.close} ms does not fit in the cap of \"tear down dependencies\"`);\n for (const check of description.checks) {\n if (check.dependency !== name || check.step !== \"round-trip\") continue;\n const slowest = valueIn(check.poll.timeout, \"ms\") + valueIn(check.poll.interval, \"ms\") * (1 + check.poll.jitter);\n if (bounds.request > slowest) found.push(`${name}'s request_timeout outlives one jittered poll of ${check.name} (B7, O13)`);\n }\n return found;\n};\n\n/**\n * This poll is a poll of the checks to start from: each 5 s with a jitter of 20 %, and a timeout of\n * 4 s (B6, O12). Its slowest poll is 4 s + 5 s × 1.2 = 10 s, the slowest poll of the cells of O13.\n * It does not depend on a driver. The starting knobs of each driver fit it.\n */\nexport const STARTING_POLL: Poll = { interval: { value: 5, unit: \"s\" }, jitter: 0.2, timeout: { value: 4, unit: \"s\" } };\n","// The dial of a client: a new TCP connection that sends the first bytes of its protocol and reads\n// the answer. Each driver gives its bytes and its rule for the answer. The core keeps node:net out\n// of `core.ts`, thus only an entry that gives a dial loads it.\n\nimport { connect as dialTcp } from \"node:net\";\nimport type { DialFn } from \"./core.ts\";\n\n/**\n * A judge reads the bytes that the server sent so far. It gives null for a good answer, an error for\n * a bad answer, or \"more\" when it must wait for more bytes.\n */\nexport type Judge = (received: Buffer) => Error | null | \"more\";\n\n/**\n * This function makes a dial function. The dial opens a TCP connection, sends `hello`, and reads the\n * answer until `judge` decides. An abort, an error of the socket, and a close before the decision fail the dial.\n * The dial destroys its socket at its end.\n */\nexport const helloDial =\n (hello: string | Uint8Array, judge: Judge): DialFn =>\n (host, port, signal) =>\n new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(new Error(\"the dial was aborted\"));\n return;\n }\n const socket = dialTcp({ host, port });\n let received = Buffer.alloc(0);\n const end = (error?: Error): void => {\n signal.removeEventListener(\"abort\", onAbort);\n socket.destroy();\n if (error === undefined) resolve();\n else reject(error);\n };\n const onAbort = (): void => end(new Error(\"the dial was aborted\"));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n socket.once(\"connect\", () => socket.write(hello));\n socket.on(\"data\", (chunk: Buffer) => {\n received = Buffer.concat([received, chunk]);\n const verdict = judge(received);\n if (verdict === \"more\") return;\n end(verdict ?? undefined);\n });\n socket.once(\"error\", (error) => end(error));\n socket.once(\"close\", () => end(new Error(\"the server closed the dial\")));\n });\n\n/** This function gives the authority of a host and a port for a Host header. An IPv6 address goes in brackets. */\nexport const authority = (host: string, port: number): string => `${host.includes(\":\") ? `[${host}]` : host}:${port}`;\n\n/**\n * This function makes a judge of the status line of an HTTP/1.1 answer. The judge waits for the end\n * of the line, in the first 8 KiB. Then `accept` reads the status code, and gives null for a good status or an error.\n * The HTTP client accepts each status. The WebSocket client accepts only 101.\n */\nexport const statusLineJudge =\n (accept: (status: number, line: string) => Error | null): Judge =>\n (received) => {\n const end = received.indexOf(\"\\r\\n\");\n if (end < 0) return received.length > 8192 ? new Error(\"the answer has no status line in its first 8 KiB\") : \"more\";\n const line = received.toString(\"latin1\", 0, end);\n const status = /^HTTP\\/1\\.[01] (\\d{3})/.exec(line);\n return status === null ? new Error(`the answer ${JSON.stringify(line)} is not an HTTP/1.1 status line`) : accept(Number(status[1]), line);\n };\n","// The parts of the machine document of a client that each client has. Each client entry writes its\n// own states and transitions, because the states are the states of its driver. The helpers, the\n// knob shape, the timer of the bounded close and the events that the core records are the same.\n\nimport type { TransitionDef } from \"@mark1russell7/algebra\";\n\n/** This function gives one transition of a machine document. */\nexport const transition = (\n id: string,\n from: string | readonly string[],\n on: string | readonly string[],\n to: string,\n extra: Partial<TransitionDef> = {},\n): TransitionDef => ({\n id,\n from,\n on,\n to,\n ...extra,\n});\n\n/** A client knob is a knob of a client in a machine document: a time in seconds, with no default. */\nexport interface ClientKnob {\n readonly key: string;\n readonly scope: \"client\";\n readonly unit: \"s\";\n readonly default: null;\n}\n\n/** This function gives a knob of a client in a machine document, by the option of the driver that it sets. */\nexport const clientKnob = (key: string): ClientKnob => ({ key, scope: \"client\", unit: \"s\", default: null });\n\n/** A close timer is the timer of the bounded close in a machine document. */\nexport interface CloseTimer {\n readonly duration: { readonly knob: \"close_bound\"; readonly unit: \"s\" };\n readonly arms: readonly [\"close.request\"];\n readonly cancels: readonly [\"status.end\"];\n readonly fires: \"close.bound\";\n}\n\n/** This timer is the bound of the close. The close request arms it, and the end cancels it. At its end, the driver destroys each socket. */\nexport const closeTimer = (): CloseTimer => ({\n duration: { knob: \"close_bound\", unit: \"s\" },\n arms: [\"close.request\"],\n cancels: [\"status.end\"],\n fires: \"close.bound\",\n});\n\n/**\n * These are the events that each machine document of a client declares. The core and the shared\n * parts of each client record them: a second try, a failed connect, and the steps of the bounded close.\n */\nexport const CLIENT_EVENTS = [\"status.connecting\", \"connect.failed\", \"command.reissue\", \"close.request\", \"close.bound\", \"close.destroy\", \"status.end\"] as const;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { Y as Poll, wt as Operation } from "./lifecycle-fgYfxrHK.js";
|
|
2
|
+
import { C as MANIFESTATIONS, D as firstDetector, E as Situation, O as unwatched, S as Detector, T as Meeting, _ as Sent, a as ClientKnobs, c as ClientSpec, d as Factory, g as SendOptions, h as STARTING_POLL, i as ClientJoin, l as ClientStep, n as CheckOptions, o as ClientOptions, r as Client, s as ClientSocket, t as Attempt, u as DialFn, w as Manifestation } from "./core-BPLRDcUJ.js";
|
|
3
|
+
import { t as Judge } from "./dial-DA8vJfh2.js";
|
|
4
|
+
import { Extent, Quantity } from "@mark1russell7/quantity";
|
|
5
|
+
import { Machine } from "@mark1russell7/algebra";
|
|
6
|
+
//#region src/mongodb/connection.d.ts
|
|
7
|
+
/** A document is a BSON document as the driver gives it: a command, or the reply of the server. */
|
|
8
|
+
type MongoDocument = Record<string, unknown>;
|
|
9
|
+
/** A database is the part of a `Db` of the driver that the wrapper uses: `command` sends one command. */
|
|
10
|
+
interface MongoDb {
|
|
11
|
+
/** This method sends a command. Its signal ends the command, and an abort during the read closes the connection. */
|
|
12
|
+
command(document: MongoDocument, options?: {
|
|
13
|
+
readonly signal?: AbortSignal | undefined;
|
|
14
|
+
}): Promise<MongoDocument>;
|
|
15
|
+
}
|
|
16
|
+
/** A server is the part of a `ServerDescription` that the wrapper reads: its type, for example `RSPrimary`. */
|
|
17
|
+
interface MongoServer {
|
|
18
|
+
readonly type: string;
|
|
19
|
+
}
|
|
20
|
+
/** These are the events of the driver that the wrapper listens to, by name, with the fields that it reads. */
|
|
21
|
+
interface MongoEvents {
|
|
22
|
+
/** The view of the topology changed. A writable server is a primary, a standalone, a mongos or a load balancer. */
|
|
23
|
+
topologyDescriptionChanged: {
|
|
24
|
+
readonly newDescription: {
|
|
25
|
+
readonly servers: ReadonlyMap<string, MongoServer>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/** A connection of the pool finished its handshake. `connectionId` is the id that the pool gives. */
|
|
29
|
+
connectionReady: {
|
|
30
|
+
readonly connectionId: number | string;
|
|
31
|
+
readonly address: string;
|
|
32
|
+
};
|
|
33
|
+
/** A command went to a connection. `serverConnectionId` is the id that the server gave in its hello. */
|
|
34
|
+
commandStarted: {
|
|
35
|
+
readonly connectionId?: number | string | undefined;
|
|
36
|
+
readonly address: string;
|
|
37
|
+
readonly serverConnectionId?: bigint | number | null | undefined;
|
|
38
|
+
};
|
|
39
|
+
/** The pool of a server closed its connections, for example at the write-off of the server. */
|
|
40
|
+
connectionPoolCleared: {
|
|
41
|
+
readonly address: string;
|
|
42
|
+
readonly interruptInUseConnections?: boolean | undefined;
|
|
43
|
+
};
|
|
44
|
+
/** A hello of the monitor failed. `connectionId` is the address of the server. */
|
|
45
|
+
serverHeartbeatFailed: {
|
|
46
|
+
readonly connectionId: string;
|
|
47
|
+
readonly failure: Error;
|
|
48
|
+
};
|
|
49
|
+
/** An error of the topology. In Node, an error with no listener throws. */
|
|
50
|
+
error: Error;
|
|
51
|
+
}
|
|
52
|
+
/** An event name is a name of `MongoEvents`. */
|
|
53
|
+
type MongoEventName = keyof MongoEvents;
|
|
54
|
+
/**
|
|
55
|
+
* A driver client is the part of a `MongoClient` that the wrapper uses. A `MongoClient` of the
|
|
56
|
+
* driver 7 has each part. A fake for tests has them too. Thus the package depends on no type of the driver.
|
|
57
|
+
*/
|
|
58
|
+
interface MongoDriverClient {
|
|
59
|
+
/** This method opens the topology and waits for a server to select. A failure closes the topology, and a later command opens it again. */
|
|
60
|
+
connect(): Promise<unknown>;
|
|
61
|
+
/** This method fails the commands in flight, sends `endSessions` to a selectable server, and closes the topology. */
|
|
62
|
+
close(): Promise<void>;
|
|
63
|
+
db(name: string): MongoDb;
|
|
64
|
+
on<E extends MongoEventName>(event: E, listener: (payload: MongoEvents[E]) => void): unknown;
|
|
65
|
+
off<E extends MongoEventName>(event: E, listener: (payload: MongoEvents[E]) => void): unknown;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* These are the options that the wrapper gives `MongoClient`. Each one comes from the declaration.
|
|
69
|
+
* The wrapper sets each option that has an effect on a timer, thus no default of the driver decides
|
|
70
|
+
* one. A knob of the driver at 0 has no timer. The wrapper writes "infinite" as 0, and it refuses a bound of 0.
|
|
71
|
+
*/
|
|
72
|
+
interface MongoDriverOptions {
|
|
73
|
+
/** The server shows this name in `$currentOp` and in its log, thus the prober can join on it too. */
|
|
74
|
+
readonly appName: string;
|
|
75
|
+
readonly replicaSet?: string | undefined;
|
|
76
|
+
readonly auth?: {
|
|
77
|
+
readonly username: string;
|
|
78
|
+
readonly password: string;
|
|
79
|
+
} | undefined;
|
|
80
|
+
readonly authSource?: string | undefined;
|
|
81
|
+
readonly maxPoolSize: number;
|
|
82
|
+
readonly minPoolSize: number;
|
|
83
|
+
/** This time bounds the TCP connect and the handshake of each connection, and of the monitor. */
|
|
84
|
+
readonly connectTimeoutMS: number;
|
|
85
|
+
/** This time bounds a read on a connection that has a command in flight. At its end, the driver closes the connection. */
|
|
86
|
+
readonly socketTimeoutMS: number;
|
|
87
|
+
/** This time bounds the wait for a server to select, for each command and for `connect`. */
|
|
88
|
+
readonly serverSelectionTimeoutMS: number;
|
|
89
|
+
/** This time is the period of the monitor, and the wait of its streaming hello. */
|
|
90
|
+
readonly heartbeatFrequencyMS: number;
|
|
91
|
+
/** This time bounds a checkout from a full pool. 0 is no bound. */
|
|
92
|
+
readonly waitQueueTimeoutMS: number;
|
|
93
|
+
/** A checkout ends an idle connection older than this time. 0 is no limit. The driver has no timer for it. */
|
|
94
|
+
readonly maxIdleTimeMS: number;
|
|
95
|
+
/** This time is the idle time of TCP keepalive. The driver always turns keepalive on. Node truncates the time to whole seconds. */
|
|
96
|
+
readonly keepAliveInitialDelay: number;
|
|
97
|
+
/** The monitor streams its hello, as in the cells. Thus a silent server is written off at `connectTimeoutMS + heartbeatFrequencyMS`. */
|
|
98
|
+
readonly serverMonitoringMode: "stream";
|
|
99
|
+
/** The driver does not send a command again by itself. The core sends again only an idempotent operation. */
|
|
100
|
+
readonly retryReads: false;
|
|
101
|
+
readonly retryWrites: false;
|
|
102
|
+
/** The driver emits `commandStarted`, which gives the id of the server for each connection (D1). */
|
|
103
|
+
readonly monitorCommands: true;
|
|
104
|
+
}
|
|
105
|
+
/** These are the arguments of `new MongoClient(url, options)`. The URL gives only the hosts. */
|
|
106
|
+
interface MongoConnectOptions {
|
|
107
|
+
readonly url: string;
|
|
108
|
+
readonly options: MongoDriverOptions;
|
|
109
|
+
}
|
|
110
|
+
/** A driver factory makes the client of the driver from its options. */
|
|
111
|
+
type MongoFactory = Factory<MongoConnectOptions, MongoDriverClient>;
|
|
112
|
+
/**
|
|
113
|
+
* This factory makes a `MongoClient`. It loads the driver only when a client connects, thus a
|
|
114
|
+
* service that does not use MongoDB does not need it. The specifier is a value, thus the package
|
|
115
|
+
* needs no types of the driver. The driver gives `MongoClient` as a named export.
|
|
116
|
+
*/
|
|
117
|
+
export declare const nodeMongo: MongoFactory;
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/mongodb/client.d.ts
|
|
120
|
+
/**
|
|
121
|
+
* These are the knobs of the client, each a time, by the names of the ordering law where the law
|
|
122
|
+
* has them. The runtime has no defaults, thus each knob is necessary. A bound that nobody gives is
|
|
123
|
+
* "infinite", not zero. `MONGO_STARTING_KNOBS` gives a declaration from the cells of S1. The knobs
|
|
124
|
+
* that each client has come from the core.
|
|
125
|
+
*/
|
|
126
|
+
interface MongoKnobs extends ClientKnobs {
|
|
127
|
+
/** This time bounds the TCP connect and the handshake of each connection (`connectTimeoutMS`). It also bounds the wait of the startup. */
|
|
128
|
+
readonly connect_bound: Quantity;
|
|
129
|
+
/** This time bounds a read of a command in flight (`socketTimeoutMS`). At its end, the driver closes the connection. */
|
|
130
|
+
readonly request_timeout: Quantity;
|
|
131
|
+
/**
|
|
132
|
+
* This time is the deadline of each command on the server (`maxTimeMS`). The wrapper writes it in
|
|
133
|
+
* each command. The server then ends the command with error 50, a certain outcome. "infinite" sends no deadline.
|
|
134
|
+
*/
|
|
135
|
+
readonly statement_timeout: Extent;
|
|
136
|
+
/** This time is the period of the monitor (`heartbeatFrequencyMS`). The driver takes 500 ms at least. */
|
|
137
|
+
readonly heartbeat_interval: Quantity;
|
|
138
|
+
/**
|
|
139
|
+
* This time bounds the wait for a writable server (`serverSelectionTimeoutMS`). A command waits
|
|
140
|
+
* through a failover in this time, thus it is the retry window of the client (O29).
|
|
141
|
+
*/
|
|
142
|
+
readonly client_retry_window: Quantity;
|
|
143
|
+
/** This time bounds a checkout from a full pool (`waitQueueTimeoutMS`). */
|
|
144
|
+
readonly checkout_timeout: Extent;
|
|
145
|
+
/** A checkout ends an idle connection older than this time (`maxIdleTimeMS`). The driver checks it only at a checkout. */
|
|
146
|
+
readonly pool_idle_expiry: Extent;
|
|
147
|
+
/** This time is the idle time of TCP keepalive (`keepAliveInitialDelay`), in whole seconds. The driver always turns keepalive on. */
|
|
148
|
+
readonly keepalive_idle: Extent;
|
|
149
|
+
/** The driver has no age for a connection, thus this knob must be "infinite". */
|
|
150
|
+
readonly max_connection_age: Extent;
|
|
151
|
+
/** This time bounds the close of the client at the shutdown. */
|
|
152
|
+
readonly close_bound: Quantity;
|
|
153
|
+
}
|
|
154
|
+
/** A seed is one more host that the driver connects to first, for example a member of a replica set. */
|
|
155
|
+
interface MongoSeed {
|
|
156
|
+
readonly host: string;
|
|
157
|
+
readonly port: number;
|
|
158
|
+
}
|
|
159
|
+
/** A MongoDB client spec is the declaration of one MongoDB client: where it connects, its pool, and its knobs. */
|
|
160
|
+
interface MongoClientSpec extends ClientSpec<MongoKnobs> {
|
|
161
|
+
/** This name is the database of the commands of the client. */
|
|
162
|
+
readonly database: string;
|
|
163
|
+
readonly username?: string | undefined;
|
|
164
|
+
/** This name is the database of the user. The default of the driver is `database`, or `admin`. */
|
|
165
|
+
readonly authSource?: string | undefined;
|
|
166
|
+
readonly replicaSet?: string | undefined;
|
|
167
|
+
/** These hosts are more seeds of the topology, after `host` and `port`. */
|
|
168
|
+
readonly seeds?: readonly MongoSeed[] | undefined;
|
|
169
|
+
/** These counts are the sizes of the pool of each server: at most `max` connections, and `min` that the driver keeps open. */
|
|
170
|
+
readonly pool: {
|
|
171
|
+
readonly max: number;
|
|
172
|
+
readonly min: number;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* These operations are the commands of the service, by name, with their idempotency. A command
|
|
176
|
+
* name does not tell if a command changes a state, for example an `aggregate` with `$merge`. Thus
|
|
177
|
+
* the client sends again only a command that this table names.
|
|
178
|
+
*/
|
|
179
|
+
readonly operations?: Readonly<Record<string, Operation>> | undefined;
|
|
180
|
+
}
|
|
181
|
+
/** These options make a client. Without them, it uses the driver, the real clock, and a dial with a hello. */
|
|
182
|
+
type MongoClientOptions = ClientOptions<MongoFactory>;
|
|
183
|
+
/**
|
|
184
|
+
* A join is one connection of the pool, when its handshake is done (D1, D2). The prober joins its
|
|
185
|
+
* kernel records to the server's view of the connection through it. The driver gives no socket,
|
|
186
|
+
* thus the local address and port are null. The server's view is the connection id of its hello.
|
|
187
|
+
*/
|
|
188
|
+
interface MongoJoin extends ClientJoin {
|
|
189
|
+
/** This text is the address of the server, as `host:port`. */
|
|
190
|
+
readonly address: string;
|
|
191
|
+
/** This number is the connection id that the server gave, from the first command on the connection. Null until that command. */
|
|
192
|
+
readonly serverConnectionId: number | null;
|
|
193
|
+
}
|
|
194
|
+
/** The steps that the MongoDB client gives a check for. A pool of the driver has no checkout that the wrapper can use. */
|
|
195
|
+
type MongoStep = ClientStep;
|
|
196
|
+
/** A MongoDB client is a dependency of a service: its declaration, its handle, its checks and its commands. */
|
|
197
|
+
interface MongoClient extends Client<MongoStep, MongoJoin> {
|
|
198
|
+
/** This text is the state of the client, as `MONGO_CLIENT_MACHINE` names it. */
|
|
199
|
+
readonly status: string;
|
|
200
|
+
/**
|
|
201
|
+
* This method sends a command to the database of the spec, and gives its outcome in three values.
|
|
202
|
+
* `operation` names the command in the table of operations. The client sends again only an
|
|
203
|
+
* idempotent operation, one time at most, after an outcome that is not certain.
|
|
204
|
+
*/
|
|
205
|
+
command(operation: string, document: MongoDocument, options?: SendOptions): Promise<Sent<MongoDocument>>;
|
|
206
|
+
}
|
|
207
|
+
/** This function gives the problems of a spec: the knobs that the driver cannot use. */
|
|
208
|
+
export declare const specProblems: (name: string, spec: MongoClientSpec) => string[];
|
|
209
|
+
/** This function gives the arguments of `new MongoClient` from a spec. Each timer of the driver comes from the declaration. */
|
|
210
|
+
export declare const mongoOptions: (name: string, spec: MongoClientSpec) => MongoConnectOptions;
|
|
211
|
+
/**
|
|
212
|
+
* The bytes of an OP_MSG with the command `{ hello: 1, $db: "admin" }`. They are a header of 16
|
|
213
|
+
* bytes with the op code 2013, the flags, a section of kind 0, and the BSON document. The server answers a
|
|
214
|
+
* hello before the authentication, thus the dial needs no password.
|
|
215
|
+
*/
|
|
216
|
+
export declare const HELLO: Uint8Array;
|
|
217
|
+
/**
|
|
218
|
+
* This judge reads the answer to the hello. It waits for the whole message. The message must be an
|
|
219
|
+
* OP_MSG with a section of kind 0, and its document must have `ok` 1.
|
|
220
|
+
*/
|
|
221
|
+
export declare const helloJudge: Judge;
|
|
222
|
+
/** This dial function opens a TCP connection, sends a hello, and waits for its reply. The server opens no session for it. */
|
|
223
|
+
export declare const helloCommandDial: DialFn;
|
|
224
|
+
/**
|
|
225
|
+
* This function gives the outcome of a failure of the driver. The driver names each error class,
|
|
226
|
+
* thus the wrapper reads the name, and depends on no class of the driver:
|
|
227
|
+
* - A failure before the command left the client is certain: it did not apply.
|
|
228
|
+
* - An error of the server is certain, except an interruption, which can leave a part applied.
|
|
229
|
+
* - A write concern error, a network error after the handshake, and a timeout of the socket are not certain.
|
|
230
|
+
*/
|
|
231
|
+
export declare const failureOutcome: (error: unknown) => Attempt<never>;
|
|
232
|
+
/**
|
|
233
|
+
* This function makes a MongoDB client, a dependency of a service over a `MongoClient`.
|
|
234
|
+
* - It sets each timer of the driver from the declaration, and it turns off the retries of the driver.
|
|
235
|
+
* The client sends again only an idempotent operation of its table.
|
|
236
|
+
* - It tells the outcome of a command in three values, from the name of each error of the driver.
|
|
237
|
+
* - It writes the deadline of the server in each command (`maxTimeMS`), thus the server ends a slow command first (O07).
|
|
238
|
+
* - It gives the state from the view of the topology: ready while a server is writable.
|
|
239
|
+
* - It listens to each error of the driver. In Node, an error with no listener throws, and library code does not end the process (A5).
|
|
240
|
+
* - It records the connection id of the server for each connection of the pool (D1).
|
|
241
|
+
*
|
|
242
|
+
* The outcomes, the second try, the commands in flight, the bounded close and the checks come from
|
|
243
|
+
* the core of the clients. This entry gives what the driver does in its own way.
|
|
244
|
+
*/
|
|
245
|
+
export declare const mongoClient: (name: string, spec: MongoClientSpec, options?: MongoClientOptions) => MongoClient;
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/mongodb/machine.d.ts
|
|
248
|
+
/**
|
|
249
|
+
* The MongoDB client of the service as a `mark1russell7.machine/1` document. It is the topology as
|
|
250
|
+
* the wrapper sees it, not one connection: the document `mongodb-node` of `@mark1russell7/machines`
|
|
251
|
+
* gives the monitor and each connection. The state is the view of the topology:
|
|
252
|
+
* - `ready`: a server is writable: a primary, a standalone, a mongos, or a load balancer.
|
|
253
|
+
* - `failing`: no server is writable, or the startup found none in `connect_bound`. The monitor
|
|
254
|
+
* goes on, and a command waits for a writable server in `client_retry_window`.
|
|
255
|
+
*
|
|
256
|
+
* A connection that closes and a pool that the driver clears do not change the state by themselves.
|
|
257
|
+
* The write-off of a server changes the view of the topology, and that changes the state. A test
|
|
258
|
+
* records the transitions of the client and replays them against this document.
|
|
259
|
+
*/
|
|
260
|
+
export declare const MONGO_CLIENT_MACHINE: Machine;
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/mongodb/meets.d.ts
|
|
263
|
+
/**
|
|
264
|
+
* This function gives the manifestations that a MongoDB client can meet, and the detectors of each,
|
|
265
|
+
* from its knobs. The cells of S1 measured them on the driver 7.7.0 against mongod 9.0.4:
|
|
266
|
+
* - The monitor writes off a silent server at `connectTimeoutMS + heartbeatFrequencyMS` after its last
|
|
267
|
+
* answer: 40.002 s at the defaults, and 7 s for 5 s + 2 s. The driver then clears the pool, and
|
|
268
|
+
* fails each command in flight. With `connectTimeoutMS` 0, nothing found a freeze in 60 s.
|
|
269
|
+
* - At `tcp_retries2 = 5`, the kernel gave up at 12.936 s.
|
|
270
|
+
* - A `keepAliveInitialDelay` of 27 s ended an idle stream at 37.255 s.
|
|
271
|
+
*
|
|
272
|
+
* The monitor tests the server, not each connection of the pool. Thus a write-off finds a failure of
|
|
273
|
+
* the server in flight and idle, and a failure of one connection waits for its own detector.
|
|
274
|
+
*/
|
|
275
|
+
export declare const mongoMeetings: (name: string, knobs: MongoKnobs) => Meeting[];
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/mongodb/starting.d.ts
|
|
278
|
+
/**
|
|
279
|
+
* This poll is the poll of the checks that `MONGO_STARTING_KNOBS` fits. It is `STARTING_POLL` of
|
|
280
|
+
* the core: each 5 s with a jitter of 20 %, and a timeout of 4 s (B6, O12). The poll does not depend on the driver.
|
|
281
|
+
*/
|
|
282
|
+
export declare const MONGO_STARTING_POLL: Poll;
|
|
283
|
+
/**
|
|
284
|
+
* These knobs are a declaration to start from, from what the cells of S1 measured on the driver
|
|
285
|
+
* 7.7.0 on Node 24.21.0 against mongod 9.0.4. The runtime has no defaults: a service gives these
|
|
286
|
+
* knobs, or its own. Each value holds the borders that the cells measured:
|
|
287
|
+
* - `connect_bound` 5 s and `heartbeat_interval` 2 s: the write-off of a silent server came 7 s after
|
|
288
|
+
* its last answer, and the cells T01.border-off.75 and .76 held. At the defaults it came at 40.002 s.
|
|
289
|
+
* - `request_timeout` 9.5 s: in the slowest poll of 10 s (O13). At the defaults the driver has no
|
|
290
|
+
* bound on a read, and only the write-off ends the command.
|
|
291
|
+
* - `statement_timeout` 9 s: below `request_timeout`, thus the server ends a slow command first,
|
|
292
|
+
* with error 50 (O07).
|
|
293
|
+
* - `client_retry_window` 30 s: after an election, the first write took 17 s to 18 s, and the band
|
|
294
|
+
* ended at 26.58 s (T11.defaults.10). The default of the driver, 30 s, holds that band.
|
|
295
|
+
* - `checkout_timeout` 5 s: a full pool fails a checkout in a startup budget. At the defaults the wait has no bound.
|
|
296
|
+
* - `pool_idle_expiry` 10 s: no cell measured it. It is the bound of O03 and O01 that the pg client
|
|
297
|
+
* uses: below a router that forgets an idle flow at 12 s. The driver applies it at the next checkout.
|
|
298
|
+
* - `keepalive_idle` 30 s: the driver always turns keepalive on, at 120 s by default. This value
|
|
299
|
+
* refreshes a held idle connection through the NAT forms of the cells (240 s and 350 s).
|
|
300
|
+
* - `max_connection_age` "infinite": the driver has no age for a connection.
|
|
301
|
+
* - `close_bound` 2 s: behind a black hole the close of the driver waits for `endSessions`.
|
|
302
|
+
* - `retrans_giveup_time` 924.6 s: the give-up of Linux at `tcp_retries2 = 15`. The cells measured 12.936 s at `tcp_retries2 = 5`.
|
|
303
|
+
*/
|
|
304
|
+
export declare const MONGO_STARTING_KNOBS: MongoKnobs;
|
|
305
|
+
//#endregion
|
|
306
|
+
export { type Attempt, type CheckOptions, type Client, type ClientJoin, type ClientKnobs, type ClientOptions, type ClientSocket, type ClientSpec, type ClientStep, type Detector, type DialFn, type Factory, MANIFESTATIONS, type Manifestation, type Meeting, type MongoClient, type MongoClientOptions, type MongoClientSpec, type MongoConnectOptions, type MongoDb, type MongoDocument, type MongoDriverClient, type MongoDriverOptions, type MongoEventName, type MongoEvents, type MongoFactory, type MongoJoin, type MongoKnobs, type MongoSeed, type MongoServer, type MongoStep, STARTING_POLL, type SendOptions, type Sent, type Situation, firstDetector, unwatched };
|
|
307
|
+
//# sourceMappingURL=mongodb.d.ts.map
|