@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/README.md +120 -15
- package/build/builder-BFxOuULH.d.ts +607 -0
- package/build/clock-DlJFFZlX.js +39 -0
- package/build/clock-DlJFFZlX.js.map +1 -0
- package/build/index.d.ts +47 -228
- package/build/index.js +1536 -88
- package/build/index.js.map +1 -1
- package/build/redis.d.ts +294 -0
- package/build/redis.js +853 -0
- package/build/redis.js.map +1 -0
- package/build/testing.d.ts +102 -0
- package/build/testing.js +349 -0
- package/build/testing.js.map +1 -0
- package/build/timing-DxZ4nkQg.js +79 -0
- package/build/timing-DxZ4nkQg.js.map +1 -0
- package/package.json +20 -2
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
|
-
|
|
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
|
-
|
|
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
|