@mark1russell7/service 0.1.0 → 0.2.0

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/build/index.d.ts CHANGED
@@ -1,231 +1,6 @@
1
+ import { $ as FailsBy, A as StartOptions, B as Check, C as ProcessPort, D as HealthVerdict, E as Health, F as MachineName, G as Gate, H as Coupling, I as Recorder, J as Step, K as Phase, L as Cancel, M as Outcome, N as Result, O as ProbeAnswer, P as LifecycleEvent, Q as mayReissue, R as Clock, S as ExitSignal, T as CheckVerdict, U as Dependency, V as Controller, W as FORMAT, X as Idempotency, Y as Transport, Z as Operation, _ as cooperativeAbortAt, a as WatchdogSpec, at as failSpan, b as RequestOutcome, c as HealthServer, d as PhaseContext, et as Poll, f as PhaseWork, g as TransportImpl, h as Stage, i as Watchdog, it as asWindow, j as CheckFn, k as ProbeKind, l as Lifecycle, m as STAGES, n as ServiceBuilder, nt as Timing, o as watchdog, ot as recoverSpan, p as RunOptions, q as ServiceDescription, r as service, rt as asDebounce, s as DependencyHandle, t as ControllerBuilder, tt as Span, u as LifecycleState, v as stageRank, w as nodeProcess, x as EXIT_SIGNALS, y as RequestFn, z as realClock } from "./builder-BFxOuULH.js";
1
2
  import { Extent, Quantity } from "@mark1russell7/quantity";
