cloving 1.4.0 → 1.5.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 +100 -15
- package/dist/src/broker/actor/authority-health.d.ts +9 -0
- package/dist/src/broker/actor/authority-health.js +24 -0
- package/dist/src/broker/actor/authority-runtime.d.ts +4 -2
- package/dist/src/broker/actor/authority-runtime.js +29 -20
- package/dist/src/broker/actor/authority-session.d.ts +6 -19
- package/dist/src/broker/actor/authority-session.js +66 -88
- package/dist/src/broker/actor/authority-settings.d.ts +2 -1
- package/dist/src/broker/actor/authority-settings.js +4 -1
- package/dist/src/broker/actor/authority-upgrades.d.ts +5 -1
- package/dist/src/broker/actor/authority-upgrades.js +2 -1
- package/dist/src/broker/actor/authority-watchdog.d.ts +6 -2
- package/dist/src/broker/actor/authority-watchdog.js +31 -9
- package/dist/src/broker/actor/authority.js +3 -1
- package/dist/src/broker/actor/projection-memory.d.ts +43 -0
- package/dist/src/broker/actor/projection-memory.js +207 -0
- package/dist/src/broker/actor/runtime-parts.d.ts +5 -0
- package/dist/src/broker/actor/runtime-parts.js +8 -3
- package/dist/src/broker/authority-restore.js +1 -1
- package/dist/src/broker/effect-upgrade.d.ts +1 -0
- package/dist/src/broker/effect-upgrade.js +4 -1
- package/dist/src/cli/arguments.js +1 -0
- package/dist/src/cli/client.d.ts +3 -1
- package/dist/src/cli/client.js +4 -4
- package/dist/src/cli/command-requests.d.ts +30 -1
- package/dist/src/cli/command-requests.js +43 -2
- package/dist/src/cli/commands.js +57 -48
- package/dist/src/cli/config-notice.js +5 -2
- package/dist/src/cli/doctor-retention.d.ts +2 -0
- package/dist/src/cli/doctor-retention.js +93 -0
- package/dist/src/cli/doctor.js +5 -0
- package/dist/src/cli/init-prompt.js +1 -1
- package/dist/src/cli/mcp-server.js +4 -4
- package/dist/src/cli/mcp-tools.d.ts +14 -14
- package/dist/src/cli/mcp-tools.js +212 -50
- package/dist/src/cli/queue-arguments.d.ts +15 -0
- package/dist/src/cli/queue-arguments.js +31 -0
- package/dist/src/cli/queue-render.d.ts +3 -0
- package/dist/src/cli/queue-render.js +26 -0
- package/dist/src/cli/reload-render.d.ts +2 -0
- package/dist/src/cli/reload-render.js +12 -0
- package/dist/src/cli/render.js +37 -8
- package/dist/src/cli/report-github-reader.js +1 -1
- package/dist/src/cli/runtime-arguments.d.ts +17 -3
- package/dist/src/cli/runtime-arguments.js +23 -19
- package/dist/src/cli/toolchain.d.ts +1 -0
- package/dist/src/cli/toolchain.js +1 -0
- package/dist/src/cli/usage.d.ts +2 -2
- package/dist/src/cli/usage.js +40 -5
- package/dist/src/cli/watch-api-document.d.ts +430 -0
- package/dist/src/cli/watch-api-document.js +291 -0
- package/dist/src/cli/watch-api-run.d.ts +56 -0
- package/dist/src/cli/watch-api-run.js +112 -0
- package/dist/src/cli/watch-api.d.ts +17 -0
- package/dist/src/cli/watch-api.js +230 -0
- package/dist/src/cli/watch-header-view.d.ts +1 -0
- package/dist/src/cli/watch-header-view.js +9 -1
- package/dist/src/cli/watch-model.d.ts +1 -0
- package/dist/src/cli/watch-model.js +3 -0
- package/dist/src/cli/watch-page.js +3 -1
- package/dist/src/cli/watch-reads.d.ts +1 -0
- package/dist/src/cli/watch-reads.js +6 -5
- package/dist/src/cli/watch-render.d.ts +1 -0
- package/dist/src/cli/watch-server.js +18 -4
- package/dist/src/cli.js +50 -43
- package/dist/src/configuration-display.js +2 -0
- package/dist/src/configuration-schema.d.ts +5 -0
- package/dist/src/daemon/claude-recovery.js +13 -9
- package/dist/src/daemon/claude-worker.d.ts +3 -3
- package/dist/src/daemon/claude-worker.js +10 -11
- package/dist/src/daemon/daemon-adapters.d.ts +7 -0
- package/dist/src/daemon/daemon-adapters.js +7 -3
- package/dist/src/daemon/daemon-loops.d.ts +2 -0
- package/dist/src/daemon/daemon-loops.js +17 -11
- package/dist/src/daemon/daemon.js +71 -60
- package/dist/src/daemon/github-attach.js +77 -4
- package/dist/src/daemon/github-calls.js +1 -1
- package/dist/src/daemon/github-methods.js +6 -2
- package/dist/src/daemon/issue-queue.d.ts +3 -0
- package/dist/src/daemon/issue-queue.js +66 -73
- package/dist/src/daemon/merge-turns.d.ts +10 -0
- package/dist/src/daemon/merge-turns.js +37 -0
- package/dist/src/daemon/queue-control.d.ts +13 -0
- package/dist/src/daemon/queue-control.js +86 -0
- package/dist/src/daemon/queue-journal.d.ts +20 -0
- package/dist/src/daemon/queue-journal.js +89 -0
- package/dist/src/daemon/record-archive.d.ts +19 -0
- package/dist/src/daemon/record-archive.js +82 -0
- package/dist/src/daemon/record-sweep.d.ts +24 -0
- package/dist/src/daemon/record-sweep.js +147 -0
- package/dist/src/daemon/retrying-turn.js +4 -4
- package/dist/src/daemon/settings-reload.d.ts +26 -0
- package/dist/src/daemon/settings-reload.js +82 -0
- package/dist/src/daemon/turn-details.d.ts +4 -0
- package/dist/src/daemon/turn-details.js +33 -0
- package/dist/src/daemon/worker-profile.d.ts +1 -0
- package/dist/src/daemon/worker-profile.js +4 -0
- package/dist/src/daemon/workflow-generations.d.ts +7 -4
- package/dist/src/daemon/workflow-generations.js +22 -14
- package/dist/src/daemon/workflow-reload.d.ts +41 -0
- package/dist/src/daemon/workflow-reload.js +244 -0
- package/dist/src/daemon/workflow-replay.d.ts +2 -1
- package/dist/src/daemon/workflow-replay.js +7 -3
- package/dist/src/doctor-report.d.ts +2 -2
- package/dist/src/doctor-report.js +2 -0
- package/dist/src/private-config.schema.json +31 -0
- package/dist/src/project-settings.d.ts +15 -1
- package/dist/src/project-settings.js +33 -1
- package/dist/src/queue-state.d.ts +26 -0
- package/dist/src/queue-state.js +34 -0
- package/dist/src/record-retention.d.ts +46 -0
- package/dist/src/record-retention.js +81 -0
- package/dist/src/service/active-tickets.js +5 -0
- package/dist/src/service/dispatcher.d.ts +4 -0
- package/dist/src/service/dispatcher.js +33 -0
- package/dist/src/service/queue-handlers.d.ts +4 -0
- package/dist/src/service/queue-handlers.js +32 -0
- package/dist/src/service/registry.d.ts +28 -2
- package/dist/src/service/registry.js +48 -0
- package/dist/src/service/replies.d.ts +16 -9
- package/dist/src/service/replies.js +5 -2
- package/dist/src/service/run-handlers.js +15 -10
- package/dist/src/service/service.d.ts +7 -3
- package/dist/src/service/service.js +16 -31
- package/dist/src/service/worker-totals.d.ts +4 -0
- package/dist/src/service/worker-totals.js +7 -0
- package/dist/src/service/workflow-reload.d.ts +35 -0
- package/dist/src/service/workflow-reload.js +52 -0
- package/dist/src/storage/decision-snapshots.d.ts +1 -2
- package/dist/src/storage/decision-snapshots.js +1 -7
- package/dist/src/storage/domain-changes.d.ts +1 -1
- package/dist/src/storage/domain-changes.js +1 -0
- package/dist/src/storage/domain-schema.d.ts +4 -1
- package/dist/src/storage/domain-schema.js +6 -2
- package/dist/src/storage/effect-schema.d.ts +1 -0
- package/dist/src/storage/effect-schema.js +4 -0
- package/dist/src/storage/projection-changes.d.ts +12 -0
- package/dist/src/storage/projection-changes.js +194 -0
- package/dist/src/storage/projection-lookups.d.ts +7 -0
- package/dist/src/storage/projection-lookups.js +30 -0
- package/dist/src/workflow-declaration.d.ts +1 -0
- package/dist/src/workflow-declaration.js +3 -0
- package/dist/tools/compile-workflows.js +3 -0
- package/dist/tools/project-configuration.d.ts +7 -2
- package/dist/tools/project-configuration.js +30 -1
- package/dist/tools/self-host-processes.js +37 -13
- package/dist/tools/self-host-project.d.ts +2 -0
- package/dist/tools/self-host-project.js +21 -3
- package/dist/tools/self-host.js +34 -24
- package/dist/tools/workflow-candidate-program.d.ts +4 -2
- package/dist/tools/workflow-candidate-program.js +30 -11
- package/dist/tools/workflow-candidate-source.d.ts +1 -0
- package/dist/tools/workflow-candidate-source.js +11 -3
- package/package.json +2 -2
- package/templates/develop/README.md +81 -4
- package/templates/develop/develop/flow-tests.ts +1 -1
- package/templates/develop/develop/interruption-tests.ts +157 -0
- package/templates/develop/develop/local.ts +1 -0
- package/templates/develop/develop/option-tests.ts +138 -9
- package/templates/develop/develop/red-green.ts +55 -0
- package/templates/develop/develop/report.ts +31 -11
- package/templates/develop/develop/round.ts +1 -0
- package/templates/develop/develop/scenarios.ts +27 -0
- package/templates/develop/develop/turn.ts +26 -6
- package/templates/develop/develop.declaration.json +1 -1
- package/templates/develop/develop.test.ts +4 -0
- package/templates/develop/template.json +29 -2
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import { eligibleIntents, } from "../../authority/reducer.js";
|
|
2
|
-
import { loadOperationSnapshot, loadProjections, } from "../../storage/decision-snapshots.js";
|
|
3
1
|
import { loadEvents } from "../../storage/domain-rows.js";
|
|
4
2
|
import { pinnedResumption } from "../authority-admission.js";
|
|
5
|
-
import {
|
|
3
|
+
import { storedDefinitions, storedEvents, } from "../authority-restore.js";
|
|
6
4
|
import { checkedRequest } from "./authority-actor.js";
|
|
7
5
|
import { subscriptions, } from "./authority-changes.js";
|
|
8
6
|
import { authorityQueries, guardedRead, } from "./authority-queries.js";
|
|
9
7
|
import { effectQueries } from "./effect-queries.js";
|
|
10
8
|
import { backupInto } from "../../storage/domain-schema.js";
|
|
11
9
|
import { observeSettings, } from "./authority-connection.js";
|
|
10
|
+
import { authorityHealth } from "./authority-health.js";
|
|
12
11
|
import { failed, serializable, storeReceipt, invalidCommand, oversized, tooLarge, unserializable, } from "./authority-receipts.js";
|
|
12
|
+
export { operationRecords } from "./projection-memory.js";
|
|
13
13
|
export function authoritySession(parts) {
|
|
14
14
|
const session = {
|
|
15
15
|
parts,
|
|
16
16
|
stopping: new AbortController(),
|
|
17
17
|
delivering: false,
|
|
18
18
|
failure: null,
|
|
19
|
-
|
|
20
|
-
describing: null,
|
|
19
|
+
health: authorityHealth(parts),
|
|
21
20
|
closed: false,
|
|
22
21
|
running: Promise.resolve(),
|
|
23
22
|
invocations: new Set(),
|
|
@@ -33,18 +32,18 @@ export function authoritySession(parts) {
|
|
|
33
32
|
});
|
|
34
33
|
return {
|
|
35
34
|
execute: (command, context) => executeCommand(session, { command, context }),
|
|
36
|
-
state: async () =>
|
|
35
|
+
state: async () => (await projected(session)).state(),
|
|
37
36
|
events: async () => {
|
|
38
37
|
await refreshed(session);
|
|
39
38
|
available(session);
|
|
40
39
|
return storedEvents(await parts.reader.connection(loadEvents));
|
|
41
40
|
},
|
|
42
|
-
eligibleIntents: async () =>
|
|
43
|
-
operations: async () =>
|
|
41
|
+
eligibleIntents: async () => (await projected(session)).eligible(),
|
|
42
|
+
operations: async () => (await projected(session)).operations(),
|
|
44
43
|
resumePinned: async (runId, manifest) => {
|
|
45
|
-
const
|
|
46
|
-
return pinnedResumption(
|
|
47
|
-
definitions: storedDefinitions(snapshot),
|
|
44
|
+
const current = await projected(session);
|
|
45
|
+
return pinnedResumption(current.state(), {
|
|
46
|
+
definitions: storedDefinitions(current.snapshot),
|
|
48
47
|
runId,
|
|
49
48
|
available: manifest,
|
|
50
49
|
});
|
|
@@ -56,7 +55,11 @@ export function authoritySession(parts) {
|
|
|
56
55
|
},
|
|
57
56
|
epoch: parts.epoch,
|
|
58
57
|
recoveryLimit: parts.recoveryLimit,
|
|
59
|
-
queries:
|
|
58
|
+
queries: {
|
|
59
|
+
...authorityQueries(read),
|
|
60
|
+
sequences: async () => (await projected(session)).sequences(),
|
|
61
|
+
workflowIds: async (runIds) => (await projected(session)).workflowIds(runIds),
|
|
62
|
+
},
|
|
60
63
|
effects: effectQueries(read, { stopLimit: parts.stopLimit }),
|
|
61
64
|
deliver: (signal) => deliver(session, signal),
|
|
62
65
|
changes: { subscribe: session.changes.add },
|
|
@@ -82,29 +85,16 @@ async function stopped(session) {
|
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
async function refreshed(session) {
|
|
85
|
-
if (session.
|
|
88
|
+
if (session.failure !== null || session.closed)
|
|
86
89
|
return;
|
|
87
|
-
await
|
|
90
|
+
await session.health.refresh();
|
|
88
91
|
}
|
|
89
|
-
|
|
92
|
+
function failClosed(session, failure) {
|
|
90
93
|
session.failure ??= failure;
|
|
91
|
-
|
|
92
|
-
close(session).catch(() => undefined);
|
|
93
|
-
}
|
|
94
|
-
async function conditionRead(session) {
|
|
95
|
-
if (session.condition !== null && session.describing === null)
|
|
96
|
-
return;
|
|
97
|
-
session.describing ??= described(session).finally(() => {
|
|
98
|
-
session.describing = null;
|
|
99
|
-
});
|
|
100
|
-
await session.describing;
|
|
94
|
+
close(session).catch(ignored);
|
|
101
95
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (detected === undefined)
|
|
105
|
-
return;
|
|
106
|
-
session.condition = detected;
|
|
107
|
-
session.condition = (await session.parts.condition()) ?? detected;
|
|
96
|
+
function ignored() {
|
|
97
|
+
return undefined;
|
|
108
98
|
}
|
|
109
99
|
async function backup(session, target) {
|
|
110
100
|
await refreshed(session);
|
|
@@ -116,36 +106,43 @@ async function backup(session, target) {
|
|
|
116
106
|
throw new Error(`backup-failed: ${String(Reflect.get(Object(error), "message"))}`, { cause: error });
|
|
117
107
|
}
|
|
118
108
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
throw new Error(`authority-unavailable: ${failure}`);
|
|
109
|
+
function opened(session) {
|
|
110
|
+
if (session.failure !== null)
|
|
111
|
+
throw new Error(`authority-unavailable: ${session.failure}`);
|
|
123
112
|
if (session.closed)
|
|
124
113
|
throw new Error("authority-unavailable: closed");
|
|
125
114
|
}
|
|
115
|
+
function available(session) {
|
|
116
|
+
opened(session);
|
|
117
|
+
const condition = session.health.condition();
|
|
118
|
+
if (condition !== null)
|
|
119
|
+
throw new Error(`authority-unavailable: ${condition}`);
|
|
120
|
+
}
|
|
126
121
|
function unavailableReceipt(session) {
|
|
127
|
-
|
|
128
|
-
if (failure !== null)
|
|
122
|
+
if (session.failure !== null)
|
|
129
123
|
return {
|
|
130
124
|
kind: "failed",
|
|
131
125
|
reason: "storage-unavailable",
|
|
132
|
-
detail: `closed after ${failure}`,
|
|
126
|
+
detail: `closed after ${session.failure}`,
|
|
133
127
|
};
|
|
134
128
|
if (session.closed)
|
|
135
129
|
return { kind: "failed", reason: "storage-unavailable", detail: "closed" };
|
|
136
|
-
|
|
130
|
+
const condition = session.health.condition();
|
|
131
|
+
return condition === null
|
|
132
|
+
? undefined
|
|
133
|
+
: { kind: "failed", reason: "authority-unavailable", detail: condition };
|
|
137
134
|
}
|
|
138
135
|
async function projected(session) {
|
|
139
136
|
await refreshed(session);
|
|
140
137
|
available(session);
|
|
141
|
-
return session.parts.
|
|
138
|
+
return session.parts.memory.read(session.parts.database);
|
|
142
139
|
}
|
|
143
140
|
async function deliver(session, signal) {
|
|
144
141
|
if (!session.parts.deliveryConfigured)
|
|
145
142
|
throw new Error("delivery-unconfigured");
|
|
146
143
|
if (session.delivering)
|
|
147
144
|
throw new Error("delivery-already-running");
|
|
148
|
-
|
|
145
|
+
opened(session);
|
|
149
146
|
session.delivering = true;
|
|
150
147
|
try {
|
|
151
148
|
await delivered(session, signal);
|
|
@@ -154,21 +151,19 @@ async function deliver(session, signal) {
|
|
|
154
151
|
session.delivering = false;
|
|
155
152
|
}
|
|
156
153
|
await refreshed(session);
|
|
157
|
-
const failure = session.
|
|
154
|
+
const failure = session.failure ?? session.health.condition();
|
|
158
155
|
if (failure !== null)
|
|
159
156
|
throw new Error(`authority-unavailable: ${failure}`);
|
|
160
157
|
}
|
|
161
158
|
async function delivered(session, signal) {
|
|
162
159
|
if (!(await session.parts.handlersSettled()))
|
|
163
160
|
throw new Error("delivery-handler-in-flight");
|
|
164
|
-
|
|
165
|
-
const watch = new AbortController();
|
|
161
|
+
opened(session);
|
|
166
162
|
const timer = setInterval(() => {
|
|
167
|
-
|
|
168
|
-
watch.abort();
|
|
163
|
+
refreshed(session).catch(ignored);
|
|
169
164
|
}, session.parts.watchMilliseconds);
|
|
170
165
|
try {
|
|
171
|
-
session.running = session.parts.run(AbortSignal.any([signal, session.stopping.signal
|
|
166
|
+
session.running = session.parts.run(AbortSignal.any([signal, session.stopping.signal]));
|
|
172
167
|
await session.running;
|
|
173
168
|
}
|
|
174
169
|
finally {
|
|
@@ -177,8 +172,7 @@ async function delivered(session, signal) {
|
|
|
177
172
|
}
|
|
178
173
|
async function executeCommand(session, input) {
|
|
179
174
|
const { command, context } = input;
|
|
180
|
-
await
|
|
181
|
-
const unavailable = unavailableReceipt(session);
|
|
175
|
+
const unavailable = await refusal(session);
|
|
182
176
|
if (unavailable !== undefined)
|
|
183
177
|
return unavailable;
|
|
184
178
|
const given = serializable({
|
|
@@ -196,15 +190,32 @@ async function executeCommand(session, input) {
|
|
|
196
190
|
if (oversized(request))
|
|
197
191
|
return tooLarge;
|
|
198
192
|
try {
|
|
199
|
-
return
|
|
193
|
+
return storeReceipt(await invoked(session, request));
|
|
200
194
|
}
|
|
201
195
|
catch (error) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
return failedReceipt(session, error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function refusal(session) {
|
|
200
|
+
try {
|
|
201
|
+
await refreshed(session);
|
|
207
202
|
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
return failed(error);
|
|
205
|
+
}
|
|
206
|
+
return unavailableReceipt(session);
|
|
207
|
+
}
|
|
208
|
+
function failedReceipt(session, error) {
|
|
209
|
+
const receipt = failed(error);
|
|
210
|
+
if (timedOut(error))
|
|
211
|
+
return receipt;
|
|
212
|
+
failClosed(session, receipt.reason === "storage-failure"
|
|
213
|
+
? "storage failure"
|
|
214
|
+
: "authority failure");
|
|
215
|
+
return receipt;
|
|
216
|
+
}
|
|
217
|
+
function timedOut(error) {
|
|
218
|
+
return error instanceof Error && error.name === "SyncTimeout";
|
|
208
219
|
}
|
|
209
220
|
async function invoked(session, request) {
|
|
210
221
|
const invocation = session.parts.execute(request);
|
|
@@ -216,36 +227,3 @@ async function invoked(session, request) {
|
|
|
216
227
|
session.invocations.delete(invocation);
|
|
217
228
|
}
|
|
218
229
|
}
|
|
219
|
-
async function healthyReceipt(session, value) {
|
|
220
|
-
const receipt = storeReceipt(value);
|
|
221
|
-
await refreshed(session);
|
|
222
|
-
return receipt;
|
|
223
|
-
}
|
|
224
|
-
export function operationRecords(snapshot) {
|
|
225
|
-
const deliveries = Map.groupBy(snapshot.deliveries, (row) => row.operationId);
|
|
226
|
-
return snapshot.outbox.map((row) => ({
|
|
227
|
-
operationId: row.operationId,
|
|
228
|
-
kind: intentKind(row.kind),
|
|
229
|
-
runId: row.runId,
|
|
230
|
-
createdSequence: row.createdSequence,
|
|
231
|
-
releasedSequence: row.releasedSequence,
|
|
232
|
-
deliveries: (deliveries.get(row.operationId) ?? []).map(({ effectId, outcome, receipt, purpose, epoch }) => ({
|
|
233
|
-
effectId,
|
|
234
|
-
outcome,
|
|
235
|
-
receipt,
|
|
236
|
-
purpose,
|
|
237
|
-
epoch,
|
|
238
|
-
})),
|
|
239
|
-
}));
|
|
240
|
-
}
|
|
241
|
-
function eligible(snapshot) {
|
|
242
|
-
return eligibleIntents({
|
|
243
|
-
...currentState(snapshot),
|
|
244
|
-
effects: {
|
|
245
|
-
operations: Object.fromEntries(snapshot.pending.map((operationId) => [
|
|
246
|
-
operationId,
|
|
247
|
-
{ status: "dispatch-pending" },
|
|
248
|
-
])),
|
|
249
|
-
},
|
|
250
|
-
});
|
|
251
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SolidObjectsConfiguration } from "solid-objects";
|
|
1
|
+
import type { Logger, SolidObjectsConfiguration } from "solid-objects";
|
|
2
2
|
export interface AuthorityTiming {
|
|
3
3
|
readonly leaseMilliseconds: number;
|
|
4
4
|
readonly renewalMilliseconds: number;
|
|
@@ -30,4 +30,5 @@ export declare function effectConfiguration(options: {
|
|
|
30
30
|
readonly stop?: unknown;
|
|
31
31
|
};
|
|
32
32
|
}): PlanSettings;
|
|
33
|
+
export declare function authorityLogger(log: ((line: string) => void) | undefined): Logger;
|
|
33
34
|
export declare function librarySettings(timing: AuthorityTiming, log: ((line: string) => void) | undefined): Partial<SolidObjectsConfiguration>;
|
|
@@ -47,6 +47,9 @@ export function effectConfiguration(options) {
|
|
|
47
47
|
function standardError(line) {
|
|
48
48
|
process.stderr.write(line);
|
|
49
49
|
}
|
|
50
|
+
export function authorityLogger(log) {
|
|
51
|
+
return lineLogger(log ?? standardError);
|
|
52
|
+
}
|
|
50
53
|
export function librarySettings(timing, log) {
|
|
51
54
|
return {
|
|
52
55
|
tableNamePrefix: "solid_",
|
|
@@ -54,7 +57,7 @@ export function librarySettings(timing, log) {
|
|
|
54
57
|
maxActivationDurationMilliseconds: 50,
|
|
55
58
|
maxPayloadBytes: 900_000 + commandLimits.stdoutBytes * 12,
|
|
56
59
|
maxResultBytes: 900_000 + commandLimits.stdoutBytes * 12,
|
|
57
|
-
logger:
|
|
60
|
+
logger: authorityLogger(log),
|
|
58
61
|
leaseDurationMilliseconds: timing.leaseMilliseconds,
|
|
59
62
|
leaseRenewalIntervalMilliseconds: timing.renewalMilliseconds,
|
|
60
63
|
processHeartbeatIntervalMilliseconds: timing.heartbeatMilliseconds,
|
|
@@ -2,7 +2,7 @@ import { upgradeCommandWaits, upgradeCommandWaitChecks } from "../command-wait-r
|
|
|
2
2
|
import { loadDecided, loadEffected, loadWaiting, loadCurrent } from "../../storage/decision-snapshots.ts";
|
|
3
3
|
import { loadSnapshot } from "../../storage/domain-rows.ts";
|
|
4
4
|
import { upgradeDecisions } from "../decision-upgrade.ts";
|
|
5
|
-
import { upgradeEffects, upgradeEventIndexes, upgradeSpendingIndexes } from "../effect-upgrade.ts";
|
|
5
|
+
import { upgradeEffects, upgradeEventIndexes, upgradeRunIndexes, upgradeSpendingIndexes } from "../effect-upgrade.ts";
|
|
6
6
|
declare const upgrades: readonly [{
|
|
7
7
|
readonly from: "adopted";
|
|
8
8
|
readonly load: typeof loadSnapshot;
|
|
@@ -27,6 +27,10 @@ declare const upgrades: readonly [{
|
|
|
27
27
|
readonly from: "indexed";
|
|
28
28
|
readonly load: typeof loadCurrent;
|
|
29
29
|
readonly apply: typeof upgradeEventIndexes;
|
|
30
|
+
}, {
|
|
31
|
+
readonly from: "event-indexed";
|
|
32
|
+
readonly load: typeof loadCurrent;
|
|
33
|
+
readonly apply: typeof upgradeRunIndexes;
|
|
30
34
|
}];
|
|
31
35
|
export type UpgradeStep = (typeof upgrades)[number];
|
|
32
36
|
export declare function upgradeSteps(kind: string): readonly UpgradeStep[];
|
|
@@ -2,7 +2,7 @@ import { upgradeCommandWaits, upgradeCommandWaitChecks, } from "../command-wait-
|
|
|
2
2
|
import { loadDecided, loadEffected, loadWaiting, loadCurrent, } from "../../storage/decision-snapshots.js";
|
|
3
3
|
import { loadSnapshot } from "../../storage/domain-rows.js";
|
|
4
4
|
import { upgradeDecisions } from "../decision-upgrade.js";
|
|
5
|
-
import { upgradeEffects, upgradeEventIndexes, upgradeSpendingIndexes, } from "../effect-upgrade.js";
|
|
5
|
+
import { upgradeEffects, upgradeEventIndexes, upgradeRunIndexes, upgradeSpendingIndexes, } from "../effect-upgrade.js";
|
|
6
6
|
const upgrades = [
|
|
7
7
|
{ from: "adopted", load: loadSnapshot, apply: upgradeDecisions },
|
|
8
8
|
{ from: "decided", load: loadDecided, apply: upgradeEffects },
|
|
@@ -10,6 +10,7 @@ const upgrades = [
|
|
|
10
10
|
{ from: "waiting", load: loadWaiting, apply: upgradeCommandWaitChecks },
|
|
11
11
|
{ from: "checked", load: loadCurrent, apply: upgradeSpendingIndexes },
|
|
12
12
|
{ from: "indexed", load: loadCurrent, apply: upgradeEventIndexes },
|
|
13
|
+
{ from: "event-indexed", load: loadCurrent, apply: upgradeRunIndexes },
|
|
13
14
|
];
|
|
14
15
|
export function upgradeSteps(kind) {
|
|
15
16
|
const first = upgrades.findIndex((step) => step.from === kind);
|
|
@@ -3,10 +3,14 @@ export interface WatchedEvent {
|
|
|
3
3
|
readonly messageId: string | null;
|
|
4
4
|
readonly attributes: Readonly<Record<string, unknown>>;
|
|
5
5
|
}
|
|
6
|
+
export interface WatchReport {
|
|
7
|
+
readonly kind: "stranded" | "dead-lettered";
|
|
8
|
+
readonly messageId: string;
|
|
9
|
+
}
|
|
10
|
+
export type MessageRuns = (messageIds: readonly string[]) => Promise<ReadonlyMap<string, string>>;
|
|
6
11
|
export interface StrandingWatchdog {
|
|
7
12
|
readonly observe: (event: WatchedEvent) => void;
|
|
8
|
-
readonly
|
|
9
|
-
readonly deadLettered: () => string | undefined;
|
|
13
|
+
readonly report: (runs: MessageRuns) => Promise<WatchReport | undefined>;
|
|
10
14
|
}
|
|
11
15
|
export declare function strandingWatchdog(bound: {
|
|
12
16
|
readonly boundMilliseconds: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { terminal } from "../../authority/transitions.js";
|
|
1
2
|
const callbacks = ["deliveryCompleted", "deliveryFailed"];
|
|
2
3
|
const finished = [
|
|
3
4
|
"solid_objects.message.completed",
|
|
@@ -7,33 +8,54 @@ const finished = [
|
|
|
7
8
|
export function strandingWatchdog(bound) {
|
|
8
9
|
const clock = bound.clock ?? monotonic;
|
|
9
10
|
const lost = new Map();
|
|
10
|
-
const dead =
|
|
11
|
-
let reported;
|
|
11
|
+
const dead = new Set();
|
|
12
12
|
return {
|
|
13
13
|
observe: (event) => {
|
|
14
14
|
const { messageId } = event;
|
|
15
15
|
if (messageId === null)
|
|
16
16
|
return;
|
|
17
17
|
if (deadCallback(event))
|
|
18
|
-
dead.
|
|
18
|
+
dead.add(messageId);
|
|
19
19
|
if (event.name === "solid_objects.activation.lost" &&
|
|
20
20
|
!lost.has(messageId))
|
|
21
21
|
lost.set(messageId, clock());
|
|
22
22
|
if (finished.includes(event.name))
|
|
23
23
|
lost.delete(messageId);
|
|
24
24
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
report: async (runs) => {
|
|
26
|
+
const overdue = overdueMessages(lost, clock() - bound.boundMilliseconds);
|
|
27
|
+
const suspects = [...dead, ...overdue];
|
|
28
|
+
if (suspects.length === 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
const statuses = await runs(suspects);
|
|
31
|
+
const released = suspects.filter((messageId) => settled(statuses.get(messageId)));
|
|
32
|
+
for (const messageId of released) {
|
|
33
|
+
lost.delete(messageId);
|
|
34
|
+
dead.delete(messageId);
|
|
35
|
+
}
|
|
36
|
+
return firstReport({ dead: [...dead], stranded: overdue, lost });
|
|
28
37
|
},
|
|
29
|
-
deadLettered: () => dead[0],
|
|
30
38
|
};
|
|
31
39
|
}
|
|
32
40
|
function monotonic() {
|
|
33
41
|
return performance.now();
|
|
34
42
|
}
|
|
35
|
-
function
|
|
36
|
-
return [...lost]
|
|
43
|
+
function overdueMessages(lost, limit) {
|
|
44
|
+
return [...lost]
|
|
45
|
+
.filter(([, at]) => at < limit)
|
|
46
|
+
.map(([messageId]) => messageId);
|
|
47
|
+
}
|
|
48
|
+
function settled(status) {
|
|
49
|
+
return terminal.some((candidate) => candidate === status);
|
|
50
|
+
}
|
|
51
|
+
function firstReport(found) {
|
|
52
|
+
const deadLetter = found.dead[0];
|
|
53
|
+
if (deadLetter !== undefined)
|
|
54
|
+
return { kind: "dead-lettered", messageId: deadLetter };
|
|
55
|
+
const stranded = found.stranded.find((messageId) => found.lost.has(messageId));
|
|
56
|
+
return stranded === undefined
|
|
57
|
+
? undefined
|
|
58
|
+
: { kind: "stranded", messageId: stranded };
|
|
37
59
|
}
|
|
38
60
|
function deadCallback(event) {
|
|
39
61
|
return (event.name === "solid_objects.dead_letter.created" &&
|
|
@@ -76,6 +76,7 @@ async function begun(opening) {
|
|
|
76
76
|
reason: "unresolved-delivery",
|
|
77
77
|
detail: condition,
|
|
78
78
|
};
|
|
79
|
+
await parts.memory.load(owned.database);
|
|
79
80
|
return integer(opened, "epoch");
|
|
80
81
|
}
|
|
81
82
|
async function start(opening) {
|
|
@@ -99,6 +100,7 @@ async function start(opening) {
|
|
|
99
100
|
authority: authoritySession({
|
|
100
101
|
database,
|
|
101
102
|
reader: parts.reader,
|
|
103
|
+
memory: parts.memory,
|
|
102
104
|
execute: (request) => authority.execute(json(request)),
|
|
103
105
|
run: (signal) => parts.runtime.run(signal),
|
|
104
106
|
stop: parts.stop,
|
|
@@ -106,8 +108,8 @@ async function start(opening) {
|
|
|
106
108
|
deliveryConfigured: options.delivery !== undefined,
|
|
107
109
|
epoch,
|
|
108
110
|
...parts.limits,
|
|
109
|
-
unhealthy: parts.unhealthy,
|
|
110
111
|
condition: parts.condition,
|
|
112
|
+
logger: parts.logger,
|
|
111
113
|
watchMilliseconds: parts.watchMilliseconds,
|
|
112
114
|
subscribe: (listener) => parts.subscribe(actorId, listener),
|
|
113
115
|
}),
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Database, Logger } from "solid-objects";
|
|
2
|
+
import { type EffectIntent } from "../../authority/reducer.ts";
|
|
3
|
+
import { type ProjectionSnapshot } from "../../storage/decision-snapshots.ts";
|
|
4
|
+
import type { DomainChange } from "../../storage/domain-changes.ts";
|
|
5
|
+
import { type StoredState } from "../authority-restore.ts";
|
|
6
|
+
import type { Sequences } from "./authority-queries.ts";
|
|
7
|
+
export interface OperationRecord {
|
|
8
|
+
readonly operationId: string;
|
|
9
|
+
readonly kind: EffectIntent["kind"];
|
|
10
|
+
readonly runId: string;
|
|
11
|
+
readonly createdSequence: number;
|
|
12
|
+
readonly releasedSequence: number | null;
|
|
13
|
+
readonly deliveries: readonly {
|
|
14
|
+
readonly effectId: string;
|
|
15
|
+
readonly outcome: string | null;
|
|
16
|
+
readonly receipt: string | null;
|
|
17
|
+
readonly purpose: string;
|
|
18
|
+
readonly epoch: number | null;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
export interface Projection {
|
|
22
|
+
readonly snapshot: ProjectionSnapshot;
|
|
23
|
+
readonly state: () => StoredState;
|
|
24
|
+
readonly eligible: () => readonly EffectIntent[];
|
|
25
|
+
readonly operations: () => readonly OperationRecord[];
|
|
26
|
+
readonly sequences: () => Sequences;
|
|
27
|
+
readonly workflowIds: (runIds?: readonly string[]) => Readonly<Record<string, string>>;
|
|
28
|
+
}
|
|
29
|
+
type Source = Pick<Database, "connection">;
|
|
30
|
+
export interface ProjectionMemory {
|
|
31
|
+
readonly record: (change: RecordedChange) => void;
|
|
32
|
+
readonly load: (database: Source) => Promise<void>;
|
|
33
|
+
readonly read: (database: Source) => Promise<Projection>;
|
|
34
|
+
}
|
|
35
|
+
export interface RecordedChange {
|
|
36
|
+
readonly messageId: string;
|
|
37
|
+
readonly change: DomainChange;
|
|
38
|
+
}
|
|
39
|
+
export declare function projectionMemory(options: {
|
|
40
|
+
readonly logger: Logger;
|
|
41
|
+
}): ProjectionMemory;
|
|
42
|
+
export declare function operationRecords(snapshot: Pick<ProjectionSnapshot, "outbox" | "deliveries">): readonly OperationRecord[];
|
|
43
|
+
export {};
|