@earendil-works/chord 1.2.0 → 1.3.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/CHANGELOG.md +58 -0
- package/README.md +14 -13
- package/dist/cell.d.ts +30 -9
- package/dist/cell.d.ts.map +1 -1
- package/dist/cell.js +52 -12
- package/dist/cell.js.map +1 -1
- package/dist/cluster.d.ts +4 -3
- package/dist/cluster.d.ts.map +1 -1
- package/dist/cluster.js +58 -42
- package/dist/cluster.js.map +1 -1
- package/dist/connection.d.ts +171 -58
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +676 -217
- package/dist/connection.js.map +1 -1
- package/dist/contract.d.ts +120 -45
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +104 -15
- package/dist/contract.js.map +1 -1
- package/dist/declare.d.ts +11 -48
- package/dist/declare.d.ts.map +1 -1
- package/dist/declare.js +1 -21
- package/dist/declare.js.map +1 -1
- package/dist/delta/index.d.ts +2 -1
- package/dist/delta/index.d.ts.map +1 -1
- package/dist/delta/index.js +4 -0
- package/dist/delta/index.js.map +1 -1
- package/dist/errors.d.ts +4 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +15 -0
- package/dist/errors.js.map +1 -1
- package/dist/host.d.ts +214 -120
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +446 -241
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/link.d.ts +47 -12
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +200 -14
- package/dist/link.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +7 -2
- package/dist/loader.js.map +1 -1
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +35 -9
- package/dist/manager.js.map +1 -1
- package/dist/object.d.ts +438 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/{record.js → object.js} +614 -335
- package/dist/object.js.map +1 -0
- package/dist/output.d.ts +85 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +350 -0
- package/dist/output.js.map +1 -0
- package/dist/registry.d.ts +3 -3
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/reload.d.ts +35 -27
- package/dist/reload.d.ts.map +1 -1
- package/dist/reload.js +256 -192
- package/dist/reload.js.map +1 -1
- package/dist/remote.d.ts +130 -35
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +493 -189
- package/dist/remote.js.map +1 -1
- package/dist/types.d.ts +39 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/websocket.d.ts +4 -5
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +3 -2
- package/dist/websocket.js.map +1 -1
- package/dist/wire.d.ts +210 -129
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +108 -55
- package/dist/wire.js.map +1 -1
- package/docs/guide.md +267 -203
- package/docs/spec.md +141 -114
- package/docs/tutorial.md +257 -235
- package/package.json +6 -1
- package/dist/record.d.ts +0 -320
- package/dist/record.d.ts.map +0 -1
- package/dist/record.js.map +0 -1
package/dist/cluster.js
CHANGED
|
@@ -3,12 +3,12 @@ import { beginReload, endReload, reloadError } from "./reload.js";
|
|
|
3
3
|
import { toWireError } from "./wire.js";
|
|
4
4
|
// Cross-host reload, coordinated by the server (the host without a parent):
|
|
5
5
|
// server: prepare itself and send `prepare` to every synced child host; collect votes
|
|
6
|
-
// all ok: the server installs its new
|
|
7
|
-
//
|
|
8
|
-
// can receive any frame of a root started in the new
|
|
6
|
+
// all ok: the server installs its new code version, sends `commit` to every child, and only then starts what may make
|
|
7
|
+
// new-code-version calls (tasks, listener and registry callbacks). Links are FIFO, so a child commits before it
|
|
8
|
+
// can receive any frame of a root started in the new code version.
|
|
9
9
|
// any failure or missed vote deadline: `abort` everywhere; each host rolls back to where it was.
|
|
10
10
|
// Clients (browsers) are not participants: they see shape/reset pushes, or `upgrade_required`.
|
|
11
|
-
// A child that connects behind the server's
|
|
11
|
+
// A child that connects behind the server's code version is fenced and reconciled with the same protocol.
|
|
12
12
|
let reloadSeq = 0;
|
|
13
13
|
const modulesFor = (remote, hostName) => {
|
|
14
14
|
let best;
|
|
@@ -31,33 +31,39 @@ function awaitVote(connection, id, deadline) {
|
|
|
31
31
|
return vote.finally(() => clearTimeout(timer));
|
|
32
32
|
}
|
|
33
33
|
export async function runClusterReload(host, plugins, options) {
|
|
34
|
-
// Fenced children (behind, being reconciled)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const votes = participants.map((connection) => {
|
|
41
|
-
const name = connection.name ?? "child";
|
|
42
|
-
const vote = awaitVote(connection, id, deadline);
|
|
43
|
-
connection.send({
|
|
44
|
-
t: "prepare",
|
|
45
|
-
reload: id,
|
|
46
|
-
epoch,
|
|
47
|
-
plugins: modulesFor(options.remote, name),
|
|
48
|
-
options: {
|
|
49
|
-
remove: modulesFor(options.remoteRemove, name),
|
|
50
|
-
gateDeadline: options.gateDeadline,
|
|
51
|
-
retireDeadline: options.retireDeadline,
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
return vote.then((report) => [name, report], (error) => {
|
|
55
|
-
const cause = reloadError(error);
|
|
56
|
-
throw new ChordError(cause.code, `${name}: ${cause.message}`, { cause, reason: cause.reason });
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
let local = false;
|
|
34
|
+
// Participants are the children in step now. Fenced children (behind, being reconciled) reconcile to the new code
|
|
35
|
+
// version afterwards; a child whose hello arrives while the reload runs is synced or reconciled once it ends
|
|
36
|
+
// (`HostImpl.peerHello`).
|
|
37
|
+
const synced = [...host.connections].filter((connection) => connection.role === "downstream" && connection.peer.kind === "host" && connection.open && connection.synced);
|
|
38
|
+
const codeVersion = host.peekNextCodeVersion();
|
|
39
|
+
const reload = await beginReload(host, plugins, options, codeVersion);
|
|
60
40
|
try {
|
|
41
|
+
// Retire first: its `retire` frames reach every child before `prepare` (FIFO), so a child never retires a code
|
|
42
|
+
// version this host still runs roots of.
|
|
43
|
+
await reload.waitForRetirement();
|
|
44
|
+
const participants = synced.filter((connection) => connection.open);
|
|
45
|
+
const id = ++reloadSeq;
|
|
46
|
+
const deadline = options.voteDeadline ?? (options.gateDeadline ?? 5000) + (options.retireDeadline ?? 5000) + 5000;
|
|
47
|
+
const votes = participants.map((connection) => {
|
|
48
|
+
const name = connection.name ?? "child";
|
|
49
|
+
const vote = awaitVote(connection, id, deadline);
|
|
50
|
+
connection.send({
|
|
51
|
+
t: "prepare",
|
|
52
|
+
reload: id,
|
|
53
|
+
codeVersion,
|
|
54
|
+
plugins: modulesFor(options.remote, name),
|
|
55
|
+
options: {
|
|
56
|
+
remove: modulesFor(options.remoteRemove, name),
|
|
57
|
+
gateDeadline: options.gateDeadline,
|
|
58
|
+
retireDeadline: options.retireDeadline,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
return vote.then((report) => [name, report], (error) => {
|
|
62
|
+
const cause = reloadError(error);
|
|
63
|
+
throw new ChordError(cause.code, `${name}: ${cause.message}`, { cause, reason: cause.reason });
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
let local = false;
|
|
61
67
|
const results = await Promise.allSettled([
|
|
62
68
|
reload.prepare().then(() => {
|
|
63
69
|
local = true;
|
|
@@ -77,7 +83,7 @@ export async function runClusterReload(host, plugins, options) {
|
|
|
77
83
|
const report = reload.commit(() => {
|
|
78
84
|
for (const connection of participants) {
|
|
79
85
|
connection.send({ t: "commit", reload: id });
|
|
80
|
-
connection.
|
|
86
|
+
connection.peerCodeVersion = codeVersion;
|
|
81
87
|
}
|
|
82
88
|
});
|
|
83
89
|
host.rememberRemoteModules(options.remote, options.remoteRemove);
|
|
@@ -96,36 +102,46 @@ export async function runClusterReload(host, plugins, options) {
|
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
/**
|
|
99
|
-
* Brings a child that connected behind this host's
|
|
100
|
-
* cluster reloads gave its name) to this host's current
|
|
101
|
-
* Until then the child is fenced: nothing is routed to it. The child's old code is never relabeled as a newer
|
|
105
|
+
* Brings a child that connected behind this host's code version up to it: a reload of the child's modules (the ones
|
|
106
|
+
* cluster reloads gave its name) to this host's current code version, prepared, voted and committed like a cluster
|
|
107
|
+
* reload. Until then the child is fenced: nothing is routed to it. The child's old code is never relabeled as a newer
|
|
108
|
+
* code version.
|
|
102
109
|
*/
|
|
103
110
|
export async function reconcileChild(host, connection) {
|
|
104
111
|
for (;;) {
|
|
105
|
-
const epoch = host.current.id;
|
|
106
112
|
if (!connection.open)
|
|
107
113
|
return;
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
// Not during a reload of this host: the child is not one of its participants, so whatever it would be synced to
|
|
115
|
+
// now may be superseded by that reload's commit. Recheck once it ended.
|
|
116
|
+
if (host.reloading) {
|
|
117
|
+
await host.whenReloadEnded();
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const codeVersion = host.current.id;
|
|
121
|
+
if (connection.peerCodeVersion === codeVersion) {
|
|
122
|
+
connection.markSynced(codeVersion);
|
|
123
|
+
host.announceRetired(connection);
|
|
110
124
|
return;
|
|
111
125
|
}
|
|
126
|
+
// Before `prepare`: the child's prepare first retires its previous code version, which waits for this permission.
|
|
127
|
+
host.announceRetired(connection);
|
|
112
128
|
const id = ++reloadSeq;
|
|
113
129
|
const vote = awaitVote(connection, id, 30_000);
|
|
114
130
|
connection.send({
|
|
115
131
|
t: "prepare",
|
|
116
132
|
reload: id,
|
|
117
|
-
|
|
133
|
+
codeVersion,
|
|
118
134
|
plugins: modulesFor(host.remoteModules, connection.name),
|
|
119
135
|
options: { remove: modulesFor(host.remoteRemoved, connection.name) },
|
|
120
136
|
});
|
|
121
137
|
await vote;
|
|
122
|
-
if (host.current.id !==
|
|
123
|
-
// This host moved on meanwhile: abort and try again with the newer
|
|
138
|
+
if (host.current.id !== codeVersion) {
|
|
139
|
+
// This host moved on meanwhile: abort and try again with the newer code version.
|
|
124
140
|
connection.send({ t: "abort", reload: id });
|
|
125
141
|
continue;
|
|
126
142
|
}
|
|
127
143
|
connection.send({ t: "commit", reload: id });
|
|
128
|
-
connection.
|
|
144
|
+
connection.peerCodeVersion = codeVersion;
|
|
129
145
|
}
|
|
130
146
|
}
|
|
131
147
|
/**
|
|
@@ -189,7 +205,7 @@ export class ChildReloads {
|
|
|
189
205
|
async #run(operation, connection, frame) {
|
|
190
206
|
const cancelled = () => new ChordError("aborted", "reload aborted by the server", { reason: "aborted" });
|
|
191
207
|
try {
|
|
192
|
-
operation.reload = await beginReload(this.#host, frame.plugins, frame.options, frame.
|
|
208
|
+
operation.reload = await beginReload(this.#host, frame.plugins, frame.options, frame.codeVersion);
|
|
193
209
|
if (operation.cancelled)
|
|
194
210
|
throw cancelled();
|
|
195
211
|
operation.state = "preparing";
|
package/dist/cluster.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster.js","sourceRoot":"","sources":["../src/cluster.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Connection } from \"./connection.ts\";\nimport { ChordError } from \"./errors.ts\";\nimport type { HostImpl, PluginRef } from \"./host.ts\";\nimport { beginReload, endReload, type Reload, type ReloadOptions, type ReloadReport, reloadError } from \"./reload.ts\";\nimport { type Frame, toWireError } from \"./wire.ts\";\n\n// Cross-host reload, coordinated by the server (the host without a parent):\n// server: prepare itself and send `prepare` to every synced child host; collect votes\n// all ok: the server installs its new epoch, sends `commit` to every child, and only then starts what may make\n// new-epoch calls (tasks, listener and registry callbacks). Links are FIFO, so a child commits before it\n// can receive any frame of a root started in the new epoch.\n// any failure or missed vote deadline: `abort` everywhere; each host rolls back to where it was.\n// Clients (browsers) are not participants: they see shape/reset pushes, or `upgrade_required`.\n// A child that connects behind the server's epoch is fenced and reconciled with the same protocol.\n\nlet reloadSeq = 0;\n\nconst modulesFor = (remote: ReloadOptions[\"remote\"], hostName: string | undefined): string[] => {\n\tlet best: [string, readonly string[]] | undefined;\n\tfor (const entry of Object.entries(remote ?? {})) {\n\t\tif (hostName?.startsWith(entry[0]) && (best === undefined || entry[0].length > best[0].length)) best = entry;\n\t}\n\treturn [...(best?.[1] ?? [])];\n};\n\n/** A vote, or its rejection after `deadline` milliseconds. */\nfunction awaitVote(connection: Connection, id: number, deadline: number): Promise<unknown> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst vote = new Promise<unknown>((resolve, reject) => {\n\t\tconnection.votes.set(id, { resolve, reject });\n\t\ttimer = setTimeout(() => {\n\t\t\tconnection.votes.delete(id);\n\t\t\treject(new ChordError(\"aborted\", `votes missing after ${deadline} ms`, { reason: \"vote_deadline\" }));\n\t\t}, deadline);\n\t});\n\treturn vote.finally(() => clearTimeout(timer));\n}\n\nexport async function runClusterReload(\n\thost: HostImpl,\n\tplugins: readonly PluginRef[],\n\toptions: ReloadOptions,\n): Promise<ReloadReport> {\n\t// Fenced children (behind, being reconciled) are not participants; they reconcile to the new epoch afterwards.\n\tconst participants = [...host.connections].filter(\n\t\t(connection) =>\n\t\t\tconnection.role === \"downstream\" && connection.peer.kind === \"host\" && connection.open && connection.synced,\n\t);\n\tconst epoch = host.peekNextEpoch();\n\tconst reload = await beginReload(host, plugins, options, epoch);\n\tconst id = ++reloadSeq;\n\tconst deadline = options.voteDeadline ?? (options.gateDeadline ?? 5000) + (options.retireDeadline ?? 5000) + 5000;\n\tconst votes = participants.map((connection) => {\n\t\tconst name = connection.name ?? \"child\";\n\t\tconst vote = awaitVote(connection, id, deadline);\n\t\tconnection.send({\n\t\t\tt: \"prepare\",\n\t\t\treload: id,\n\t\t\tepoch,\n\t\t\tplugins: modulesFor(options.remote, name),\n\t\t\toptions: {\n\t\t\t\tremove: modulesFor(options.remoteRemove, name),\n\t\t\t\tgateDeadline: options.gateDeadline,\n\t\t\t\tretireDeadline: options.retireDeadline,\n\t\t\t},\n\t\t});\n\t\treturn vote.then(\n\t\t\t(report) => [name, report as ReloadReport | undefined] as const,\n\t\t\t(error: unknown) => {\n\t\t\t\tconst cause = reloadError(error);\n\t\t\t\tthrow new ChordError(cause.code, `${name}: ${cause.message}`, { cause, reason: cause.reason });\n\t\t\t},\n\t\t);\n\t});\n\tlet local = false;\n\ttry {\n\t\tconst results = await Promise.allSettled([\n\t\t\treload.prepare().then(() => {\n\t\t\t\tlocal = true;\n\t\t\t}),\n\t\t\t...votes,\n\t\t]);\n\t\tconst failed = results.find((result) => result.status === \"rejected\");\n\t\tif (failed !== undefined) {\n\t\t\tfor (const connection of participants) {\n\t\t\t\tconnection.votes.delete(id);\n\t\t\t\tconnection.send({ t: \"abort\", reload: id });\n\t\t\t}\n\t\t\tif (local) await reload.rollback();\n\t\t\tthrow failed.reason;\n\t\t}\n\t\tconst report = reload.commit(() => {\n\t\t\tfor (const connection of participants) {\n\t\t\t\tconnection.send({ t: \"commit\", reload: id });\n\t\t\t\tconnection.peerEpoch = epoch;\n\t\t\t}\n\t\t});\n\t\thost.rememberRemoteModules(options.remote, options.remoteRemove);\n\t\tconst children: Record<string, ReloadReport> = {};\n\t\tfor (const result of results.slice(1)) {\n\t\t\tif (result.status !== \"fulfilled\") continue;\n\t\t\tconst [name, child] = result.value as readonly [string, ReloadReport | undefined];\n\t\t\tif (child !== undefined) children[name] = child;\n\t\t}\n\t\treturn { ...report, children };\n\t} finally {\n\t\tendReload(host);\n\t}\n}\n\n/**\n * Brings a child that connected behind this host's epoch up to it: a reload of the child's modules (the ones\n * cluster reloads gave its name) to this host's current epoch, prepared, voted and committed like a cluster reload.\n * Until then the child is fenced: nothing is routed to it. The child's old code is never relabeled as a newer epoch.\n */\nexport async function reconcileChild(host: HostImpl, connection: Connection): Promise<void> {\n\tfor (;;) {\n\t\tconst epoch = host.current.id;\n\t\tif (!connection.open) return;\n\t\tif (connection.peerEpoch === epoch) {\n\t\t\tconnection.markSynced(epoch);\n\t\t\treturn;\n\t\t}\n\t\tconst id = ++reloadSeq;\n\t\tconst vote = awaitVote(connection, id, 30_000);\n\t\tconnection.send({\n\t\t\tt: \"prepare\",\n\t\t\treload: id,\n\t\t\tepoch,\n\t\t\tplugins: modulesFor(host.remoteModules, connection.name),\n\t\t\toptions: { remove: modulesFor(host.remoteRemoved, connection.name) },\n\t\t});\n\t\tawait vote;\n\t\tif (host.current.id !== epoch) {\n\t\t\t// This host moved on meanwhile: abort and try again with the newer epoch.\n\t\t\tconnection.send({ t: \"abort\", reload: id });\n\t\t\tcontinue;\n\t\t}\n\t\tconnection.send({ t: \"commit\", reload: id });\n\t\tconnection.peerEpoch = epoch;\n\t}\n}\n\n/** One prepare the child received, registered before its first await so an abort or disconnect is never lost. */\ninterface ChildOperation {\n\treadonly id: number;\n\treload: Reload | undefined;\n\tstate: \"loading\" | \"preparing\" | \"prepared\";\n\tcancelled: boolean;\n}\n\n/**\n * The child side: prepare and vote, then wait for the server's commit or abort. An abort or a lost parent link can\n * arrive at any point: while loading modules, while preparing (pending opens close themselves when they complete),\n * or after the vote. The operation is unwound once its pending work settles, and the reload slot is freed.\n */\nexport class ChildReloads {\n\treadonly #host: HostImpl;\n\t#operation: ChildOperation | undefined;\n\n\tconstructor(host: HostImpl) {\n\t\tthis.#host = host;\n\t}\n\n\thandle(connection: Connection, frame: Extract<Frame, { t: \"prepare\" | \"commit\" | \"abort\" }>): void {\n\t\tif (frame.t === \"prepare\") {\n\t\t\tif (this.#operation !== undefined) {\n\t\t\t\tconst error = new ChordError(\"unavailable\", \"a reload is already running\", { reason: \"reload_running\" });\n\t\t\t\tconnection.send({ t: \"vote\", reload: frame.reload, error: toWireError(error) });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst operation: ChildOperation = { id: frame.reload, reload: undefined, state: \"loading\", cancelled: false };\n\t\t\tthis.#operation = operation;\n\t\t\tthis.#run(operation, connection, frame).catch((error: unknown) => this.#host.report(error));\n\t\t\treturn;\n\t\t}\n\t\tconst operation = this.#operation;\n\t\tif (operation === undefined || operation.id !== frame.reload) return;\n\t\tif (frame.t === \"abort\") {\n\t\t\tthis.#cancel(operation);\n\t\t\treturn;\n\t\t}\n\t\tif (operation.state !== \"prepared\") return;\n\t\tthis.#operation = undefined;\n\t\ttry {\n\t\t\toperation.reload?.commit();\n\t\t} finally {\n\t\t\tendReload(this.#host);\n\t\t}\n\t}\n\n\t/** The parent link died: nobody will commit, so roll back (now, or once pending work settles). */\n\tparentLost(): void {\n\t\tif (this.#operation !== undefined) this.#cancel(this.#operation);\n\t}\n\n\t#cancel(operation: ChildOperation): void {\n\t\toperation.cancelled = true;\n\t\tif (operation.state !== \"prepared\") {\n\t\t\t// Loading or preparing: make prepare fail now; #run unwinds when it settles.\n\t\t\toperation.reload?.rollback().catch((error: unknown) => this.#host.report(error));\n\t\t\treturn;\n\t\t}\n\t\tthis.#operation = undefined;\n\t\toperation.reload\n\t\t\t?.rollback()\n\t\t\t.catch((error: unknown) => this.#host.report(error))\n\t\t\t.finally(() => endReload(this.#host));\n\t}\n\n\tasync #run(\n\t\toperation: ChildOperation,\n\t\tconnection: Connection,\n\t\tframe: Extract<Frame, { t: \"prepare\" }>,\n\t): Promise<void> {\n\t\tconst cancelled = (): ChordError =>\n\t\t\tnew ChordError(\"aborted\", \"reload aborted by the server\", { reason: \"aborted\" });\n\t\ttry {\n\t\t\toperation.reload = await beginReload(this.#host, frame.plugins, frame.options, frame.epoch);\n\t\t\tif (operation.cancelled) throw cancelled();\n\t\t\toperation.state = \"preparing\";\n\t\t\tawait operation.reload.prepare();\n\t\t\tif (operation.cancelled) throw cancelled();\n\t\t\toperation.state = \"prepared\";\n\t\t\tconnection.send({ t: \"vote\", reload: frame.reload, report: operation.reload.summary() });\n\t\t} catch (error) {\n\t\t\tif (operation.reload !== undefined) {\n\t\t\t\tawait operation.reload.rollback().catch((cleanup: unknown) => this.#host.report(cleanup));\n\t\t\t\tendReload(this.#host);\n\t\t\t}\n\t\t\tif (this.#operation === operation) this.#operation = undefined;\n\t\t\tif (!operation.cancelled) connection.send({ t: \"vote\", reload: frame.reload, error: toWireError(error) });\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cluster.js","sourceRoot":"","sources":["../src/cluster.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Connection } from \"./connection.ts\";\nimport { ChordError } from \"./errors.ts\";\nimport type { HostImpl, PluginRef } from \"./host.ts\";\nimport { beginReload, endReload, type Reload, type ReloadOptions, type ReloadReport, reloadError } from \"./reload.ts\";\nimport { type Frame, toWireError } from \"./wire.ts\";\n\n// Cross-host reload, coordinated by the server (the host without a parent):\n// server: prepare itself and send `prepare` to every synced child host; collect votes\n// all ok: the server installs its new code version, sends `commit` to every child, and only then starts what may make\n// new-code-version calls (tasks, listener and registry callbacks). Links are FIFO, so a child commits before it\n// can receive any frame of a root started in the new code version.\n// any failure or missed vote deadline: `abort` everywhere; each host rolls back to where it was.\n// Clients (browsers) are not participants: they see shape/reset pushes, or `upgrade_required`.\n// A child that connects behind the server's code version is fenced and reconciled with the same protocol.\n\nlet reloadSeq = 0;\n\nconst modulesFor = (remote: ReloadOptions[\"remote\"], hostName: string | undefined): string[] => {\n\tlet best: [string, readonly string[]] | undefined;\n\tfor (const entry of Object.entries(remote ?? {})) {\n\t\tif (hostName?.startsWith(entry[0]) && (best === undefined || entry[0].length > best[0].length)) best = entry;\n\t}\n\treturn [...(best?.[1] ?? [])];\n};\n\n/** A vote, or its rejection after `deadline` milliseconds. */\nfunction awaitVote(connection: Connection, id: number, deadline: number): Promise<unknown> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst vote = new Promise<unknown>((resolve, reject) => {\n\t\tconnection.votes.set(id, { resolve, reject });\n\t\ttimer = setTimeout(() => {\n\t\t\tconnection.votes.delete(id);\n\t\t\treject(new ChordError(\"aborted\", `votes missing after ${deadline} ms`, { reason: \"vote_deadline\" }));\n\t\t}, deadline);\n\t});\n\treturn vote.finally(() => clearTimeout(timer));\n}\n\nexport async function runClusterReload(\n\thost: HostImpl,\n\tplugins: readonly PluginRef[],\n\toptions: ReloadOptions,\n): Promise<ReloadReport> {\n\t// Participants are the children in step now. Fenced children (behind, being reconciled) reconcile to the new code\n\t// version afterwards; a child whose hello arrives while the reload runs is synced or reconciled once it ends\n\t// (`HostImpl.peerHello`).\n\tconst synced = [...host.connections].filter(\n\t\t(connection) =>\n\t\t\tconnection.role === \"downstream\" && connection.peer.kind === \"host\" && connection.open && connection.synced,\n\t);\n\tconst codeVersion = host.peekNextCodeVersion();\n\tconst reload = await beginReload(host, plugins, options, codeVersion);\n\ttry {\n\t\t// Retire first: its `retire` frames reach every child before `prepare` (FIFO), so a child never retires a code\n\t\t// version this host still runs roots of.\n\t\tawait reload.waitForRetirement();\n\t\tconst participants = synced.filter((connection) => connection.open);\n\t\tconst id = ++reloadSeq;\n\t\tconst deadline = options.voteDeadline ?? (options.gateDeadline ?? 5000) + (options.retireDeadline ?? 5000) + 5000;\n\t\tconst votes = participants.map((connection) => {\n\t\t\tconst name = connection.name ?? \"child\";\n\t\t\tconst vote = awaitVote(connection, id, deadline);\n\t\t\tconnection.send({\n\t\t\t\tt: \"prepare\",\n\t\t\t\treload: id,\n\t\t\t\tcodeVersion,\n\t\t\t\tplugins: modulesFor(options.remote, name),\n\t\t\t\toptions: {\n\t\t\t\t\tremove: modulesFor(options.remoteRemove, name),\n\t\t\t\t\tgateDeadline: options.gateDeadline,\n\t\t\t\t\tretireDeadline: options.retireDeadline,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn vote.then(\n\t\t\t\t(report) => [name, report as ReloadReport | undefined] as const,\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\tconst cause = reloadError(error);\n\t\t\t\t\tthrow new ChordError(cause.code, `${name}: ${cause.message}`, { cause, reason: cause.reason });\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t\tlet local = false;\n\t\tconst results = await Promise.allSettled([\n\t\t\treload.prepare().then(() => {\n\t\t\t\tlocal = true;\n\t\t\t}),\n\t\t\t...votes,\n\t\t]);\n\t\tconst failed = results.find((result) => result.status === \"rejected\");\n\t\tif (failed !== undefined) {\n\t\t\tfor (const connection of participants) {\n\t\t\t\tconnection.votes.delete(id);\n\t\t\t\tconnection.send({ t: \"abort\", reload: id });\n\t\t\t}\n\t\t\tif (local) await reload.rollback();\n\t\t\tthrow failed.reason;\n\t\t}\n\t\tconst report = reload.commit(() => {\n\t\t\tfor (const connection of participants) {\n\t\t\t\tconnection.send({ t: \"commit\", reload: id });\n\t\t\t\tconnection.peerCodeVersion = codeVersion;\n\t\t\t}\n\t\t});\n\t\thost.rememberRemoteModules(options.remote, options.remoteRemove);\n\t\tconst children: Record<string, ReloadReport> = {};\n\t\tfor (const result of results.slice(1)) {\n\t\t\tif (result.status !== \"fulfilled\") continue;\n\t\t\tconst [name, child] = result.value as readonly [string, ReloadReport | undefined];\n\t\t\tif (child !== undefined) children[name] = child;\n\t\t}\n\t\treturn { ...report, children };\n\t} finally {\n\t\tendReload(host);\n\t}\n}\n\n/**\n * Brings a child that connected behind this host's code version up to it: a reload of the child's modules (the ones\n * cluster reloads gave its name) to this host's current code version, prepared, voted and committed like a cluster\n * reload. Until then the child is fenced: nothing is routed to it. The child's old code is never relabeled as a newer\n * code version.\n */\nexport async function reconcileChild(host: HostImpl, connection: Connection): Promise<void> {\n\tfor (;;) {\n\t\tif (!connection.open) return;\n\t\t// Not during a reload of this host: the child is not one of its participants, so whatever it would be synced to\n\t\t// now may be superseded by that reload's commit. Recheck once it ended.\n\t\tif (host.reloading) {\n\t\t\tawait host.whenReloadEnded();\n\t\t\tcontinue;\n\t\t}\n\t\tconst codeVersion = host.current.id;\n\t\tif (connection.peerCodeVersion === codeVersion) {\n\t\t\tconnection.markSynced(codeVersion);\n\t\t\thost.announceRetired(connection);\n\t\t\treturn;\n\t\t}\n\t\t// Before `prepare`: the child's prepare first retires its previous code version, which waits for this permission.\n\t\thost.announceRetired(connection);\n\t\tconst id = ++reloadSeq;\n\t\tconst vote = awaitVote(connection, id, 30_000);\n\t\tconnection.send({\n\t\t\tt: \"prepare\",\n\t\t\treload: id,\n\t\t\tcodeVersion,\n\t\t\tplugins: modulesFor(host.remoteModules, connection.name),\n\t\t\toptions: { remove: modulesFor(host.remoteRemoved, connection.name) },\n\t\t});\n\t\tawait vote;\n\t\tif (host.current.id !== codeVersion) {\n\t\t\t// This host moved on meanwhile: abort and try again with the newer code version.\n\t\t\tconnection.send({ t: \"abort\", reload: id });\n\t\t\tcontinue;\n\t\t}\n\t\tconnection.send({ t: \"commit\", reload: id });\n\t\tconnection.peerCodeVersion = codeVersion;\n\t}\n}\n\n/** One prepare the child received, registered before its first await so an abort or disconnect is never lost. */\ninterface ChildOperation {\n\treadonly id: number;\n\treload: Reload | undefined;\n\tstate: \"loading\" | \"preparing\" | \"prepared\";\n\tcancelled: boolean;\n}\n\n/**\n * The child side: prepare and vote, then wait for the server's commit or abort. An abort or a lost parent link can\n * arrive at any point: while loading modules, while preparing (pending opens close themselves when they complete),\n * or after the vote. The operation is unwound once its pending work settles, and the reload slot is freed.\n */\nexport class ChildReloads {\n\treadonly #host: HostImpl;\n\t#operation: ChildOperation | undefined;\n\n\tconstructor(host: HostImpl) {\n\t\tthis.#host = host;\n\t}\n\n\thandle(connection: Connection, frame: Extract<Frame, { t: \"prepare\" | \"commit\" | \"abort\" }>): void {\n\t\tif (frame.t === \"prepare\") {\n\t\t\tif (this.#operation !== undefined) {\n\t\t\t\tconst error = new ChordError(\"unavailable\", \"a reload is already running\", { reason: \"reload_running\" });\n\t\t\t\tconnection.send({ t: \"vote\", reload: frame.reload, error: toWireError(error) });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst operation: ChildOperation = { id: frame.reload, reload: undefined, state: \"loading\", cancelled: false };\n\t\t\tthis.#operation = operation;\n\t\t\tthis.#run(operation, connection, frame).catch((error: unknown) => this.#host.report(error));\n\t\t\treturn;\n\t\t}\n\t\tconst operation = this.#operation;\n\t\tif (operation === undefined || operation.id !== frame.reload) return;\n\t\tif (frame.t === \"abort\") {\n\t\t\tthis.#cancel(operation);\n\t\t\treturn;\n\t\t}\n\t\tif (operation.state !== \"prepared\") return;\n\t\tthis.#operation = undefined;\n\t\ttry {\n\t\t\toperation.reload?.commit();\n\t\t} finally {\n\t\t\tendReload(this.#host);\n\t\t}\n\t}\n\n\t/** The parent link died: nobody will commit, so roll back (now, or once pending work settles). */\n\tparentLost(): void {\n\t\tif (this.#operation !== undefined) this.#cancel(this.#operation);\n\t}\n\n\t#cancel(operation: ChildOperation): void {\n\t\toperation.cancelled = true;\n\t\tif (operation.state !== \"prepared\") {\n\t\t\t// Loading or preparing: make prepare fail now; #run unwinds when it settles.\n\t\t\toperation.reload?.rollback().catch((error: unknown) => this.#host.report(error));\n\t\t\treturn;\n\t\t}\n\t\tthis.#operation = undefined;\n\t\toperation.reload\n\t\t\t?.rollback()\n\t\t\t.catch((error: unknown) => this.#host.report(error))\n\t\t\t.finally(() => endReload(this.#host));\n\t}\n\n\tasync #run(\n\t\toperation: ChildOperation,\n\t\tconnection: Connection,\n\t\tframe: Extract<Frame, { t: \"prepare\" }>,\n\t): Promise<void> {\n\t\tconst cancelled = (): ChordError =>\n\t\t\tnew ChordError(\"aborted\", \"reload aborted by the server\", { reason: \"aborted\" });\n\t\ttry {\n\t\t\toperation.reload = await beginReload(this.#host, frame.plugins, frame.options, frame.codeVersion);\n\t\t\tif (operation.cancelled) throw cancelled();\n\t\t\toperation.state = \"preparing\";\n\t\t\tawait operation.reload.prepare();\n\t\t\tif (operation.cancelled) throw cancelled();\n\t\t\toperation.state = \"prepared\";\n\t\t\tconnection.send({ t: \"vote\", reload: frame.reload, report: operation.reload.summary() });\n\t\t} catch (error) {\n\t\t\tif (operation.reload !== undefined) {\n\t\t\t\tawait operation.reload.rollback().catch((cleanup: unknown) => this.#host.report(cleanup));\n\t\t\t\tendReload(this.#host);\n\t\t\t}\n\t\t\tif (this.#operation === operation) this.#operation = undefined;\n\t\t\tif (!operation.cancelled) connection.send({ t: \"vote\", reload: frame.reload, error: toWireError(error) });\n\t\t}\n\t}\n}\n"]}
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,82 +1,152 @@
|
|
|
1
|
+
import { type Encoder } from "./delta/index.ts";
|
|
1
2
|
import type { HostImpl } from "./host.ts";
|
|
2
|
-
import type
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
3
|
+
import { type Link } from "./link.ts";
|
|
4
|
+
import { RefImpl } from "./object.ts";
|
|
5
|
+
import type { RemoteBinding, RemoteChannel } from "./remote.ts";
|
|
6
|
+
import type { StateEvent } from "./types.ts";
|
|
6
7
|
import { type Frame, type RootRef } from "./wire.ts";
|
|
7
8
|
export interface LinkOptions {
|
|
8
9
|
/** Milliseconds between pings (default 15000). */
|
|
9
10
|
readonly pingInterval?: number;
|
|
10
11
|
/** Close the link when nothing arrived for this long (default 45000). Everything held through it is released. */
|
|
11
12
|
readonly pingTimeout?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Unacknowledged frame bytes (JSON text length) beyond which state frames wait for the peer (default 256 KiB). Only
|
|
15
|
+
* applies when both ends acknowledge frames and the link reports sizes; `Infinity` never holds state back.
|
|
16
|
+
*/
|
|
17
|
+
readonly window?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Bytes of state ops that may wait for the peer on this link (default 4 windows). Beyond, the oldest waiting streams
|
|
20
|
+
* are replaced by their current value (a reset), so memory stays bounded however long the peer stalls.
|
|
21
|
+
*/
|
|
22
|
+
readonly pendingCap?: number;
|
|
23
|
+
/**
|
|
24
|
+
* The peer speaks another link protocol version (`peer`: undefined if it did not say): the link closes, and should
|
|
25
|
+
* not be reconnected with the same code. A browser reloads its page, for instance.
|
|
26
|
+
*/
|
|
27
|
+
readonly onProtocolMismatch?: (versions: {
|
|
28
|
+
readonly local: number;
|
|
29
|
+
readonly peer: number | undefined;
|
|
30
|
+
}) => void;
|
|
12
31
|
}
|
|
13
|
-
|
|
32
|
+
type StateFrame = Extract<Frame, {
|
|
33
|
+
t: "state";
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* How a waiting stream recovers when its ops must be dropped: the provider resends the current value as a reset at
|
|
37
|
+
* flush; a relay (which has no value) asks downstream for one.
|
|
38
|
+
*/
|
|
39
|
+
type Overflow = {
|
|
40
|
+
readonly resend: () => void;
|
|
41
|
+
} | {
|
|
42
|
+
readonly resync: () => void;
|
|
43
|
+
};
|
|
44
|
+
export interface AcceptOptions<P = unknown> extends LinkOptions {
|
|
14
45
|
/** The principal of whatever connects, from the app's authenticator. Every request on the link acts as it. */
|
|
15
|
-
readonly principal:
|
|
46
|
+
readonly principal: P;
|
|
16
47
|
/**
|
|
17
|
-
* What the peer is, decided here and never by its `hello`: `client` (default; a browser: no roots, no
|
|
18
|
-
* a reload participant, not addressable) or `host` (a child host; needs `name`).
|
|
48
|
+
* What the peer is, decided here and never by its `hello`: `client` (default; a browser: no roots, no code versions,
|
|
49
|
+
* not a reload participant, not addressable) or `host` (a child host; needs `name`).
|
|
19
50
|
*/
|
|
20
51
|
readonly kind?: "host" | "client";
|
|
21
52
|
/** A child host's name: its hello must match, and it is reachable `at` this name. */
|
|
22
53
|
readonly name?: string;
|
|
23
54
|
/** On `invalidatePrincipal`: a new principal keeps the link, `undefined` closes it (default: close). */
|
|
24
|
-
|
|
55
|
+
restamp?(principal: P): P | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Calls the peer may have in progress on this link (default: no limit). Further ones never run: they are answered
|
|
58
|
+
* at once with `unavailable` (reason `too_many_calls`), and the link stays open.
|
|
59
|
+
*/
|
|
60
|
+
readonly maxCalls?: number;
|
|
25
61
|
}
|
|
26
62
|
export interface Peer {
|
|
27
63
|
readonly kind: "host" | "client";
|
|
28
64
|
/** Configured (downstream hosts) or announced by the trusted parent (upstream). */
|
|
29
65
|
readonly name: string | undefined;
|
|
30
66
|
}
|
|
31
|
-
/** A
|
|
32
|
-
|
|
67
|
+
/** A state member's stream to the consumer: its subscription and delta encoder. */
|
|
68
|
+
interface Feed {
|
|
69
|
+
unsubscribe: () => void;
|
|
70
|
+
encoder: Encoder;
|
|
71
|
+
/** Events that arrived while a unit or reset was being built (replayed after it is sent). */
|
|
72
|
+
buffer: StateEvent<unknown>[] | undefined;
|
|
73
|
+
}
|
|
74
|
+
/** A hold the peer made on this host and serves locally. */
|
|
75
|
+
interface ServedLocal {
|
|
33
76
|
readonly kind: "local";
|
|
34
|
-
readonly
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
|
|
77
|
+
readonly id: number;
|
|
78
|
+
readonly ref: RefImpl;
|
|
79
|
+
readonly principal: unknown;
|
|
80
|
+
/** The client connection it comes from (see `Frame` hold `origin`); undefined for a host's own holds. */
|
|
81
|
+
readonly origin: string | undefined;
|
|
82
|
+
/** The consumer wants state (hold without `state: false`, or `sub`). */
|
|
83
|
+
wantsState: boolean;
|
|
84
|
+
/** The consumer had values before (re-hold): its first unit waits for the window like any other. */
|
|
85
|
+
readonly again: boolean;
|
|
86
|
+
/** A unit was sent. */
|
|
87
|
+
unitSent: boolean;
|
|
88
|
+
/** The members whose snapshot the consumer has, with their streams. */
|
|
89
|
+
readonly feeds: Map<string, Feed>;
|
|
90
|
+
}
|
|
91
|
+
/** A hold the peer made on this host: served locally or relayed to another link. */
|
|
92
|
+
type Served = ServedLocal | {
|
|
38
93
|
readonly kind: "relay";
|
|
39
|
-
readonly relay:
|
|
94
|
+
readonly relay: RelayRef;
|
|
40
95
|
};
|
|
41
96
|
type Pending = {
|
|
42
97
|
readonly resolve: (value: unknown) => void;
|
|
43
98
|
readonly reject: (error: unknown) => void;
|
|
44
99
|
};
|
|
45
100
|
/**
|
|
46
|
-
* A hold relayed from an origin link to a downstream link. It owns everything done through it:
|
|
47
|
-
*
|
|
48
|
-
*
|
|
101
|
+
* A hold relayed from an origin link to a downstream link. It owns everything done through it: state and calls queued
|
|
102
|
+
* while the route resolves, and the downstream twins once forwarded. `close` undoes all of it exactly once (cancels
|
|
103
|
+
* calls downstream, drops what waits for the origin, releases the downstream hold, rejects waiters).
|
|
49
104
|
*/
|
|
50
|
-
export declare class
|
|
105
|
+
export declare class RelayRef {
|
|
51
106
|
#private;
|
|
52
107
|
readonly origin: Connection;
|
|
53
108
|
readonly frame: Extract<Frame, {
|
|
54
109
|
t: "hold";
|
|
55
110
|
}>;
|
|
56
|
-
readonly principal:
|
|
57
|
-
|
|
111
|
+
readonly principal: unknown;
|
|
112
|
+
/** The client connection the hold comes from (forwarded downstream); undefined for a host's own holds. */
|
|
113
|
+
readonly originTag: string | undefined;
|
|
58
114
|
/** The origin's authorization generation when the hold arrived; a revocation bumps it. */
|
|
59
115
|
readonly generation: number;
|
|
60
116
|
down: Connection | undefined;
|
|
61
117
|
downId: number;
|
|
62
118
|
closed: boolean;
|
|
119
|
+
/** The origin wants state (forwarded as `state: false`, `sub`, `unsub`). */
|
|
120
|
+
stateWanted: boolean;
|
|
121
|
+
/** The members of the first unit, still to forward past the origin's window. */
|
|
122
|
+
urgent: Set<string> | undefined;
|
|
123
|
+
/** Members whose ops this relay dropped: downstream ops are ignored until their reset arrives. */
|
|
124
|
+
readonly awaiting: Set<string>;
|
|
63
125
|
readonly calls: Map<number, RelayCall>;
|
|
64
|
-
readonly subs: Map<number, {
|
|
65
|
-
readonly member: string;
|
|
66
|
-
downId: number | undefined;
|
|
67
|
-
}>;
|
|
68
126
|
constructor(origin: Connection, frame: Extract<Frame, {
|
|
69
127
|
t: "hold";
|
|
70
|
-
}>, principal:
|
|
128
|
+
}>, principal: unknown, originTag: string | undefined);
|
|
71
129
|
get originId(): number;
|
|
130
|
+
get client(): boolean;
|
|
72
131
|
/** Still authorized as it was when it arrived. */
|
|
73
132
|
get current(): boolean;
|
|
74
133
|
/** The downstream connection once the route resolved; rejects if the relay closes first. */
|
|
75
134
|
ready(): Promise<Connection>;
|
|
76
|
-
/** Forwards the hold
|
|
135
|
+
/** Forwards the hold to `down`. */
|
|
77
136
|
open(down: Connection): void;
|
|
78
|
-
|
|
79
|
-
|
|
137
|
+
/** The origin starts or stops the hold's state. */
|
|
138
|
+
wantState(wanted: boolean): void;
|
|
139
|
+
/**
|
|
140
|
+
* The origin could not keep up and dropped what waited for member `m` (or asked for it again): ask downstream for a
|
|
141
|
+
* reset, and ignore its ops until the reset arrives.
|
|
142
|
+
*/
|
|
143
|
+
resync(m: string): void;
|
|
144
|
+
/** A shape from downstream: forwarded at once; the members of the first unit then skip the origin's window. */
|
|
145
|
+
shape(frame: Extract<Frame, {
|
|
146
|
+
t: "shape";
|
|
147
|
+
}>): void;
|
|
148
|
+
/** A state frame from downstream. */
|
|
149
|
+
state(frame: StateFrame): void;
|
|
80
150
|
/**
|
|
81
151
|
* Closes the relay. `releaseDown`: the downstream hold is still there (origin released, disconnected, or was
|
|
82
152
|
* revoked): cancel its calls and release it. `notify`: tell the origin the hold is gone.
|
|
@@ -89,15 +159,52 @@ export declare class RelayHold {
|
|
|
89
159
|
/** One relayed call: settles once, by result, cancellation or the relay closing; removes both mappings. */
|
|
90
160
|
export declare class RelayCall {
|
|
91
161
|
#private;
|
|
92
|
-
readonly relay:
|
|
162
|
+
readonly relay: RelayRef;
|
|
93
163
|
readonly originId: number;
|
|
94
164
|
readonly downId: number;
|
|
95
|
-
constructor(relay:
|
|
165
|
+
constructor(relay: RelayRef, originId: number, downId: number, pending: Pending);
|
|
96
166
|
finish(error: unknown, value?: unknown): void;
|
|
97
167
|
/** Cancels downstream (when the downstream hold still exists) and settles with `error`. */
|
|
98
168
|
cancel(error: unknown, forward: boolean): void;
|
|
99
169
|
}
|
|
100
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* One end of a link on a host: consumer of remote references, provider for the peer's holds, and relay.
|
|
172
|
+
*
|
|
173
|
+
* State
|
|
174
|
+
* -----
|
|
175
|
+
*
|
|
176
|
+
* A hold implies the state of its variant (unless it says `state: false`). The provider answers with a unit: the shape,
|
|
177
|
+
* then a snapshot of each state member the consumer does not have yet (`shape.state` lists them), then the members'
|
|
178
|
+
* ops and resets. A reload or a variant change sends another unit with the members that are new; members that left the
|
|
179
|
+
* variant stop. The consumer applies a shape once its snapshots are here (`RemoteBinding`).
|
|
180
|
+
*
|
|
181
|
+
* Flow control
|
|
182
|
+
* ------------
|
|
183
|
+
*
|
|
184
|
+
* Problem: state is pushed as fast as it changes, but a peer may read slowly (a phone behind a relay), and a relay may
|
|
185
|
+
* hide that: Cloudflare forwards to browsers without backpressure. Everything sent then queues in front of the next
|
|
186
|
+
* call result. Example: a browser opens three long transcripts (500 KiB of snapshots each) over a 5 Mbit/s path; a
|
|
187
|
+
* result sent next waits 2.4 s, and the snapshot of a transcript the user already left is still sent in full.
|
|
188
|
+
*
|
|
189
|
+
* Solution: the peer acknowledges what it processed (`ack`, a cumulative frame count), and the sender counts the sizes
|
|
190
|
+
* of the frames not yet acknowledged. While they exceed the window, state waits here in slots, flushed in creation
|
|
191
|
+
* order as acknowledgements come:
|
|
192
|
+
* - a member's stream has one slot: ops are appended (encoded batches of one stream may be concatenated, see
|
|
193
|
+
* `encoder`), a snapshot, reset or error replaces it;
|
|
194
|
+
* - a hold's unit has one slot, built lazily from the current values when it is sent, so a newer unit merges into it;
|
|
195
|
+
* while it waits, the members it introduces have no stream yet.
|
|
196
|
+
*
|
|
197
|
+
* The first unit of a hold that is not a re-hold (`again`) is sent at once, past the window: a `get` waits for it, and
|
|
198
|
+
* must not wait behind other holds' state. Results, shapes, gone, reload control and pings are sent at once too: they
|
|
199
|
+
* are bounded by the requests the peer makes. Hence the one ordering exception of a link: while state waits, a result
|
|
200
|
+
* can arrive before state changes made before it. Frames of one stream stay in order.
|
|
201
|
+
*
|
|
202
|
+
* Waiting ops are capped per link (`pendingCap`): beyond, the oldest streams are replaced by their current value (a
|
|
203
|
+
* reset built at flush), or, on a relay, by a reset asked of the provider (`resync`), so memory stays bounded however
|
|
204
|
+
* long the peer stalls. The window bounds what is unacknowledged when a frame is sent, so one frame (a large snapshot)
|
|
205
|
+
* may exceed it. Both hellos must announce `acks`; with an older peer nothing is acknowledged and nothing waits. Links
|
|
206
|
+
* that do not report sizes (IPC) never fill the window.
|
|
207
|
+
*/
|
|
101
208
|
export declare class Connection implements RemoteChannel {
|
|
102
209
|
#private;
|
|
103
210
|
readonly host: HostImpl;
|
|
@@ -105,53 +212,59 @@ export declare class Connection implements RemoteChannel {
|
|
|
105
212
|
/** `upstream`: the link to this host's parent. `downstream`: a link this host accepted. */
|
|
106
213
|
readonly role: "upstream" | "downstream";
|
|
107
214
|
readonly peer: Peer;
|
|
108
|
-
|
|
109
|
-
readonly
|
|
215
|
+
/** A client connection's opaque id: holds that come over it (directly or relayed) carry it as their `origin`. */
|
|
216
|
+
readonly originId: string | undefined;
|
|
217
|
+
principal: unknown;
|
|
218
|
+
readonly restamp: ((principal: unknown) => unknown | undefined) | undefined;
|
|
110
219
|
/** Bumped when the stamped principal is revoked or re-stamped: relays from before are refused. */
|
|
111
220
|
generation: number;
|
|
112
|
-
/** The
|
|
113
|
-
|
|
114
|
-
/** A downstream host peer is usable once it runs this host's current
|
|
221
|
+
/** The code version a downstream host peer runs (from its hello, then from commits). */
|
|
222
|
+
peerCodeVersion: number | undefined;
|
|
223
|
+
/** A downstream host peer is usable once it runs this host's current code version (see `HostImpl.peerHello`). */
|
|
115
224
|
synced: boolean;
|
|
116
|
-
/**
|
|
117
|
-
readonly
|
|
118
|
-
readonly remotes: Map<number,
|
|
225
|
+
/** Code versions this child announced while behind this host (fenced): this host never ran them. */
|
|
226
|
+
readonly fencedCodeVersions: Set<number>;
|
|
227
|
+
readonly remotes: Map<number, RemoteBinding>;
|
|
119
228
|
readonly calls: Map<number, Pending>;
|
|
120
|
-
readonly subs: Map<number, (frame: StateSink) => void>;
|
|
121
229
|
readonly served: Map<number, Served>;
|
|
122
230
|
readonly incoming: Map<number, AbortController>;
|
|
123
|
-
readonly
|
|
124
|
-
/** Origin side of relays: the peer's sub ids that are relayed. */
|
|
125
|
-
readonly relaySubs: Map<number, RelayHold>;
|
|
126
|
-
readonly relayedHolds: Map<number, RelayHold>;
|
|
231
|
+
readonly relayedRefs: Map<number, RelayRef>;
|
|
127
232
|
readonly relayedCalls: Map<number, RelayCall>;
|
|
128
|
-
readonly relayedSubs: Map<number, {
|
|
129
|
-
readonly relay: RelayHold;
|
|
130
|
-
readonly originSubId: number;
|
|
131
|
-
}>;
|
|
132
233
|
/** Reload votes awaited from this (child) peer. */
|
|
133
234
|
readonly votes: Map<number, Pending>;
|
|
134
235
|
constructor(host: HostImpl, link: Link, config: {
|
|
135
236
|
readonly role: "upstream" | "downstream";
|
|
136
|
-
readonly principal:
|
|
237
|
+
readonly principal: unknown;
|
|
137
238
|
readonly peer: Peer;
|
|
138
|
-
readonly
|
|
239
|
+
readonly originId?: string;
|
|
240
|
+
readonly restamp?: (principal: unknown) => unknown | undefined;
|
|
241
|
+
readonly maxCalls?: number;
|
|
139
242
|
} & LinkOptions);
|
|
140
243
|
get open(): boolean;
|
|
141
244
|
/** The peer's name: configured for downstream hosts, announced by the parent upstream. */
|
|
142
245
|
get name(): string | undefined;
|
|
143
246
|
send(frame: Frame): void;
|
|
247
|
+
/** Sends a state frame, or keeps it waiting (see the class comment). */
|
|
248
|
+
sendState(frame: StateFrame, overflow: Overflow): void;
|
|
249
|
+
/** Forgets what waits for a stream that ended. */
|
|
250
|
+
dropState(hold: number, m: string): void;
|
|
251
|
+
/** Forgets everything that waits for a hold (released, closed, or its state stopped). */
|
|
252
|
+
dropHoldState(hold: number): void;
|
|
253
|
+
/** What waits for the peer: slots and bytes of ops (for tests and diagnostics). */
|
|
254
|
+
get waiting(): {
|
|
255
|
+
readonly slots: number;
|
|
256
|
+
readonly bytes: number;
|
|
257
|
+
};
|
|
144
258
|
nextId(): number;
|
|
145
|
-
/** Resolves once the peer is usable (a host peer runs this host's
|
|
259
|
+
/** Resolves once the peer is usable (a host peer runs this host's code version); rejects if the link closes first. */
|
|
146
260
|
whenSynced(): Promise<void>;
|
|
147
|
-
markSynced(
|
|
261
|
+
markSynced(codeVersion: number): void;
|
|
148
262
|
/** Revokes the stamped principal: relays made under it close at once (also those still resolving a route). */
|
|
149
|
-
revoke(next:
|
|
150
|
-
hold(
|
|
263
|
+
revoke(next: unknown | undefined): void;
|
|
264
|
+
hold(binding: RemoteBinding, again: boolean): number;
|
|
151
265
|
release(id: number): void;
|
|
266
|
+
wantState(hold: number, wanted: boolean): void;
|
|
152
267
|
call(hold: number, member: string, args: unknown, root: RootRef | undefined, signal: AbortSignal): Promise<unknown>;
|
|
153
|
-
sub(hold: number, member: string, sink: (frame: StateSink) => void): number;
|
|
154
|
-
unsub(id: number): void;
|
|
155
268
|
}
|
|
156
269
|
export {};
|
|
157
270
|
//# sourceMappingURL=connection.d.ts.map
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
|