2
- //#region src/timing.d.ts
3
- /**
4
- * All policies have the same timing shape. A timing is a count of polls (a debounce) or a
5
- * duration (a window), and each converts to the other. A long window is easier to read as a
6
- * duration, and that is the only difference between the two.
7
- */
8
- type Timing = {
9
- readonly type: "debounce";
10
- /** This interval is the time between two polls of the signal. */
11
- readonly interval: Quantity;
12
- /** This count is the number of failed polls in sequence before the policy fails. */
13
- readonly failAfter: number;
14
- /** This count is the number of passed polls in sequence before the policy recovers. */
15
- readonly recoverAfter: number;
16
- } | {
17
- readonly type: "window";
18
- /** This duration is how long the signal fails before the policy fails, on the monotonic clock. */
19
- readonly failAfter: Quantity;
20
- /** This duration is how long the signal passes before the policy recovers. Without it, the first pass recovers the policy. */
21
- readonly recoverAfter?: Quantity | undefined;
22
- };
23
- /** A poll has an interval, a jitter as a fraction (±20 % is 0.2) and a timeout. */
24
- interface Poll {
25
- readonly interval: Quantity;
26
- readonly jitter: number;
27
- readonly timeout: Quantity;
28
- }
29
- /** A span is a range of times, from the earliest to the latest. */
30
- interface Span {
31
- readonly min: Quantity;
32
- readonly max: Quantity;
33
- }
34
- /**
35
- * This type tells how a failed step answers a poll. An `"error"` step refuses at once. A
36
- * `"timeout"` step (a black hole) answers only when the timeout of the poll ends. Thus it is
37
- * slower by that timeout, at the two ends of the span.
38
- */
39
- type FailsBy = "error" | "timeout";
40
- /**
41
- * This function gives the time that a policy takes to fail after its signal becomes bad. A
42
- * debounce of N polls takes approximately N intervals, with the jitter. A window takes its
43
- * duration, plus one interval at most for the poll that starts its sequence. A step that fails by
44
- * a timeout adds the timeout of the poll. At 3 polls of 5 s ± 20 %, the latch fails in 8–18 s on
45
- * an error, and in 12–22 s on a 4 s timeout.
46
- */
47
- export declare const failSpan: (timing: Timing, poll: Poll, by?: FailsBy) => Span;
48
- /** This function gives the time that a policy takes to recover after its signal becomes good. */
49
- export declare const recoverSpan: (timing: Timing, poll: Poll) => Span;
50
- /** This function gives the window that a debounce approximates: N polls of an interval fail after N intervals. */
51
- export declare const asWindow: (timing: Timing) => Timing;
52
- /** This function gives the debounce that approximates a window on a poll: the polls that fit, rounded up. */
53
- export declare const asDebounce: (timing: Timing, poll: Poll) => Timing;
54
- //#endregion
55
- //#region src/operation.d.ts
56
- /**
57
- * This type tells if an operation can go again after a failure. It is a property of the
58
- * operation, not of the dependency. One Redis client sends a GET, which is safe to send again. It
59
- * also sends an INCR, which counts two times when it goes again after a timeout. Each operation
60
- * declares its idempotency:
61
- * - `true`: a second issue has the same effect. Examples are an HTTP GET or PUT, a SELECT, a
62
- * Redis GET or SET, and a write that the server removes when it is a copy.
63
- * - `false`: the client does not send it again after it can have reached the server.
64
- * - keyed: the server keeps a key for a window, as with an `Idempotency-Key` header, and it
65
- * ignores a copy in that window. A copy after the window is a second operation. Thus the
66
- * window is a timer, and it must be longer than all retries of the operation.
67
- */
68
- type Idempotency = boolean | {
69
- readonly key: string;
70
- readonly window: Extent;
71
- };
72
- /** An operation is an endpoint that the service calls or serves, a command, or a query. */
73
- interface Operation {
74
- readonly idempotent: Idempotency;
75
- }
76
- /**
77
- * This function tells if an operation can go again `since` its first issue. An idempotent
78
- * operation can always go again, and other operations cannot. A keyed operation can go again
79
- * while the server keeps its key.
80
- */
81
- export declare const mayReissue: (operation: Operation, since: Quantity) => boolean;
82
- //#endregion
83
- //#region src/description.d.ts
84
- /**
85
- * This is the format of a service as data: what `describe()` exports, and nothing starts. The
86
- * timer algebra and the ordering law read it. Each timer has a unit. Each knob comes from the
87
- * declaration, because the runtime has no defaults. A bound that nobody gives is "no bound", not
88
- * zero.
89
- */
90
- export declare const FORMAT = "mark1russell7.service/1";
91
- /** A step is a step of a connection that a check examines. A check that skips a step cannot see a failure in it. */
92
- type Step = "dial" | "local-state" | "round-trip" | "checkout" | "progress" | "topology" | "latency";
93
- /** A gate is the policy that the failures of a check count toward. */
94
- type Gate = "readiness" | "liveness";
95
- /** A check is an active check: one signal, for one step of a connection. */
96
- interface Check {
97
- readonly name: string;
98
- /** This name is the dependency that it examines, or null when it reads the state of the process. */
99
- readonly dependency: string | null;
100
- readonly step: Step;
101
- /** This flag tells if it uses the connection of the application. A check on its own connection cannot see a failure of that connection. */
102
- readonly ownConnection: boolean;
103
- /** This gate is the policy that it controls, or null for a check that only reports. It has no default. */
104
- readonly gates: Gate | null;
105
- readonly poll: Poll;
106
- }
107
- /**
108
- * A dependency is something that the service uses, with its client. Its knobs have the names that
109
- * the ordering law uses: `request_timeout`, `connect_bound`, `driver_writeoff_time`,
110
- * `heartbeat_interval`, `client_retry_window`. Thus the law reads them without a translation.
111
- */
112
- interface Dependency {
113
- readonly name: string;
114
- /** This identifier is the component in the corpus (`systems.component.postgresql`), or null when the corpus does not have it. */
115
- readonly system: string | null;
116
- readonly knobs: Readonly<Record<string, Extent>>;
117
- /**
118
- * These are the operations that the service does with it, by name (`GET /v1/forecast`, `INCR`).
119
- * Each operation declares if it can go again. A dependency usually has the two kinds, thus a
120
- * dependency has no idempotency of its own. An operation without a declaration does not go again.
121
- */
122
- readonly operations: Readonly<Record<string, Operation>>;
123
- }
124
- /**
125
- * A transport is something that takes work in: a server, a consumer or a worker. Each is a
126
- * component with a state machine. The service is ready only while each transport takes work.
127
- */
128
- interface Transport {
129
- readonly name: string;
130
- readonly kind: "server" | "consumer" | "worker";
131
- /** These are its knobs, with the names of the law where the law has them: the `server_idle_close` of a server. */
132
- readonly knobs: Readonly<Record<string, Extent>>;
133
- }
134
- /**
135
- * A controller is a group of routes. It is ready only while the dependencies that it needs are
136
- * ready. Each route declares if its callers can send it again. When the shutdown stops a request,
137
- * an idempotent route answers 503, which a caller can retry. Other routes answer 500.
138
- */
139
- interface Controller {
140
- readonly name: string;
141
- readonly requires: readonly string[];
142
- readonly routes: Readonly<Record<string, Operation>>;
143
- }
144
- /** A coupling is a declared edge between two policies. The algebra checks its margin. */
145
- interface Coupling {
146
- readonly from: Gate;
147
- readonly to: Gate;
148
- readonly effect: "fails";
149
- }
150
- /** A phase is one step of the shutdown sequence, with a cap. Its owner adds it. */
151
- interface Phase {
152
- readonly name: string;
153
- readonly cap: Quantity;
154
- }
155
- /** A service description is the whole graph of a service, as data. */
156
- interface ServiceDescription {
157
- readonly format: typeof FORMAT;
158
- readonly name: string;
159
- /** This flag tells if a part joined after the start. That part starts tripped, and the export is partial. */
160
- readonly partial: boolean;
161
- readonly transports: readonly Transport[];
162
- readonly dependencies: readonly Dependency[];
163
- readonly controllers: readonly Controller[];
164
- readonly checks: readonly Check[];
165
- readonly policies: {
166
- readonly readiness: Timing;
167
- readonly liveness: Timing;
168
- /** This duration is how long the service can take to start. The startup probe must agree with it. */
169
- readonly startup: Quantity;
170
- };
171
- readonly couplings: readonly Coupling[];
172
- readonly shutdown: {
173
- readonly phases: readonly Phase[];
174
- /** This window is how long before the end of the drain the cooperative abort occurs, or no bound. */
175
- readonly cooperativeWindow: Extent;
176
- };
177
- }
178
- //#endregion
179
- //#region src/builder.d.ts
180
- /**
181
- * A service is composed with plain method uses before it starts. The builder is imperative, and
182
- * it holds static knowledge. The service does not use an `init` function that gives registrations.
183
- *
184
- * const svc = service("app");
185
- * svc.dependency("weather", {
186
- * system: null,
187
- * knobs: { request_timeout },
188
- * operations: { "GET /v1/forecast": { idempotent: true } },
189
- * });
190
- * svc.controller("forecast").requires("weather").route("GET /api/forecast", { idempotent: true });
191
- * svc.check({ name: "weather.round-trip", dependency: "weather", … });
192
- * svc.describe(); // the whole graph as data, with nothing started
193
- * await svc.start();
194
- *
195
- * The graph has one source, thus its parts cannot disagree. A dependency or a check can join
196
- * after the start. It starts tripped, and the export is partial. The runtime has no defaults of
197
- * its own. Thus `describe()` refuses a service that does not declare a policy, its startup budget
198
- * or its shutdown, and it tells which.
199
- */
200
- interface ControllerBuilder {
201
- /** This method declares the dependencies that its routes need. The controller is ready only while they are ready. */
202
- requires(...dependencies: string[]): ControllerBuilder;
203
- /** This method declares a route that the controller serves, and if its callers can send it again. */
204
- route(route: string, operation: Operation): ControllerBuilder;
205
- }
206
- /** A service builder composes a service, gives it as data, and starts it. */
207
- interface ServiceBuilder {
208
- readonly name: string;
209
- dependency(name: string, spec: Omit<Dependency, "name">): ServiceBuilder;
210
- server(name: string, knobs: Readonly<Record<string, Extent>>): ServiceBuilder;
211
- consumer(name: string, knobs: Readonly<Record<string, Extent>>): ServiceBuilder;
212
- worker(name: string, knobs: Readonly<Record<string, Extent>>): ServiceBuilder;
213
- controller(name: string): ControllerBuilder;
214
- check(check: Check): ServiceBuilder;
215
- readiness(timing: Timing): ServiceBuilder;
216
- liveness(timing: Timing): ServiceBuilder;
217
- startup(budget: Quantity): ServiceBuilder;
218
- couple(coupling: Coupling): ServiceBuilder;
219
- phase(name: string, cap: Quantity): ServiceBuilder;
220
- cooperativeWindow(window: Extent): ServiceBuilder;
221
- /** This method gives the service as data, and it starts nothing. */
222
- describe(): ServiceDescription;
223
- /** This method closes the composition. A part that joins after it starts tripped, and it makes the export partial. */
224
- start(): Promise<void>;
225
- }
226
- /** This function starts the composition of a service. */
227
- export declare const service: (name: string) => ServiceBuilder;
228
- //#endregion
3
+ import { Machine } from "@mark1russell7/algebra";
229
4
  //#region src/law.d.ts
