@ixo/oracle-runtime-workers 0.13.0 → 0.16.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 +32 -10
- package/package.json +13 -8
- package/src/artifacts/config.ts +158 -0
- package/src/artifacts/crypto.ts +94 -0
- package/src/artifacts/routes.ts +73 -0
- package/src/artifacts/store.ts +238 -0
- package/src/artifacts/sweep.ts +200 -0
- package/src/artifacts/tool.ts +103 -0
- package/src/artifacts/viewer-page.ts +614 -0
- package/src/attachments/download.ts +168 -45
- package/src/attachments/index.ts +9 -0
- package/src/attachments/magic.ts +33 -0
- package/src/attachments/pipeline.ts +105 -49
- package/src/attachments/sandbox-archive.ts +9 -8
- package/src/attachments/view-cache.ts +142 -0
- package/src/attachments/view.ts +53 -12
- package/src/channels/auth.ts +310 -0
- package/src/channels/contract.ts +246 -0
- package/src/channels/turns.ts +280 -0
- package/src/core/budgeted-llm.ts +223 -0
- package/src/core/capability-router.ts +273 -0
- package/src/core/context-budget.ts +97 -10
- package/src/core/env.ts +111 -0
- package/src/core/execution-profile.ts +17 -0
- package/src/core/index.ts +267 -9
- package/src/core/llm.ts +30 -9
- package/src/core/main-agent-types.ts +41 -1
- package/src/core/main-agent.ts +367 -65
- package/src/core/manifest.ts +34 -0
- package/src/core/meta-tools.ts +128 -18
- package/src/core/middlewares/capability-gate.ts +130 -12
- package/src/core/middlewares/context-guard.ts +55 -24
- package/src/core/middlewares/index.ts +20 -2
- package/src/core/middlewares/page-context.ts +46 -2
- package/src/core/middlewares/return-direct-first.ts +47 -0
- package/src/core/middlewares/summarization.ts +152 -10
- package/src/core/middlewares/tool-execution.ts +323 -0
- package/src/core/middlewares/tool-repetition-guard.ts +202 -35
- package/src/core/middlewares/turn-boundary.ts +162 -0
- package/src/core/openrouter-pricing.ts +155 -22
- package/src/core/prompt-composer.ts +112 -7
- package/src/core/registries.ts +253 -48
- package/src/core/request-admission.ts +159 -0
- package/src/core/runtime-context.ts +107 -8
- package/src/core/state.ts +1 -1
- package/src/core/sub-agent-fallback.ts +21 -2
- package/src/core/subagent-as-tool.ts +78 -43
- package/src/core/tool-access.ts +189 -0
- package/src/core/tool-scheduler.ts +125 -0
- package/src/core/turn-budget.ts +205 -0
- package/src/core/turn-time-note.ts +43 -0
- package/src/core/user-kv.ts +446 -0
- package/src/core/wrap-plugin-tool.ts +20 -0
- package/src/delivery/plan.ts +278 -0
- package/src/delivery/profile.ts +161 -0
- package/src/delivery/prompt.ts +27 -0
- package/src/delivery/schema.ts +61 -0
- package/src/delivery/shaper.ts +389 -0
- package/src/delivery/types.ts +84 -0
- package/src/do/ambient.ts +51 -15
- package/src/do/contracts.ts +170 -6
- package/src/do/idle-eviction.ts +38 -0
- package/src/do/matrix-turn-ledger.ts +24 -5
- package/src/do/result-store-test-do.ts +137 -3
- package/src/do/result-store.ts +239 -58
- package/src/do/room-mirror.ts +29 -0
- package/src/do/run-buffer.ts +64 -9
- package/src/do/run-coordinator.ts +451 -76
- package/src/do/run-request.ts +2 -0
- package/src/do/run-store-test-do.ts +301 -1
- package/src/do/run-store.ts +385 -43
- package/src/do/secrets-adapter.ts +22 -3
- package/src/do/sse-stream.ts +60 -9
- package/src/do/transcript.ts +48 -5
- package/src/do/turn-body.ts +105 -5
- package/src/do/ucan-service.ts +204 -34
- package/src/do/user-oracle-do.ts +2003 -274
- package/src/feedback/config.ts +127 -0
- package/src/feedback/contract.ts +148 -0
- package/src/feedback/linear-sink.ts +285 -0
- package/src/feedback/privacy.ts +150 -0
- package/src/feedback/reservation.ts +298 -0
- package/src/feedback/submit.ts +247 -0
- package/src/index.ts +138 -6
- package/src/llm/byo-adapter.ts +27 -6
- package/src/llm/byo-catalog.ts +13 -0
- package/src/llm/byo-model-fallback.ts +248 -0
- package/src/llm/byo-service.ts +45 -8
- package/src/llm/provider-error.ts +139 -2
- package/src/matrix/gateway-do.ts +504 -108
- package/src/matrix/group-chat.ts +66 -23
- package/src/matrix/inbox-store.ts +16 -1
- package/src/matrix/ingest.ts +79 -14
- package/src/matrix/media-deadline.ts +130 -0
- package/src/matrix/replay-format.ts +5 -3
- package/src/matrix/reply-parts.ts +92 -0
- package/src/matrix/room-alias.ts +96 -0
- package/src/matrix/user-homeserver.ts +166 -7
- package/src/owner-store/ixo-vfs-store.ts +170 -43
- package/src/owner-store/migrating-store.ts +31 -15
- package/src/owner-store/types.ts +18 -3
- package/src/plugin-api/index.ts +43 -2
- package/src/plugin-api/oracle-plugin.ts +35 -0
- package/src/plugin-api/request-admission.ts +41 -0
- package/src/plugin-api/tool-helper.ts +25 -1
- package/src/plugin-api/tool-plane.ts +92 -0
- package/src/plugin-api/types.ts +194 -6
- package/src/plugins/agui/agui.plugin.ts +31 -16
- package/src/plugins/composio/composio-tools.ts +57 -6
- package/src/plugins/composio/composio-ucan.ts +3 -2
- package/src/plugins/composio/composio.plugin.ts +2 -0
- package/src/plugins/delegated-capabilities.ts +25 -0
- package/src/plugins/editor/content-session.ts +173 -83
- package/src/plugins/editor/content-tools.ts +76 -17
- package/src/plugins/editor/editor-mx.ts +28 -9
- package/src/plugins/editor/editor.plugin.ts +3 -1
- package/src/plugins/editor/failures.ts +28 -7
- package/src/plugins/editor/prompts.ts +2 -1
- package/src/plugins/editor/provider.ts +553 -40
- package/src/plugins/editor/standalone-editor-tool.ts +61 -18
- package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
- package/src/plugins/flows/action-metadata.ts +111 -11
- package/src/plugins/flows/actions.ts +18 -0
- package/src/plugins/flows/decision-context.ts +31 -0
- package/src/plugins/flows/edit.ts +223 -52
- package/src/plugins/flows/errors.ts +22 -1
- package/src/plugins/flows/flow-doc.ts +92 -9
- package/src/plugins/flows/flows.plugin.ts +11 -5
- package/src/plugins/flows/forms.ts +34 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/input-policy.ts +140 -0
- package/src/plugins/flows/prompts.ts +14 -11
- package/src/plugins/flows/read.ts +49 -9
- package/src/plugins/flows/ref-syntax.ts +129 -0
- package/src/plugins/flows/test-homeserver.ts +245 -0
- package/src/plugins/flows/tools/authoring.ts +216 -112
- package/src/plugins/flows/tools/discovery.ts +4 -1
- package/src/plugins/flows/tools/forms.ts +21 -2
- package/src/plugins/flows/tools/settings.ts +34 -13
- package/src/plugins/flows/translator.ts +13 -10
- package/src/plugins/flows/types.ts +167 -42
- package/src/plugins/index.ts +11 -1
- package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
- package/src/plugins/ixo-transaction/index.ts +2 -0
- package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
- package/src/plugins/ixo-transaction/receipts.ts +99 -0
- package/src/plugins/ixo-transaction/tools.ts +329 -0
- package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
- package/src/plugins/mcp-tool-adapter.ts +23 -2
- package/src/plugins/memory/memory-tools.ts +7 -2
- package/src/plugins/memory/memory-ucan.ts +3 -2
- package/src/plugins/memory/memory.plugin.ts +2 -0
- package/src/plugins/pod-creator/blueprint-store.ts +163 -0
- package/src/plugins/pod-creator/blueprint-types.ts +61 -0
- package/src/plugins/pod-creator/bounded-map.ts +87 -0
- package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
- package/src/plugins/pod-creator/chain-gateway.ts +110 -0
- package/src/plugins/pod-creator/config.ts +85 -0
- package/src/plugins/pod-creator/create-session-store.ts +180 -0
- package/src/plugins/pod-creator/create-tools.ts +538 -0
- package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
- package/src/plugins/pod-creator/index.ts +65 -0
- package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
- package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
- package/src/plugins/pod-creator/stage.ts +201 -0
- package/src/plugins/pod-creator/sub-agents.ts +189 -0
- package/src/plugins/portal/action-log.ts +21 -1
- package/src/plugins/portal/declared-tools.ts +90 -0
- package/src/plugins/portal/portal.plugin.ts +42 -16
- package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
- package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
- package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
- package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
- package/src/plugins/tasks/manifest.ts +1 -1
- package/src/plugins/tasks/middleware.ts +20 -7
- package/src/plugins/tasks/tasks-tools.ts +30 -3
- package/src/plugins/tasks/tasks.plugin.ts +64 -11
- package/src/plugins/user-preferences/schema.ts +12 -1
- package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
- package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
- package/src/plugins/vfs/vfs-client.ts +250 -71
- package/src/plugins/vfs/vfs-content.ts +30 -11
- package/src/plugins/vfs/vfs-errors.ts +22 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
- package/src/plugins/vfs/vfs-tools.ts +60 -20
- package/src/realtime/frontend-call-registry.ts +228 -81
- package/src/realtime/realtime-endpoint.ts +215 -44
- package/src/realtime/session-socket-hub.ts +47 -2
- package/src/secrets/secrets-service.ts +9 -3
- package/src/shell/app.ts +562 -82
- package/src/shell/auth.ts +131 -37
- package/src/shell/turn-body-cap.ts +11 -13
- package/src/sqlite/blob-compactor.ts +61 -28
- package/src/sqlite/database.ts +156 -90
- package/src/sqlite/do-vfs.ts +416 -129
- package/src/sqlite/page-tier.ts +133 -40
- package/src/sqlite/serialization.ts +40 -0
- package/src/sqlite/sessions-store.ts +10 -2
- package/src/sqlite/sqlite-saver.ts +139 -19
- package/src/sqlite/user-kv-store.ts +317 -0
- package/src/tasks/schedule.ts +201 -12
- package/src/tasks/scheduler.ts +586 -98
- package/src/tasks/spec.ts +67 -3
- package/src/tasks/store.ts +292 -27
- package/src/tasks/topic-deliverables.ts +70 -0
package/src/sqlite/database.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
DoVfs,
|
|
28
28
|
DoVfsError,
|
|
29
29
|
isDoVfs,
|
|
30
|
+
type Exclusive,
|
|
30
31
|
VACUUM_FILE_SUFFIX,
|
|
31
32
|
VFS_PAGE_SIZE,
|
|
32
33
|
type DoVfsOptions,
|
|
@@ -91,16 +92,35 @@ export function vfsNameForObject(ctx: DoSqliteContext): string {
|
|
|
91
92
|
return `do-${ctx.id.toString()}`;
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The async chain holding the connection mutex: a top-level transaction, a
|
|
97
|
+
* `withoutTransactions()` callback, or one statement issued outside both.
|
|
98
|
+
* `inTx` is set from BEGIN until COMMIT/ROLLBACK has run. `held` turns
|
|
99
|
+
* false on release, so a promise chain that outlives its holder (started
|
|
100
|
+
* inside, still running afterwards) queues like any other caller instead
|
|
101
|
+
* of borrowing a lock that is no longer its own.
|
|
102
|
+
*/
|
|
103
|
+
interface MutexFrame {
|
|
104
|
+
held: boolean;
|
|
105
|
+
inTx: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
94
108
|
export class DoSqliteDatabase {
|
|
95
109
|
private db: number | null;
|
|
110
|
+
/** Open transaction levels on the connection (1 = BEGIN, +1 per savepoint). */
|
|
96
111
|
private txDepth = 0;
|
|
97
112
|
private savepointSeq = 0;
|
|
98
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* The connection mutex (see `withMutex()`): transactions, statements
|
|
115
|
+
* issued outside a transaction, and `withoutTransactions()` callbacks
|
|
116
|
+
* queue on it, so a statement never runs inside another chain's open
|
|
117
|
+
* transaction.
|
|
118
|
+
*/
|
|
99
119
|
private txQueue: Promise<void> = Promise.resolve();
|
|
100
120
|
/** Serialises individual statements on this connection (see `serialized()`). */
|
|
101
121
|
private opQueue: Promise<void> = Promise.resolve();
|
|
102
|
-
/**
|
|
103
|
-
private readonly
|
|
122
|
+
/** The frame of the chain that holds the mutex, visible to everything it awaits. */
|
|
123
|
+
private readonly holder = new AsyncLocalStorage<MutexFrame>();
|
|
104
124
|
|
|
105
125
|
private constructor(
|
|
106
126
|
private readonly runtime: SqliteRuntime,
|
|
@@ -252,18 +272,59 @@ export class DoSqliteDatabase {
|
|
|
252
272
|
sql: string,
|
|
253
273
|
params?: SqlParams,
|
|
254
274
|
): Promise<T[]> {
|
|
255
|
-
return this.
|
|
256
|
-
this.
|
|
275
|
+
return this.withMutex(() =>
|
|
276
|
+
this.serialized(() =>
|
|
277
|
+
this.retryingColdMisses(() => this.execUnlocked<T>(sql, params)),
|
|
278
|
+
),
|
|
257
279
|
);
|
|
258
280
|
}
|
|
259
281
|
|
|
260
282
|
/** Run a statement that produces no rows of interest; returns the change count. */
|
|
261
283
|
async run(sql: string, params?: SqlParams): Promise<RunResult> {
|
|
262
|
-
return this.
|
|
263
|
-
this.
|
|
284
|
+
return this.withMutex(() =>
|
|
285
|
+
this.serialized(() =>
|
|
286
|
+
this.retryingColdMisses(() => this.runUnlocked(sql, params)),
|
|
287
|
+
),
|
|
264
288
|
);
|
|
265
289
|
}
|
|
266
290
|
|
|
291
|
+
/** The caller's mutex frame, when its async chain holds the mutex right now. */
|
|
292
|
+
private ownFrame(): MutexFrame | undefined {
|
|
293
|
+
const frame = this.holder.getStore();
|
|
294
|
+
return frame?.held === true ? frame : undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Run `fn` holding the connection mutex. Re-entrant: a chain that already
|
|
299
|
+
* holds it (a transaction body, a `withoutTransactions()` callback, and
|
|
300
|
+
* everything they await) runs `fn` at once; any other chain queues until
|
|
301
|
+
* the holder is done — a statement issued while another chain's
|
|
302
|
+
* transaction is open runs after its COMMIT or ROLLBACK, never inside it.
|
|
303
|
+
*
|
|
304
|
+
* A holder must not await work that another chain started and that
|
|
305
|
+
* itself needs the mutex: that work waits for the holder, the holder for
|
|
306
|
+
* the work.
|
|
307
|
+
*/
|
|
308
|
+
private async withMutex<T>(
|
|
309
|
+
fn: (frame: MutexFrame) => Promise<T>,
|
|
310
|
+
): Promise<T> {
|
|
311
|
+
const own = this.ownFrame();
|
|
312
|
+
if (own !== undefined) return fn(own);
|
|
313
|
+
const previous = this.txQueue;
|
|
314
|
+
let release: () => void = () => undefined;
|
|
315
|
+
this.txQueue = new Promise<void>((resolve) => {
|
|
316
|
+
release = resolve;
|
|
317
|
+
});
|
|
318
|
+
await previous;
|
|
319
|
+
const frame: MutexFrame = { held: true, inTx: false };
|
|
320
|
+
try {
|
|
321
|
+
return await this.holder.run(frame, () => fn(frame));
|
|
322
|
+
} finally {
|
|
323
|
+
frame.held = false;
|
|
324
|
+
release();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
267
328
|
/**
|
|
268
329
|
* Run one statement, re-running it after a cold-miss resolve. Only
|
|
269
330
|
* outside a transaction: inside one, SQLite may have put the pager into
|
|
@@ -271,8 +332,7 @@ export class DoSqliteDatabase {
|
|
|
271
332
|
* retried by `transaction()` instead.
|
|
272
333
|
*/
|
|
273
334
|
private async retryingColdMisses<T>(op: () => Promise<T>): Promise<T> {
|
|
274
|
-
if (this.
|
|
275
|
-
return op();
|
|
335
|
+
if (this.ownFrame()?.inTx === true) return op();
|
|
276
336
|
let pinned = false;
|
|
277
337
|
try {
|
|
278
338
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -382,44 +442,21 @@ export class DoSqliteDatabase {
|
|
|
382
442
|
}
|
|
383
443
|
|
|
384
444
|
/**
|
|
385
|
-
* Run `fn` inside a transaction. Top-level transactions
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
* `
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
* when
|
|
445
|
+
* Run `fn` inside a transaction. Top-level transactions hold the
|
|
446
|
+
* connection mutex (see `withMutex()`) from BEGIN to COMMIT/ROLLBACK — a
|
|
447
|
+
* Durable Object is single-threaded but async, and LangGraph issues
|
|
448
|
+
* `put`/`putWrites` concurrently, so two callers awaiting `BEGIN` at the
|
|
449
|
+
* same time would otherwise collide ("cannot start a transaction within a
|
|
450
|
+
* transaction"), and a statement from another chain would join the open
|
|
451
|
+
* transaction. True nesting (a `transaction()` call made *inside* `fn`'s
|
|
452
|
+
* async chain) is detected with AsyncLocalStorage and becomes a
|
|
453
|
+
* SAVEPOINT. Commits when `fn` resolves, rolls back and rethrows when it
|
|
454
|
+
* rejects. The VFS flushes committed pages to storage atomically when
|
|
455
|
+
* SQLite releases the write lock at COMMIT.
|
|
394
456
|
*/
|
|
395
457
|
async transaction<T>(fn: () => Promise<T>): Promise<T> {
|
|
396
|
-
if (this.
|
|
397
|
-
|
|
398
|
-
await this.run(`SAVEPOINT ${savepoint}`);
|
|
399
|
-
this.txDepth++;
|
|
400
|
-
try {
|
|
401
|
-
const result = await fn();
|
|
402
|
-
this.txDepth--;
|
|
403
|
-
await this.run(`RELEASE ${savepoint}`);
|
|
404
|
-
return result;
|
|
405
|
-
} catch (error) {
|
|
406
|
-
this.txDepth--;
|
|
407
|
-
try {
|
|
408
|
-
await this.run(`ROLLBACK TO ${savepoint}; RELEASE ${savepoint}`);
|
|
409
|
-
} catch {
|
|
410
|
-
// The original error is more useful than a rollback failure.
|
|
411
|
-
}
|
|
412
|
-
throw error;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const previous = this.txQueue;
|
|
417
|
-
let release: () => void = () => undefined;
|
|
418
|
-
this.txQueue = new Promise<void>((resolve) => {
|
|
419
|
-
release = resolve;
|
|
420
|
-
});
|
|
421
|
-
await previous;
|
|
422
|
-
try {
|
|
458
|
+
if (this.ownFrame()?.inTx === true) return this.savepoint(fn);
|
|
459
|
+
return this.withMutex(async (frame) => {
|
|
423
460
|
// A cold page inside `fn` fails a statement with SQLITE_IOERR; the
|
|
424
461
|
// transaction is rolled back, the segment(s) fetched and pinned, and
|
|
425
462
|
// `fn` re-run from the top (it must be re-runnable: the checkpointer's
|
|
@@ -429,27 +466,7 @@ export class DoSqliteDatabase {
|
|
|
429
466
|
try {
|
|
430
467
|
for (let attempt = 0; ; attempt++) {
|
|
431
468
|
try {
|
|
432
|
-
return await this.
|
|
433
|
-
await this.run('BEGIN IMMEDIATE');
|
|
434
|
-
this.txDepth++;
|
|
435
|
-
try {
|
|
436
|
-
const result = await fn();
|
|
437
|
-
this.txDepth--;
|
|
438
|
-
await this.run('COMMIT');
|
|
439
|
-
// Misses a statement swallowed (see `retryingColdMisses`)
|
|
440
|
-
// must not trigger a retry of some later, unrelated error.
|
|
441
|
-
this.vfs.clearMisses();
|
|
442
|
-
return result;
|
|
443
|
-
} catch (error) {
|
|
444
|
-
this.txDepth--;
|
|
445
|
-
try {
|
|
446
|
-
await this.run('ROLLBACK');
|
|
447
|
-
} catch {
|
|
448
|
-
// The original error is more useful than a rollback failure.
|
|
449
|
-
}
|
|
450
|
-
throw error;
|
|
451
|
-
}
|
|
452
|
-
});
|
|
469
|
+
return await this.transactionAttempt(frame, fn);
|
|
453
470
|
} catch (error) {
|
|
454
471
|
if (
|
|
455
472
|
!this.vfs.hasMisses() ||
|
|
@@ -464,34 +481,76 @@ export class DoSqliteDatabase {
|
|
|
464
481
|
} finally {
|
|
465
482
|
if (pinned) this.vfs.releasePins();
|
|
466
483
|
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** One BEGIN … COMMIT of `fn`, rolled back when anything in it fails. */
|
|
488
|
+
private async transactionAttempt<T>(
|
|
489
|
+
frame: MutexFrame,
|
|
490
|
+
fn: () => Promise<T>,
|
|
491
|
+
): Promise<T> {
|
|
492
|
+
await this.run('BEGIN IMMEDIATE');
|
|
493
|
+
frame.inTx = true;
|
|
494
|
+
this.txDepth = 1;
|
|
495
|
+
try {
|
|
496
|
+
const result = await fn();
|
|
497
|
+
await this.run('COMMIT');
|
|
498
|
+
// Misses a statement swallowed (see `retryingColdMisses`) must not
|
|
499
|
+
// trigger a retry of some later, unrelated error.
|
|
500
|
+
this.vfs.clearMisses();
|
|
501
|
+
return result;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
// Also after a failed COMMIT: SQLite keeps the transaction open when
|
|
504
|
+
// a deferred constraint fails it. When SQLite already rolled back on
|
|
505
|
+
// its own, this ROLLBACK fails harmlessly.
|
|
506
|
+
try {
|
|
507
|
+
await this.run('ROLLBACK');
|
|
508
|
+
} catch {
|
|
509
|
+
// The original error is more useful than a rollback failure.
|
|
510
|
+
}
|
|
511
|
+
throw error;
|
|
467
512
|
} finally {
|
|
468
|
-
|
|
513
|
+
// Only now: until ROLLBACK/COMMIT has run, the transaction is open.
|
|
514
|
+
frame.inTx = false;
|
|
515
|
+
this.txDepth = 0;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** A nested `transaction()`: a SAVEPOINT inside the caller's transaction. */
|
|
520
|
+
private async savepoint<T>(fn: () => Promise<T>): Promise<T> {
|
|
521
|
+
const savepoint = `sp_${++this.savepointSeq}`;
|
|
522
|
+
await this.run(`SAVEPOINT ${savepoint}`);
|
|
523
|
+
this.txDepth++;
|
|
524
|
+
try {
|
|
525
|
+
const result = await fn();
|
|
526
|
+
await this.run(`RELEASE ${savepoint}`);
|
|
527
|
+
return result;
|
|
528
|
+
} catch (error) {
|
|
529
|
+
try {
|
|
530
|
+
await this.run(`ROLLBACK TO ${savepoint}; RELEASE ${savepoint}`);
|
|
531
|
+
} catch {
|
|
532
|
+
// The original error is more useful than a rollback failure.
|
|
533
|
+
}
|
|
534
|
+
throw error;
|
|
535
|
+
} finally {
|
|
536
|
+
this.txDepth--;
|
|
469
537
|
}
|
|
470
538
|
}
|
|
471
539
|
|
|
472
540
|
/**
|
|
473
|
-
* Run `fn` while HOLDING the
|
|
474
|
-
* transaction — for statements SQLite refuses to run inside one
|
|
475
|
-
*
|
|
476
|
-
*
|
|
541
|
+
* Run `fn` while HOLDING the connection mutex without opening a
|
|
542
|
+
* transaction — for statements SQLite refuses to run inside one
|
|
543
|
+
* (`VACUUM`) and for work no transaction may interleave with. Transactions
|
|
544
|
+
* and statements of other chains queue behind it; statements, nested
|
|
545
|
+
* `withoutTransactions()` and transactions inside `fn` run at once.
|
|
477
546
|
*/
|
|
478
547
|
async withoutTransactions<T>(fn: () => Promise<T>): Promise<T> {
|
|
479
|
-
if (this.
|
|
548
|
+
if (this.ownFrame()?.inTx === true) {
|
|
480
549
|
throw new DoVfsError(
|
|
481
550
|
'withoutTransactions() cannot run inside a transaction',
|
|
482
551
|
);
|
|
483
552
|
}
|
|
484
|
-
|
|
485
|
-
let release: () => void = () => undefined;
|
|
486
|
-
this.txQueue = new Promise<void>((resolve) => {
|
|
487
|
-
release = resolve;
|
|
488
|
-
});
|
|
489
|
-
await previous;
|
|
490
|
-
try {
|
|
491
|
-
return await fn();
|
|
492
|
-
} finally {
|
|
493
|
-
release();
|
|
494
|
-
}
|
|
553
|
+
return this.withMutex(() => fn());
|
|
495
554
|
}
|
|
496
555
|
|
|
497
556
|
/**
|
|
@@ -542,17 +601,24 @@ export class DoSqliteDatabase {
|
|
|
542
601
|
return this.vfs.tierEnabled;
|
|
543
602
|
}
|
|
544
603
|
|
|
545
|
-
/**
|
|
604
|
+
/**
|
|
605
|
+
* One eviction pass. It holds the connection mutex only for its short
|
|
606
|
+
* synchronous steps (plan, read a segment's rows, commit a segment) and
|
|
607
|
+
* never across R2 I/O, so a turn that starts mid-pass is not held up
|
|
608
|
+
* for the rest of it (see `DoVfs.tierFlush`).
|
|
609
|
+
*/
|
|
546
610
|
async tierFlush(
|
|
547
611
|
opts: { force?: boolean; maxSegments?: number } = {},
|
|
548
612
|
): Promise<TierFlushResult> {
|
|
549
|
-
if (this.
|
|
613
|
+
if (this.ownFrame()?.inTx === true)
|
|
550
614
|
throw new DoVfsError('tierFlush() is not allowed inside a transaction');
|
|
551
|
-
return this.
|
|
552
|
-
this.vfs.tierFlush(this.fileName, opts),
|
|
553
|
-
);
|
|
615
|
+
return this.vfs.tierFlush(this.fileName, opts, this.exclusive);
|
|
554
616
|
}
|
|
555
617
|
|
|
618
|
+
/** Runs a synchronous VFS step holding the connection mutex. */
|
|
619
|
+
private readonly exclusive: Exclusive = <T>(step: () => T): Promise<T> =>
|
|
620
|
+
this.withoutTransactions(async () => step());
|
|
621
|
+
|
|
556
622
|
/** Persist the chunks touched since the last call (call at the end of a turn). */
|
|
557
623
|
recordAccess(): void {
|
|
558
624
|
this.vfs.recordAccess(this.fileName);
|
|
@@ -571,7 +637,7 @@ export class DoSqliteDatabase {
|
|
|
571
637
|
|
|
572
638
|
/** Pull every cold chunk back into storage (the file is then fully hot). */
|
|
573
639
|
async materializeTier(): Promise<{ chunks: number }> {
|
|
574
|
-
if (this.
|
|
640
|
+
if (this.ownFrame()?.inTx === true)
|
|
575
641
|
throw new DoVfsError(
|
|
576
642
|
'materializeTier() is not allowed inside a transaction',
|
|
577
643
|
);
|
|
@@ -695,7 +761,7 @@ export class DoSqliteDatabase {
|
|
|
695
761
|
* the file is open. Transiently needs 2× the file in DO storage.
|
|
696
762
|
*/
|
|
697
763
|
async compact(): Promise<{ beforeBytes: number; afterBytes: number }> {
|
|
698
|
-
if (this.
|
|
764
|
+
if (this.ownFrame()?.inTx === true)
|
|
699
765
|
throw new DoVfsError('compact() is not allowed inside a transaction');
|
|
700
766
|
const target = `${this.fileName}${VACUUM_FILE_SUFFIX}`;
|
|
701
767
|
return this.withoutTransactions(async () => {
|