230
5
  /**
231
6
  * This module is the evaluator of the timer-ordering law. Its decisions are the same as the
@@ -345,5 +120,49 @@ export declare const serviceBindings: (svc: ServiceDescription, scenario: Scenar
345
120
  /** This function applies each service rule to a declaration and a scenario: one time for the service, and one time for each client. */
346
121
  export declare const lint: (svc: ServiceDescription, scenario: Scenario, rules: readonly Rule[]) => Finding[];
347
122
  //#endregion
348
- export type { Binding, Bound, Check, Controller, ControllerBuilder, Coupling, Dependency, Evaluated, FailsBy, Finding, Gate, Idempotency, Inequality, Operation, Phase, Poll, Rule, Scenario, ServiceBuilder, ServiceDescription, Side, Span, Step, Term, Timing, Transport, Verdict };
123
+ //#region src/machines.d.ts
124
+ /**
125
+ * The startup gate opens one time. The lifecycle moves it from `closed` to `starting` while its
126
+ * components start. Then one warm-up sweep of each check opens it. A component that does not start
127
+ * fails it for good.
128
+ */
129
+ export declare const GATE_MACHINE: Machine;
130
+ /** A debounce latch counts the polls of its check in sequence. A readiness latch starts tripped. */
131
+ export declare const LATCH_MACHINE: Machine;
132
+ /**
133
+ * A window latch measures time on the monotonic clock from the first failure of a sequence, and
134
+ * from the first pass after a trip.
135
+ */
136
+ export declare const WINDOW_MACHINE: Machine;
137
+ /**
138
+ * A poller starts one check on a jittered loop with a fixed delay. A call that outlives its timeout
139
+ * stays pending (`hung`), and the poller does not start the check again until the call settles.
140
+ */
141
+ export declare const POLLER_MACHINE: Machine;
142
+ /**
143
+ * The exit manager is the only part that ends the process. The first exit request starts the
144
+ * shutdown, and a later one has no effect. An error makes the exit code 1.
145
+ */
146
+ export declare const EXIT_MACHINE: Machine;
147
+ /** A watchdog measures how late its own tick comes, and a late tick shows a blocked event loop. */
148
+ export declare const WATCHDOG_MACHINE: Machine;
149
+ /** This function gives the name of a knob for the cap of a phase: `drain` gives `drain_cap`. */
150
+ export declare const capKnob: (phase: string) => string;
151
+ /**
152
+ * This function gives the drain of a declaration as a machine. It has one state for each declared
153
+ * phase, in its sequence, and each state has a timer at its cap. The drain also has the cooperative abort: the
154
+ * cap of the drain less the cooperative window, and half that cap at the least.
155
+ */
156
+ export declare const drainMachine: (description: ServiceDescription) => Machine;
157
+ /** These are the static machines of the runtime, by the name that their events carry. */
158
+ export declare const MACHINES: Readonly<Record<Exclude<MachineName, "drain" | "client">, Machine>>;
159
+ /**
160
+ * This function replays the transitions of one machine of the runtime against its document. Each
161
+ * instance starts in the state that its first transition leaves. Each transition must leave the
162
+ * state where the replay is. The document must have a transition from that state, on that event,
163
+ * to that state. The function gives each disagreement.
164
+ */
165
+ export declare const replay: (doc: Machine, events: readonly LifecycleEvent[]) => string[];
166
+ //#endregion
167
+ export { type Binding, type Bound, type Cancel, type Check, type CheckFn, type CheckVerdict, type Clock, type Controller, type ControllerBuilder, type Coupling, type Dependency, type DependencyHandle, EXIT_SIGNALS, type Evaluated, type ExitSignal, FORMAT, type FailsBy, type Finding, type Gate, type Health, type HealthServer, type HealthVerdict, type Idempotency, type Inequality, type Lifecycle, type LifecycleEvent, type LifecycleState, type MachineName, type Operation, type Outcome, type Phase, type PhaseContext, type PhaseWork, type Poll, type ProbeAnswer, type ProbeKind, type ProcessPort, type Recorder, type RequestFn, type RequestOutcome, type Result, type Rule, type RunOptions, STAGES, type Scenario, type ServiceBuilder, type ServiceDescription, type Side, type Span, type Stage, type StartOptions, type Step, type Term, type Timing, type Transport, type TransportImpl, type Verdict, type Watchdog, type WatchdogSpec, asDebounce, asWindow, cooperativeAbortAt, failSpan, mayReissue, nodeProcess, realClock, recoverSpan, service, stageRank, watchdog };
349
168
  //# sourceMappingURL=index.d.ts.map