@ny4n/durable 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +711 -2
- package/dist/documents.d.ts +66 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +98 -0
- package/dist/documents.js.map +1 -0
- package/dist/entries.d.ts +31 -0
- package/dist/entries.d.ts.map +1 -0
- package/dist/entries.js +31 -0
- package/dist/entries.js.map +1 -0
- package/dist/env/decode.d.ts +16 -0
- package/dist/env/decode.d.ts.map +1 -0
- package/dist/env/decode.js +28 -0
- package/dist/env/decode.js.map +1 -0
- package/dist/env/index.d.ts +263 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +49 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/line-scan.d.ts +13 -0
- package/dist/env/line-scan.d.ts.map +1 -0
- package/dist/env/line-scan.js +147 -0
- package/dist/env/line-scan.js.map +1 -0
- package/dist/env/node-watch.d.ts +29 -0
- package/dist/env/node-watch.d.ts.map +1 -0
- package/dist/env/node-watch.js +485 -0
- package/dist/env/node-watch.js.map +1 -0
- package/dist/env/node.d.ts +56 -0
- package/dist/env/node.d.ts.map +1 -0
- package/dist/env/node.js +1166 -0
- package/dist/env/node.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/harness/agent.d.ts +33 -0
- package/dist/harness/agent.d.ts.map +1 -0
- package/dist/harness/agent.js +235 -0
- package/dist/harness/agent.js.map +1 -0
- package/dist/harness/assistant.d.ts +15 -0
- package/dist/harness/assistant.d.ts.map +1 -0
- package/dist/harness/assistant.js +23 -0
- package/dist/harness/assistant.js.map +1 -0
- package/dist/harness/compaction.d.ts +56 -0
- package/dist/harness/compaction.d.ts.map +1 -0
- package/dist/harness/compaction.js +384 -0
- package/dist/harness/compaction.js.map +1 -0
- package/dist/harness/context.d.ts +63 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +221 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/define.d.ts +18 -0
- package/dist/harness/define.d.ts.map +1 -0
- package/dist/harness/define.js +25 -0
- package/dist/harness/define.js.map +1 -0
- package/dist/harness/events.d.ts +180 -0
- package/dist/harness/events.d.ts.map +1 -0
- package/dist/harness/events.js +370 -0
- package/dist/harness/events.js.map +1 -0
- package/dist/harness/generation.d.ts +54 -0
- package/dist/harness/generation.d.ts.map +1 -0
- package/dist/harness/generation.js +514 -0
- package/dist/harness/generation.js.map +1 -0
- package/dist/harness/harness.d.ts +17 -0
- package/dist/harness/harness.d.ts.map +1 -0
- package/dist/harness/harness.js +332 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/inbox.d.ts +59 -0
- package/dist/harness/inbox.d.ts.map +1 -0
- package/dist/harness/inbox.js +97 -0
- package/dist/harness/inbox.js.map +1 -0
- package/dist/harness/json.d.ts +10 -0
- package/dist/harness/json.d.ts.map +1 -0
- package/dist/harness/json.js +33 -0
- package/dist/harness/json.js.map +1 -0
- package/dist/harness/live.d.ts +142 -0
- package/dist/harness/live.d.ts.map +1 -0
- package/dist/harness/live.js +149 -0
- package/dist/harness/live.js.map +1 -0
- package/dist/harness/output.d.ts +69 -0
- package/dist/harness/output.d.ts.map +1 -0
- package/dist/harness/output.js +318 -0
- package/dist/harness/output.js.map +1 -0
- package/dist/harness/prompt.d.ts +27 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +131 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/provider.d.ts +14 -0
- package/dist/harness/provider.d.ts.map +1 -0
- package/dist/harness/provider.js +30 -0
- package/dist/harness/provider.js.map +1 -0
- package/dist/harness/registry.d.ts +6 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +101 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/run.d.ts +17 -0
- package/dist/harness/run.d.ts.map +1 -0
- package/dist/harness/run.js +26 -0
- package/dist/harness/run.js.map +1 -0
- package/dist/harness/scheduler.d.ts +106 -0
- package/dist/harness/scheduler.d.ts.map +1 -0
- package/dist/harness/scheduler.js +1743 -0
- package/dist/harness/scheduler.js.map +1 -0
- package/dist/harness/submissions.d.ts +29 -0
- package/dist/harness/submissions.d.ts.map +1 -0
- package/dist/harness/submissions.js +175 -0
- package/dist/harness/submissions.js.map +1 -0
- package/dist/harness/task-graph.d.ts +49 -0
- package/dist/harness/task-graph.d.ts.map +1 -0
- package/dist/harness/task-graph.js +168 -0
- package/dist/harness/task-graph.js.map +1 -0
- package/dist/harness/tool.d.ts +76 -0
- package/dist/harness/tool.d.ts.map +1 -0
- package/dist/harness/tool.js +736 -0
- package/dist/harness/tool.js.map +1 -0
- package/dist/harness/types.d.ts +713 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/usage.d.ts +18 -0
- package/dist/harness/usage.d.ts.map +1 -0
- package/dist/harness/usage.js +57 -0
- package/dist/harness/usage.js.map +1 -0
- package/dist/harness/util.d.ts +19 -0
- package/dist/harness/util.d.ts.map +1 -0
- package/dist/harness/util.js +58 -0
- package/dist/harness/util.js.map +1 -0
- package/dist/harness/view.d.ts +47 -0
- package/dist/harness/view.d.ts.map +1 -0
- package/dist/harness/view.js +206 -0
- package/dist/harness/view.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +9 -0
- package/dist/ids.js.map +1 -0
- package/dist/images/cloudflare.d.ts +4 -0
- package/dist/images/cloudflare.d.ts.map +1 -0
- package/dist/images/cloudflare.js +9 -0
- package/dist/images/cloudflare.js.map +1 -0
- package/dist/images/exif.d.ts +3 -0
- package/dist/images/exif.d.ts.map +1 -0
- package/dist/images/exif.js +71 -0
- package/dist/images/exif.js.map +1 -0
- package/dist/images/index.d.ts +18 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +125 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/node-worker.d.ts +17 -0
- package/dist/images/node-worker.d.ts.map +1 -0
- package/dist/images/node-worker.js +16 -0
- package/dist/images/node-worker.js.map +1 -0
- package/dist/images/node.d.ts +21 -0
- package/dist/images/node.d.ts.map +1 -0
- package/dist/images/node.js +121 -0
- package/dist/images/node.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/session/forks.d.ts +10 -0
- package/dist/session/forks.d.ts.map +1 -0
- package/dist/session/forks.js +42 -0
- package/dist/session/forks.js.map +1 -0
- package/dist/session/observation.d.ts +42 -0
- package/dist/session/observation.d.ts.map +1 -0
- package/dist/session/observation.js +281 -0
- package/dist/session/observation.js.map +1 -0
- package/dist/session/session.d.ts +133 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +811 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/transaction.d.ts +119 -0
- package/dist/session/transaction.d.ts.map +1 -0
- package/dist/session/transaction.js +795 -0
- package/dist/session/transaction.js.map +1 -0
- package/dist/storage/jsonl/index.d.ts +2 -0
- package/dist/storage/jsonl/index.d.ts.map +1 -0
- package/dist/storage/jsonl/index.js +2 -0
- package/dist/storage/jsonl/index.js.map +1 -0
- package/dist/storage/jsonl/node.d.ts +6 -0
- package/dist/storage/jsonl/node.d.ts.map +1 -0
- package/dist/storage/jsonl/node.js +8 -0
- package/dist/storage/jsonl/node.js.map +1 -0
- package/dist/storage/jsonl/storage.d.ts +143 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -0
- package/dist/storage/jsonl/storage.js +682 -0
- package/dist/storage/jsonl/storage.js.map +1 -0
- package/dist/storage/memory.d.ts +62 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +733 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/scan.d.ts +18 -0
- package/dist/storage/scan.d.ts.map +1 -0
- package/dist/storage/scan.js +29 -0
- package/dist/storage/scan.js.map +1 -0
- package/dist/storage/sqlite/cloudflare.d.ts +46 -0
- package/dist/storage/sqlite/cloudflare.d.ts.map +1 -0
- package/dist/storage/sqlite/cloudflare.js +106 -0
- package/dist/storage/sqlite/cloudflare.js.map +1 -0
- package/dist/storage/sqlite/database.d.ts +37 -0
- package/dist/storage/sqlite/database.d.ts.map +1 -0
- package/dist/storage/sqlite/database.js +2 -0
- package/dist/storage/sqlite/database.js.map +1 -0
- package/dist/storage/sqlite/index.d.ts +4 -0
- package/dist/storage/sqlite/index.d.ts.map +1 -0
- package/dist/storage/sqlite/index.js +3 -0
- package/dist/storage/sqlite/index.js.map +1 -0
- package/dist/storage/sqlite/migrations.d.ts +11 -0
- package/dist/storage/sqlite/migrations.d.ts.map +1 -0
- package/dist/storage/sqlite/migrations.js +111 -0
- package/dist/storage/sqlite/migrations.js.map +1 -0
- package/dist/storage/sqlite/node.d.ts +41 -0
- package/dist/storage/sqlite/node.d.ts.map +1 -0
- package/dist/storage/sqlite/node.js +179 -0
- package/dist/storage/sqlite/node.js.map +1 -0
- package/dist/storage/sqlite/storage.d.ts +65 -0
- package/dist/storage/sqlite/storage.d.ts.map +1 -0
- package/dist/storage/sqlite/storage.js +670 -0
- package/dist/storage/sqlite/storage.js.map +1 -0
- package/dist/tasks.d.ts +6 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +5 -0
- package/dist/tasks.js.map +1 -0
- package/dist/testing/assertions.d.ts +5 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +25 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/env-conformance.d.ts +7 -0
- package/dist/testing/env-conformance.d.ts.map +1 -0
- package/dist/testing/env-conformance.js +431 -0
- package/dist/testing/env-conformance.js.map +1 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/runner.d.ts +12 -0
- package/dist/testing/runner.d.ts.map +1 -0
- package/dist/testing/runner.js +23 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/testing/storage-benchmark.d.ts +41 -0
- package/dist/testing/storage-benchmark.d.ts.map +1 -0
- package/dist/testing/storage-benchmark.js +350 -0
- package/dist/testing/storage-benchmark.js.map +1 -0
- package/dist/testing/storage-conformance.d.ts +4 -0
- package/dist/testing/storage-conformance.d.ts.map +1 -0
- package/dist/testing/storage-conformance.js +1213 -0
- package/dist/testing/storage-conformance.js.map +1 -0
- package/dist/testing/types.d.ts +37 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing/types.js +2 -0
- package/dist/testing/types.js.map +1 -0
- package/dist/tools/bash.d.ts +55 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +138 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit-diff.d.ts +89 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +386 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +18 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +117 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/env.d.ts +5 -0
- package/dist/tools/env.d.ts.map +1 -0
- package/dist/tools/env.js +7 -0
- package/dist/tools/env.js.map +1 -0
- package/dist/tools/file-mutation-queue.d.ts +9 -0
- package/dist/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/tools/file-mutation-queue.js +52 -0
- package/dist/tools/file-mutation-queue.js.map +1 -0
- package/dist/tools/image-processor.d.ts +47 -0
- package/dist/tools/image-processor.d.ts.map +1 -0
- package/dist/tools/image-processor.js +30 -0
- package/dist/tools/image-processor.js.map +1 -0
- package/dist/tools/image.d.ts +20 -0
- package/dist/tools/image.d.ts.map +1 -0
- package/dist/tools/image.js +178 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/index.d.ts +46 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/path-utils.d.ts +5 -0
- package/dist/tools/path-utils.d.ts.map +1 -0
- package/dist/tools/path-utils.js +26 -0
- package/dist/tools/path-utils.js.map +1 -0
- package/dist/tools/read.d.ts +25 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +235 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +31 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/truncate.d.ts +64 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +154 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +932 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +140 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ConversationId, DocumentId, EntryId, Seq, Storage } from "../types.ts";
|
|
2
|
+
export type StorageBenchmarkScale = {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly entryCount: number;
|
|
5
|
+
readonly taskCount: number;
|
|
6
|
+
readonly documentCount: number;
|
|
7
|
+
};
|
|
8
|
+
export declare const STORAGE_MEMORY_SCALES: readonly StorageBenchmarkScale[];
|
|
9
|
+
export declare const TIMING_SCALE: StorageBenchmarkScale;
|
|
10
|
+
declare const REPLAY_TAILS: readonly [0, 16, 128, 1024];
|
|
11
|
+
export declare function storageBenchmarkPrimaryRecordCount(scale: StorageBenchmarkScale): number;
|
|
12
|
+
export type StorageBenchmarkDataset = {
|
|
13
|
+
readonly firstEntryId: EntryId;
|
|
14
|
+
readonly filteredTaskCount: number;
|
|
15
|
+
readonly exactDocumentId: DocumentId;
|
|
16
|
+
readonly exactDocumentKey: string;
|
|
17
|
+
readonly replayDocumentIds: Readonly<Record<(typeof REPLAY_TAILS)[number], DocumentId>>;
|
|
18
|
+
readonly historicalDocumentId: DocumentId;
|
|
19
|
+
readonly ancientAt: Seq;
|
|
20
|
+
readonly recentAt: Seq;
|
|
21
|
+
readonly deepestConversationId: ConversationId;
|
|
22
|
+
readonly ancestorHeadEntryId: EntryId;
|
|
23
|
+
};
|
|
24
|
+
/** Seed deterministic representative data through only the public `Storage` contract. */
|
|
25
|
+
export declare function seedStorageBenchmark(storage: Storage, scale?: StorageBenchmarkScale): Promise<StorageBenchmarkDataset>;
|
|
26
|
+
export type StorageReadBenchmark = {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
run(storage: Storage, dataset: StorageBenchmarkDataset): Promise<number>;
|
|
29
|
+
expected(dataset: StorageBenchmarkDataset): number;
|
|
30
|
+
};
|
|
31
|
+
export declare const STORAGE_READ_BENCHMARKS: readonly StorageReadBenchmark[];
|
|
32
|
+
export type StorageWriteBenchmark = {
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly expected: number;
|
|
35
|
+
run(storage: Storage): Promise<number>;
|
|
36
|
+
};
|
|
37
|
+
/** Seed the common state expected by every write benchmark sample. */
|
|
38
|
+
export declare function seedStorageWriteBenchmark(storage: Storage): Promise<void>;
|
|
39
|
+
export declare const STORAGE_WRITE_BENCHMARKS: readonly StorageWriteBenchmark[];
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=storage-benchmark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-benchmark.d.ts","sourceRoot":"","sources":["../../src/testing/storage-benchmark.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,cAAc,EAEd,UAAU,EACV,OAAO,EACP,GAAG,EACH,OAAO,EAKP,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,SAAS,qBAAqB,EAGjE,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,qBAK1B,CAAC;AAEF,QAAA,MAAM,YAAY,YAAI,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAK,CAAU,CAAC;AAMlD,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAUvF;AAID,MAAM,MAAM,uBAAuB,GAAG;IACrC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC;IACrC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IACxF,QAAQ,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC;IACvB,QAAQ,CAAC,qBAAqB,EAAE,cAAc,CAAC;IAC/C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACtC,CAAC;AAoBF,yFAAyF;AACzF,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,qBAAoC,GACzC,OAAO,CAAC,uBAAuB,CAAC,CAyLlC;AAED,MAAM,MAAM,oBAAoB,GAAG;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,QAAQ,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAkGlE,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvC,CAAC;AAEF,sEAAsE;AACtE,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB/E;AAED,eAAO,MAAM,wBAAwB,EAAE,SAAS,qBAAqB,EAgFpE,CAAC"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { BACKGROUND_CONTEXT } from "@ny4n/chord/context";
|
|
2
|
+
import { ROOT_CONVERSATION_ID } from "../types.js";
|
|
3
|
+
export const STORAGE_MEMORY_SCALES = [
|
|
4
|
+
{ name: "1k", entryCount: 1_000, taskCount: 200, documentCount: 200 },
|
|
5
|
+
{ name: "10k", entryCount: 10_000, taskCount: 2_000, documentCount: 2_000 },
|
|
6
|
+
];
|
|
7
|
+
export const TIMING_SCALE = {
|
|
8
|
+
name: "timing",
|
|
9
|
+
entryCount: 1_000,
|
|
10
|
+
taskCount: 300,
|
|
11
|
+
documentCount: 300,
|
|
12
|
+
};
|
|
13
|
+
const REPLAY_TAILS = [0, 16, 128, 1_024];
|
|
14
|
+
const HISTORY_SEGMENT_LENGTH = 128;
|
|
15
|
+
const FORK_DEPTH = 8;
|
|
16
|
+
const ENTRIES_PER_FORK = 32;
|
|
17
|
+
const BATCH_SIZE = 100;
|
|
18
|
+
export function storageBenchmarkPrimaryRecordCount(scale) {
|
|
19
|
+
return (1 +
|
|
20
|
+
scale.entryCount +
|
|
21
|
+
scale.taskCount +
|
|
22
|
+
scale.documentCount +
|
|
23
|
+
REPLAY_TAILS.length +
|
|
24
|
+
1 +
|
|
25
|
+
FORK_DEPTH * (1 + ENTRIES_PER_FORK));
|
|
26
|
+
}
|
|
27
|
+
function task(id, index) {
|
|
28
|
+
const statuses = ["pending", "running", "terminal"];
|
|
29
|
+
const status = statuses[index % statuses.length];
|
|
30
|
+
const common = {
|
|
31
|
+
id,
|
|
32
|
+
conversationId: ROOT_CONVERSATION_ID,
|
|
33
|
+
kind: index % 4 === 0 ? "benchmark.filtered" : "benchmark.other",
|
|
34
|
+
version: 1,
|
|
35
|
+
input: { index },
|
|
36
|
+
background: index % 5 === 0,
|
|
37
|
+
abortRequested: index % 7 === 0,
|
|
38
|
+
};
|
|
39
|
+
if (status === "terminal") {
|
|
40
|
+
return { ...common, state: { status, outcome: { status: "completed", result: { index } } } };
|
|
41
|
+
}
|
|
42
|
+
return { ...common, state: { status, checkpoint: { index, payload: "x".repeat(64) } } };
|
|
43
|
+
}
|
|
44
|
+
/** Seed deterministic representative data through only the public `Storage` contract. */
|
|
45
|
+
export async function seedStorageBenchmark(storage, scale = TIMING_SCALE) {
|
|
46
|
+
await storage.commit([{ type: "conversation", value: { id: ROOT_CONVERSATION_ID } }], BACKGROUND_CONTEXT);
|
|
47
|
+
let firstEntryId;
|
|
48
|
+
for (let start = 0; start < scale.entryCount; start += BATCH_SIZE) {
|
|
49
|
+
const writes = [];
|
|
50
|
+
for (let index = start; index < Math.min(start + BATCH_SIZE, scale.entryCount); index++) {
|
|
51
|
+
const id = await storage.mintId();
|
|
52
|
+
if (index === 0)
|
|
53
|
+
firstEntryId = id;
|
|
54
|
+
writes.push({
|
|
55
|
+
type: "entry",
|
|
56
|
+
value: {
|
|
57
|
+
id,
|
|
58
|
+
conversationId: ROOT_CONVERSATION_ID,
|
|
59
|
+
kind: "benchmark.entry",
|
|
60
|
+
...(index === 0 ? { head: id } : {}),
|
|
61
|
+
data: { index, text: `entry-${index}-${"x".repeat(96)}` },
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
await storage.commit(writes, BACKGROUND_CONTEXT);
|
|
66
|
+
}
|
|
67
|
+
for (let start = 0; start < scale.taskCount; start += BATCH_SIZE) {
|
|
68
|
+
const writes = [];
|
|
69
|
+
for (let index = start; index < Math.min(start + BATCH_SIZE, scale.taskCount); index++) {
|
|
70
|
+
writes.push({ type: "task", value: task(await storage.mintId(), index) });
|
|
71
|
+
}
|
|
72
|
+
await storage.commit(writes, BACKGROUND_CONTEXT);
|
|
73
|
+
}
|
|
74
|
+
let exactDocumentId;
|
|
75
|
+
for (let start = 0; start < scale.documentCount; start += BATCH_SIZE) {
|
|
76
|
+
const writes = [];
|
|
77
|
+
for (let index = start; index < Math.min(start + BATCH_SIZE, scale.documentCount); index++) {
|
|
78
|
+
const id = await storage.mintId();
|
|
79
|
+
exactDocumentId = id;
|
|
80
|
+
writes.push({
|
|
81
|
+
type: "document.create",
|
|
82
|
+
record: { id, kind: "benchmark.family", key: `key-${index}`, scope: { kind: "session" } },
|
|
83
|
+
content: { kind: "base", version: 1, value: { index, text: "x".repeat(128) } },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
await storage.commit(writes, BACKGROUND_CONTEXT);
|
|
87
|
+
}
|
|
88
|
+
const replayEntries = await Promise.all(REPLAY_TAILS.map(async (tail) => {
|
|
89
|
+
const id = await storage.mintId();
|
|
90
|
+
return { tail, id };
|
|
91
|
+
}));
|
|
92
|
+
await storage.commit(replayEntries.map(({ tail, id }) => ({
|
|
93
|
+
type: "document.create",
|
|
94
|
+
record: {
|
|
95
|
+
id,
|
|
96
|
+
kind: "benchmark.replay",
|
|
97
|
+
key: String(tail),
|
|
98
|
+
scope: { kind: "conversation", conversationId: ROOT_CONVERSATION_ID },
|
|
99
|
+
history: "rewindable",
|
|
100
|
+
fork: "asOf",
|
|
101
|
+
},
|
|
102
|
+
content: { kind: "base", version: 1, value: { count: 0, text: "x".repeat(64) } },
|
|
103
|
+
})), BACKGROUND_CONTEXT);
|
|
104
|
+
for (let count = 1; count <= REPLAY_TAILS.at(-1); count++) {
|
|
105
|
+
await storage.commit(replayEntries
|
|
106
|
+
.filter(({ tail }) => count <= tail)
|
|
107
|
+
.map(({ id }) => ({
|
|
108
|
+
type: "document.change",
|
|
109
|
+
id,
|
|
110
|
+
content: { kind: "delta", version: 1, ops: [["s", ["count"], count]] },
|
|
111
|
+
})), BACKGROUND_CONTEXT);
|
|
112
|
+
}
|
|
113
|
+
const historicalDocumentId = await storage.mintId();
|
|
114
|
+
const historicalRecord = {
|
|
115
|
+
id: historicalDocumentId,
|
|
116
|
+
kind: "benchmark.history",
|
|
117
|
+
scope: { kind: "conversation", conversationId: ROOT_CONVERSATION_ID },
|
|
118
|
+
history: "rewindable",
|
|
119
|
+
fork: "asOf",
|
|
120
|
+
};
|
|
121
|
+
await storage.commit([
|
|
122
|
+
{
|
|
123
|
+
type: "document.create",
|
|
124
|
+
record: historicalRecord,
|
|
125
|
+
content: { kind: "base", version: 1, value: { count: 0 } },
|
|
126
|
+
},
|
|
127
|
+
], BACKGROUND_CONTEXT);
|
|
128
|
+
let ancientAt;
|
|
129
|
+
for (let count = 1; count <= HISTORY_SEGMENT_LENGTH; count++) {
|
|
130
|
+
ancientAt = await storage.commit([
|
|
131
|
+
{
|
|
132
|
+
type: "document.change",
|
|
133
|
+
id: historicalDocumentId,
|
|
134
|
+
content: { kind: "delta", version: 1, ops: [["s", ["count"], count]] },
|
|
135
|
+
},
|
|
136
|
+
], BACKGROUND_CONTEXT);
|
|
137
|
+
}
|
|
138
|
+
await storage.commit([
|
|
139
|
+
{
|
|
140
|
+
type: "document.change",
|
|
141
|
+
id: historicalDocumentId,
|
|
142
|
+
content: { kind: "base", version: 1, value: { count: HISTORY_SEGMENT_LENGTH } },
|
|
143
|
+
},
|
|
144
|
+
], BACKGROUND_CONTEXT);
|
|
145
|
+
if (ancientAt === undefined)
|
|
146
|
+
throw new Error("Benchmark history seed produced no commits");
|
|
147
|
+
let recentAt = ancientAt;
|
|
148
|
+
for (let count = HISTORY_SEGMENT_LENGTH + 1; count <= HISTORY_SEGMENT_LENGTH * 2; count++) {
|
|
149
|
+
recentAt = await storage.commit([
|
|
150
|
+
{
|
|
151
|
+
type: "document.change",
|
|
152
|
+
id: historicalDocumentId,
|
|
153
|
+
content: { kind: "delta", version: 1, ops: [["s", ["count"], count]] },
|
|
154
|
+
},
|
|
155
|
+
], BACKGROUND_CONTEXT);
|
|
156
|
+
}
|
|
157
|
+
if (firstEntryId === undefined)
|
|
158
|
+
throw new Error("Benchmark scale must create entries");
|
|
159
|
+
let parentConversationId = ROOT_CONVERSATION_ID;
|
|
160
|
+
let parentAt = firstEntryId;
|
|
161
|
+
let deepestConversationId = ROOT_CONVERSATION_ID;
|
|
162
|
+
for (let depth = 0; depth < FORK_DEPTH; depth++) {
|
|
163
|
+
const conversationId = await storage.mintId();
|
|
164
|
+
await storage.commit([
|
|
165
|
+
{
|
|
166
|
+
type: "conversation",
|
|
167
|
+
value: { id: conversationId, parent: { conversationId: parentConversationId, at: parentAt } },
|
|
168
|
+
},
|
|
169
|
+
], BACKGROUND_CONTEXT);
|
|
170
|
+
const ids = await Promise.all(Array.from({ length: ENTRIES_PER_FORK }, () => storage.mintId()));
|
|
171
|
+
await storage.commit(ids.map((id, index) => ({
|
|
172
|
+
type: "entry",
|
|
173
|
+
value: {
|
|
174
|
+
id,
|
|
175
|
+
conversationId,
|
|
176
|
+
kind: "benchmark.fork",
|
|
177
|
+
data: { depth, index },
|
|
178
|
+
},
|
|
179
|
+
})), BACKGROUND_CONTEXT);
|
|
180
|
+
parentConversationId = conversationId;
|
|
181
|
+
parentAt = ids.at(-1);
|
|
182
|
+
deepestConversationId = conversationId;
|
|
183
|
+
}
|
|
184
|
+
if (exactDocumentId === undefined)
|
|
185
|
+
throw new Error("Benchmark scale must create documents");
|
|
186
|
+
return {
|
|
187
|
+
firstEntryId,
|
|
188
|
+
filteredTaskCount: Math.min(50, Math.ceil(scale.taskCount / 60)),
|
|
189
|
+
exactDocumentId,
|
|
190
|
+
exactDocumentKey: `key-${scale.documentCount - 1}`,
|
|
191
|
+
replayDocumentIds: Object.fromEntries(replayEntries.map(({ tail, id }) => [tail, id])),
|
|
192
|
+
historicalDocumentId,
|
|
193
|
+
ancientAt,
|
|
194
|
+
recentAt,
|
|
195
|
+
deepestConversationId,
|
|
196
|
+
ancestorHeadEntryId: firstEntryId,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export const STORAGE_READ_BENCHMARKS = [
|
|
200
|
+
{
|
|
201
|
+
name: "exact entry lookup",
|
|
202
|
+
async run(storage, dataset) {
|
|
203
|
+
return (await storage.entry(dataset.firstEntryId, BACKGROUND_CONTEXT))?.entry.id ?? -1;
|
|
204
|
+
},
|
|
205
|
+
expected: ({ firstEntryId }) => firstEntryId,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "entry page scan (100)",
|
|
209
|
+
async run(storage) {
|
|
210
|
+
return (await storage.scanEntries({ conversationId: ROOT_CONVERSATION_ID }, 100, undefined, BACKGROUND_CONTEXT)).items.length;
|
|
211
|
+
},
|
|
212
|
+
expected: () => 100,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: "filtered task scan (50)",
|
|
216
|
+
async run(storage) {
|
|
217
|
+
return (await storage.scanTasks({ kind: "benchmark.filtered", status: "pending", background: true }, 50, undefined, BACKGROUND_CONTEXT)).items.length;
|
|
218
|
+
},
|
|
219
|
+
expected: ({ filteredTaskCount }) => filteredTaskCount,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "exact document address among many",
|
|
223
|
+
async run(storage, dataset) {
|
|
224
|
+
return ((await storage.findDocument({ kind: "benchmark.family", key: dataset.exactDocumentKey, scope: { kind: "session" } }, "current", BACKGROUND_CONTEXT))?.id ?? -1);
|
|
225
|
+
},
|
|
226
|
+
expected: ({ exactDocumentId }) => exactDocumentId,
|
|
227
|
+
},
|
|
228
|
+
...REPLAY_TAILS.map((tail) => ({
|
|
229
|
+
name: `document replay tail (${tail})`,
|
|
230
|
+
async run(storage, dataset) {
|
|
231
|
+
return Number((await storage.document(dataset.replayDocumentIds[tail], "current", BACKGROUND_CONTEXT))?.value.count);
|
|
232
|
+
},
|
|
233
|
+
expected: () => tail,
|
|
234
|
+
})),
|
|
235
|
+
{
|
|
236
|
+
name: "ancient historical read before newer base",
|
|
237
|
+
async run(storage, dataset) {
|
|
238
|
+
return Number((await storage.document(dataset.historicalDocumentId, dataset.ancientAt, BACKGROUND_CONTEXT))?.value.count);
|
|
239
|
+
},
|
|
240
|
+
expected: () => HISTORY_SEGMENT_LENGTH,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: "recent historical read after newer base",
|
|
244
|
+
async run(storage, dataset) {
|
|
245
|
+
return Number((await storage.document(dataset.historicalDocumentId, dataset.recentAt, BACKGROUND_CONTEXT))?.value.count);
|
|
246
|
+
},
|
|
247
|
+
expected: () => HISTORY_SEGMENT_LENGTH * 2,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "fork-depth history scan (100)",
|
|
251
|
+
async run(storage, dataset) {
|
|
252
|
+
return (await storage.scanEntries({ conversationId: dataset.deepestConversationId }, 100, undefined, BACKGROUND_CONTEXT)).items.length;
|
|
253
|
+
},
|
|
254
|
+
expected: () => 100,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "fork-depth head lookup",
|
|
258
|
+
async run(storage, dataset) {
|
|
259
|
+
return ((await storage.findLatestHeadMarker(dataset.deepestConversationId, undefined, BACKGROUND_CONTEXT))?.id ?? -1);
|
|
260
|
+
},
|
|
261
|
+
expected: ({ ancestorHeadEntryId }) => ancestorHeadEntryId,
|
|
262
|
+
},
|
|
263
|
+
];
|
|
264
|
+
/** Seed the common state expected by every write benchmark sample. */
|
|
265
|
+
export async function seedStorageWriteBenchmark(storage) {
|
|
266
|
+
await storage.commit([{ type: "conversation", value: { id: ROOT_CONVERSATION_ID } }], BACKGROUND_CONTEXT);
|
|
267
|
+
await storage.commit(await Promise.all(Array.from({ length: 100 }, async (_, index) => ({
|
|
268
|
+
type: "entry",
|
|
269
|
+
value: {
|
|
270
|
+
id: await storage.mintId(),
|
|
271
|
+
conversationId: ROOT_CONVERSATION_ID,
|
|
272
|
+
kind: "benchmark.baseline",
|
|
273
|
+
data: { index },
|
|
274
|
+
},
|
|
275
|
+
}))), BACKGROUND_CONTEXT);
|
|
276
|
+
}
|
|
277
|
+
export const STORAGE_WRITE_BENCHMARKS = [
|
|
278
|
+
{
|
|
279
|
+
name: "commit one entry",
|
|
280
|
+
expected: 1,
|
|
281
|
+
async run(storage) {
|
|
282
|
+
const id = await storage.mintId();
|
|
283
|
+
await storage.commit([
|
|
284
|
+
{
|
|
285
|
+
type: "entry",
|
|
286
|
+
value: {
|
|
287
|
+
id,
|
|
288
|
+
conversationId: ROOT_CONVERSATION_ID,
|
|
289
|
+
kind: "benchmark.write",
|
|
290
|
+
data: { text: "x".repeat(128) },
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
], BACKGROUND_CONTEXT);
|
|
294
|
+
return 1;
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "commit 100 entries",
|
|
299
|
+
expected: 100,
|
|
300
|
+
async run(storage) {
|
|
301
|
+
const writes = await Promise.all(Array.from({ length: 100 }, async (_, index) => ({
|
|
302
|
+
type: "entry",
|
|
303
|
+
value: {
|
|
304
|
+
id: await storage.mintId(),
|
|
305
|
+
conversationId: ROOT_CONVERSATION_ID,
|
|
306
|
+
kind: "benchmark.write",
|
|
307
|
+
data: { index, text: "x".repeat(128) },
|
|
308
|
+
},
|
|
309
|
+
})));
|
|
310
|
+
await storage.commit(writes, BACKGROUND_CONTEXT);
|
|
311
|
+
return writes.length;
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "commit mixed entry/task/submission/document",
|
|
316
|
+
expected: 4,
|
|
317
|
+
async run(storage) {
|
|
318
|
+
const entryId = await storage.mintId();
|
|
319
|
+
const taskId = await storage.mintId();
|
|
320
|
+
const submissionId = await storage.mintId();
|
|
321
|
+
const documentId = await storage.mintId();
|
|
322
|
+
const writes = [
|
|
323
|
+
{
|
|
324
|
+
type: "entry",
|
|
325
|
+
value: { id: entryId, conversationId: ROOT_CONVERSATION_ID, kind: "benchmark.mixed" },
|
|
326
|
+
},
|
|
327
|
+
{ type: "task", value: task(taskId, taskId) },
|
|
328
|
+
{
|
|
329
|
+
type: "submission",
|
|
330
|
+
value: {
|
|
331
|
+
id: submissionId,
|
|
332
|
+
conversationId: ROOT_CONVERSATION_ID,
|
|
333
|
+
requestId: `benchmark-${submissionId}`,
|
|
334
|
+
type: "write",
|
|
335
|
+
status: "done",
|
|
336
|
+
entry: entryId,
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
type: "document.create",
|
|
341
|
+
record: { id: documentId, kind: "benchmark.mixed", key: String(documentId), scope: { kind: "session" } },
|
|
342
|
+
content: { kind: "base", version: 1, value: { entryId, taskId } },
|
|
343
|
+
},
|
|
344
|
+
];
|
|
345
|
+
await storage.commit(writes, BACKGROUND_CONTEXT);
|
|
346
|
+
return writes.length;
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
//# sourceMappingURL=storage-benchmark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-benchmark.js","sourceRoot":"","sources":["../../src/testing/storage-benchmark.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonValue } from \"@ny4n/chord\";\nimport { BACKGROUND_CONTEXT } from \"@ny4n/chord/context\";\nimport type {\n\tConversationId,\n\tDocumentCreate,\n\tDocumentId,\n\tEntryId,\n\tSeq,\n\tStorage,\n\tStorageWrite,\n\tSubmissionId,\n\tTaskId,\n\tTaskRecord,\n} from \"../types.ts\";\nimport { ROOT_CONVERSATION_ID } from \"../types.ts\";\n\nexport type StorageBenchmarkScale = {\n\treadonly name: string;\n\treadonly entryCount: number;\n\treadonly taskCount: number;\n\treadonly documentCount: number;\n};\n\nexport const STORAGE_MEMORY_SCALES: readonly StorageBenchmarkScale[] = [\n\t{ name: \"1k\", entryCount: 1_000, taskCount: 200, documentCount: 200 },\n\t{ name: \"10k\", entryCount: 10_000, taskCount: 2_000, documentCount: 2_000 },\n];\n\nexport const TIMING_SCALE: StorageBenchmarkScale = {\n\tname: \"timing\",\n\tentryCount: 1_000,\n\ttaskCount: 300,\n\tdocumentCount: 300,\n};\n\nconst REPLAY_TAILS = [0, 16, 128, 1_024] as const;\nconst HISTORY_SEGMENT_LENGTH = 128;\nconst FORK_DEPTH = 8;\nconst ENTRIES_PER_FORK = 32;\nconst BATCH_SIZE = 100;\n\nexport function storageBenchmarkPrimaryRecordCount(scale: StorageBenchmarkScale): number {\n\treturn (\n\t\t1 +\n\t\tscale.entryCount +\n\t\tscale.taskCount +\n\t\tscale.documentCount +\n\t\tREPLAY_TAILS.length +\n\t\t1 +\n\t\tFORK_DEPTH * (1 + ENTRIES_PER_FORK)\n\t);\n}\n\ntype StoredTask = TaskRecord<JsonValue, JsonValue, JsonValue>;\n\nexport type StorageBenchmarkDataset = {\n\treadonly firstEntryId: EntryId;\n\treadonly filteredTaskCount: number;\n\treadonly exactDocumentId: DocumentId;\n\treadonly exactDocumentKey: string;\n\treadonly replayDocumentIds: Readonly<Record<(typeof REPLAY_TAILS)[number], DocumentId>>;\n\treadonly historicalDocumentId: DocumentId;\n\treadonly ancientAt: Seq;\n\treadonly recentAt: Seq;\n\treadonly deepestConversationId: ConversationId;\n\treadonly ancestorHeadEntryId: EntryId;\n};\n\nfunction task(id: TaskId<JsonValue>, index: number): StoredTask {\n\tconst statuses = [\"pending\", \"running\", \"terminal\"] as const;\n\tconst status = statuses[index % statuses.length];\n\tconst common = {\n\t\tid,\n\t\tconversationId: ROOT_CONVERSATION_ID,\n\t\tkind: index % 4 === 0 ? \"benchmark.filtered\" : \"benchmark.other\",\n\t\tversion: 1,\n\t\tinput: { index },\n\t\tbackground: index % 5 === 0,\n\t\tabortRequested: index % 7 === 0,\n\t};\n\tif (status === \"terminal\") {\n\t\treturn { ...common, state: { status, outcome: { status: \"completed\", result: { index } } } };\n\t}\n\treturn { ...common, state: { status, checkpoint: { index, payload: \"x\".repeat(64) } } };\n}\n\n/** Seed deterministic representative data through only the public `Storage` contract. */\nexport async function seedStorageBenchmark(\n\tstorage: Storage,\n\tscale: StorageBenchmarkScale = TIMING_SCALE,\n): Promise<StorageBenchmarkDataset> {\n\tawait storage.commit([{ type: \"conversation\", value: { id: ROOT_CONVERSATION_ID } }], BACKGROUND_CONTEXT);\n\n\tlet firstEntryId: EntryId | undefined;\n\tfor (let start = 0; start < scale.entryCount; start += BATCH_SIZE) {\n\t\tconst writes: StorageWrite[] = [];\n\t\tfor (let index = start; index < Math.min(start + BATCH_SIZE, scale.entryCount); index++) {\n\t\t\tconst id = await storage.mintId<EntryId>();\n\t\t\tif (index === 0) firstEntryId = id;\n\t\t\twrites.push({\n\t\t\t\ttype: \"entry\",\n\t\t\t\tvalue: {\n\t\t\t\t\tid,\n\t\t\t\t\tconversationId: ROOT_CONVERSATION_ID,\n\t\t\t\t\tkind: \"benchmark.entry\",\n\t\t\t\t\t...(index === 0 ? { head: id } : {}),\n\t\t\t\t\tdata: { index, text: `entry-${index}-${\"x\".repeat(96)}` },\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tawait storage.commit(writes, BACKGROUND_CONTEXT);\n\t}\n\n\tfor (let start = 0; start < scale.taskCount; start += BATCH_SIZE) {\n\t\tconst writes: StorageWrite[] = [];\n\t\tfor (let index = start; index < Math.min(start + BATCH_SIZE, scale.taskCount); index++) {\n\t\t\twrites.push({ type: \"task\", value: task(await storage.mintId<TaskId<JsonValue>>(), index) });\n\t\t}\n\t\tawait storage.commit(writes, BACKGROUND_CONTEXT);\n\t}\n\n\tlet exactDocumentId: DocumentId | undefined;\n\tfor (let start = 0; start < scale.documentCount; start += BATCH_SIZE) {\n\t\tconst writes: StorageWrite[] = [];\n\t\tfor (let index = start; index < Math.min(start + BATCH_SIZE, scale.documentCount); index++) {\n\t\t\tconst id = await storage.mintId<DocumentId>();\n\t\t\texactDocumentId = id;\n\t\t\twrites.push({\n\t\t\t\ttype: \"document.create\",\n\t\t\t\trecord: { id, kind: \"benchmark.family\", key: `key-${index}`, scope: { kind: \"session\" } },\n\t\t\t\tcontent: { kind: \"base\", version: 1, value: { index, text: \"x\".repeat(128) } },\n\t\t\t});\n\t\t}\n\t\tawait storage.commit(writes, BACKGROUND_CONTEXT);\n\t}\n\n\tconst replayEntries = await Promise.all(\n\t\tREPLAY_TAILS.map(async (tail) => {\n\t\t\tconst id = await storage.mintId<DocumentId>();\n\t\t\treturn { tail, id };\n\t\t}),\n\t);\n\tawait storage.commit(\n\t\treplayEntries.map(({ tail, id }) => ({\n\t\t\ttype: \"document.create\" as const,\n\t\t\trecord: {\n\t\t\t\tid,\n\t\t\t\tkind: \"benchmark.replay\",\n\t\t\t\tkey: String(tail),\n\t\t\t\tscope: { kind: \"conversation\" as const, conversationId: ROOT_CONVERSATION_ID },\n\t\t\t\thistory: \"rewindable\" as const,\n\t\t\t\tfork: \"asOf\" as const,\n\t\t\t},\n\t\t\tcontent: { kind: \"base\" as const, version: 1, value: { count: 0, text: \"x\".repeat(64) } },\n\t\t})),\n\t\tBACKGROUND_CONTEXT,\n\t);\n\tfor (let count = 1; count <= REPLAY_TAILS.at(-1)!; count++) {\n\t\tawait storage.commit(\n\t\t\treplayEntries\n\t\t\t\t.filter(({ tail }) => count <= tail)\n\t\t\t\t.map(({ id }) => ({\n\t\t\t\t\ttype: \"document.change\" as const,\n\t\t\t\t\tid,\n\t\t\t\t\tcontent: { kind: \"delta\" as const, version: 1, ops: [[\"s\", [\"count\"], count] as const] },\n\t\t\t\t})),\n\t\t\tBACKGROUND_CONTEXT,\n\t\t);\n\t}\n\n\tconst historicalDocumentId = await storage.mintId<DocumentId>();\n\tconst historicalRecord = {\n\t\tid: historicalDocumentId,\n\t\tkind: \"benchmark.history\",\n\t\tscope: { kind: \"conversation\", conversationId: ROOT_CONVERSATION_ID },\n\t\thistory: \"rewindable\",\n\t\tfork: \"asOf\",\n\t} satisfies DocumentCreate;\n\tawait storage.commit(\n\t\t[\n\t\t\t{\n\t\t\t\ttype: \"document.create\",\n\t\t\t\trecord: historicalRecord,\n\t\t\t\tcontent: { kind: \"base\", version: 1, value: { count: 0 } },\n\t\t\t},\n\t\t],\n\t\tBACKGROUND_CONTEXT,\n\t);\n\tlet ancientAt: Seq | undefined;\n\tfor (let count = 1; count <= HISTORY_SEGMENT_LENGTH; count++) {\n\t\tancientAt = await storage.commit(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\ttype: \"document.change\",\n\t\t\t\t\tid: historicalDocumentId,\n\t\t\t\t\tcontent: { kind: \"delta\", version: 1, ops: [[\"s\", [\"count\"], count]] },\n\t\t\t\t},\n\t\t\t],\n\t\t\tBACKGROUND_CONTEXT,\n\t\t);\n\t}\n\tawait storage.commit(\n\t\t[\n\t\t\t{\n\t\t\t\ttype: \"document.change\",\n\t\t\t\tid: historicalDocumentId,\n\t\t\t\tcontent: { kind: \"base\", version: 1, value: { count: HISTORY_SEGMENT_LENGTH } },\n\t\t\t},\n\t\t],\n\t\tBACKGROUND_CONTEXT,\n\t);\n\tif (ancientAt === undefined) throw new Error(\"Benchmark history seed produced no commits\");\n\tlet recentAt = ancientAt;\n\tfor (let count = HISTORY_SEGMENT_LENGTH + 1; count <= HISTORY_SEGMENT_LENGTH * 2; count++) {\n\t\trecentAt = await storage.commit(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\ttype: \"document.change\",\n\t\t\t\t\tid: historicalDocumentId,\n\t\t\t\t\tcontent: { kind: \"delta\", version: 1, ops: [[\"s\", [\"count\"], count]] },\n\t\t\t\t},\n\t\t\t],\n\t\t\tBACKGROUND_CONTEXT,\n\t\t);\n\t}\n\n\tif (firstEntryId === undefined) throw new Error(\"Benchmark scale must create entries\");\n\tlet parentConversationId = ROOT_CONVERSATION_ID;\n\tlet parentAt = firstEntryId;\n\tlet deepestConversationId = ROOT_CONVERSATION_ID;\n\tfor (let depth = 0; depth < FORK_DEPTH; depth++) {\n\t\tconst conversationId = await storage.mintId<ConversationId>();\n\t\tawait storage.commit(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\ttype: \"conversation\",\n\t\t\t\t\tvalue: { id: conversationId, parent: { conversationId: parentConversationId, at: parentAt } },\n\t\t\t\t},\n\t\t\t],\n\t\t\tBACKGROUND_CONTEXT,\n\t\t);\n\t\tconst ids = await Promise.all(Array.from({ length: ENTRIES_PER_FORK }, () => storage.mintId<EntryId>()));\n\t\tawait storage.commit(\n\t\t\tids.map((id, index) => ({\n\t\t\t\ttype: \"entry\" as const,\n\t\t\t\tvalue: {\n\t\t\t\t\tid,\n\t\t\t\t\tconversationId,\n\t\t\t\t\tkind: \"benchmark.fork\",\n\t\t\t\t\tdata: { depth, index },\n\t\t\t\t},\n\t\t\t})),\n\t\t\tBACKGROUND_CONTEXT,\n\t\t);\n\t\tparentConversationId = conversationId;\n\t\tparentAt = ids.at(-1)!;\n\t\tdeepestConversationId = conversationId;\n\t}\n\n\tif (exactDocumentId === undefined) throw new Error(\"Benchmark scale must create documents\");\n\treturn {\n\t\tfirstEntryId,\n\t\tfilteredTaskCount: Math.min(50, Math.ceil(scale.taskCount / 60)),\n\t\texactDocumentId,\n\t\texactDocumentKey: `key-${scale.documentCount - 1}`,\n\t\treplayDocumentIds: Object.fromEntries(replayEntries.map(({ tail, id }) => [tail, id])) as Record<\n\t\t\t(typeof REPLAY_TAILS)[number],\n\t\t\tDocumentId\n\t\t>,\n\t\thistoricalDocumentId,\n\t\tancientAt,\n\t\trecentAt,\n\t\tdeepestConversationId,\n\t\tancestorHeadEntryId: firstEntryId,\n\t};\n}\n\nexport type StorageReadBenchmark = {\n\treadonly name: string;\n\trun(storage: Storage, dataset: StorageBenchmarkDataset): Promise<number>;\n\texpected(dataset: StorageBenchmarkDataset): number;\n};\n\nexport const STORAGE_READ_BENCHMARKS: readonly StorageReadBenchmark[] = [\n\t{\n\t\tname: \"exact entry lookup\",\n\t\tasync run(storage, dataset) {\n\t\t\treturn (await storage.entry(dataset.firstEntryId, BACKGROUND_CONTEXT))?.entry.id ?? -1;\n\t\t},\n\t\texpected: ({ firstEntryId }) => firstEntryId,\n\t},\n\t{\n\t\tname: \"entry page scan (100)\",\n\t\tasync run(storage) {\n\t\t\treturn (\n\t\t\t\tawait storage.scanEntries({ conversationId: ROOT_CONVERSATION_ID }, 100, undefined, BACKGROUND_CONTEXT)\n\t\t\t).items.length;\n\t\t},\n\t\texpected: () => 100,\n\t},\n\t{\n\t\tname: \"filtered task scan (50)\",\n\t\tasync run(storage) {\n\t\t\treturn (\n\t\t\t\tawait storage.scanTasks(\n\t\t\t\t\t{ kind: \"benchmark.filtered\", status: \"pending\", background: true },\n\t\t\t\t\t50,\n\t\t\t\t\tundefined,\n\t\t\t\t\tBACKGROUND_CONTEXT,\n\t\t\t\t)\n\t\t\t).items.length;\n\t\t},\n\t\texpected: ({ filteredTaskCount }) => filteredTaskCount,\n\t},\n\t{\n\t\tname: \"exact document address among many\",\n\t\tasync run(storage, dataset) {\n\t\t\treturn (\n\t\t\t\t(\n\t\t\t\t\tawait storage.findDocument(\n\t\t\t\t\t\t{ kind: \"benchmark.family\", key: dataset.exactDocumentKey, scope: { kind: \"session\" } },\n\t\t\t\t\t\t\"current\",\n\t\t\t\t\t\tBACKGROUND_CONTEXT,\n\t\t\t\t\t)\n\t\t\t\t)?.id ?? -1\n\t\t\t);\n\t\t},\n\t\texpected: ({ exactDocumentId }) => exactDocumentId,\n\t},\n\t...REPLAY_TAILS.map(\n\t\t(tail): StorageReadBenchmark => ({\n\t\t\tname: `document replay tail (${tail})`,\n\t\t\tasync run(storage, dataset) {\n\t\t\t\treturn Number(\n\t\t\t\t\t(await storage.document(dataset.replayDocumentIds[tail], \"current\", BACKGROUND_CONTEXT))?.value.count,\n\t\t\t\t);\n\t\t\t},\n\t\t\texpected: () => tail,\n\t\t}),\n\t),\n\t{\n\t\tname: \"ancient historical read before newer base\",\n\t\tasync run(storage, dataset) {\n\t\t\treturn Number(\n\t\t\t\t(await storage.document(dataset.historicalDocumentId, dataset.ancientAt, BACKGROUND_CONTEXT))?.value.count,\n\t\t\t);\n\t\t},\n\t\texpected: () => HISTORY_SEGMENT_LENGTH,\n\t},\n\t{\n\t\tname: \"recent historical read after newer base\",\n\t\tasync run(storage, dataset) {\n\t\t\treturn Number(\n\t\t\t\t(await storage.document(dataset.historicalDocumentId, dataset.recentAt, BACKGROUND_CONTEXT))?.value.count,\n\t\t\t);\n\t\t},\n\t\texpected: () => HISTORY_SEGMENT_LENGTH * 2,\n\t},\n\t{\n\t\tname: \"fork-depth history scan (100)\",\n\t\tasync run(storage, dataset) {\n\t\t\treturn (\n\t\t\t\tawait storage.scanEntries(\n\t\t\t\t\t{ conversationId: dataset.deepestConversationId },\n\t\t\t\t\t100,\n\t\t\t\t\tundefined,\n\t\t\t\t\tBACKGROUND_CONTEXT,\n\t\t\t\t)\n\t\t\t).items.length;\n\t\t},\n\t\texpected: () => 100,\n\t},\n\t{\n\t\tname: \"fork-depth head lookup\",\n\t\tasync run(storage, dataset) {\n\t\t\treturn (\n\t\t\t\t(await storage.findLatestHeadMarker(dataset.deepestConversationId, undefined, BACKGROUND_CONTEXT))?.id ?? -1\n\t\t\t);\n\t\t},\n\t\texpected: ({ ancestorHeadEntryId }) => ancestorHeadEntryId,\n\t},\n];\n\nexport type StorageWriteBenchmark = {\n\treadonly name: string;\n\treadonly expected: number;\n\trun(storage: Storage): Promise<number>;\n};\n\n/** Seed the common state expected by every write benchmark sample. */\nexport async function seedStorageWriteBenchmark(storage: Storage): Promise<void> {\n\tawait storage.commit([{ type: \"conversation\", value: { id: ROOT_CONVERSATION_ID } }], BACKGROUND_CONTEXT);\n\tawait storage.commit(\n\t\tawait Promise.all(\n\t\t\tArray.from({ length: 100 }, async (_, index) => ({\n\t\t\t\ttype: \"entry\" as const,\n\t\t\t\tvalue: {\n\t\t\t\t\tid: await storage.mintId<EntryId>(),\n\t\t\t\t\tconversationId: ROOT_CONVERSATION_ID,\n\t\t\t\t\tkind: \"benchmark.baseline\",\n\t\t\t\t\tdata: { index },\n\t\t\t\t},\n\t\t\t})),\n\t\t),\n\t\tBACKGROUND_CONTEXT,\n\t);\n}\n\nexport const STORAGE_WRITE_BENCHMARKS: readonly StorageWriteBenchmark[] = [\n\t{\n\t\tname: \"commit one entry\",\n\t\texpected: 1,\n\t\tasync run(storage) {\n\t\t\tconst id = await storage.mintId<EntryId>();\n\t\t\tawait storage.commit(\n\t\t\t\t[\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"entry\",\n\t\t\t\t\t\tvalue: {\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tconversationId: ROOT_CONVERSATION_ID,\n\t\t\t\t\t\t\tkind: \"benchmark.write\",\n\t\t\t\t\t\t\tdata: { text: \"x\".repeat(128) },\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tBACKGROUND_CONTEXT,\n\t\t\t);\n\t\t\treturn 1;\n\t\t},\n\t},\n\t{\n\t\tname: \"commit 100 entries\",\n\t\texpected: 100,\n\t\tasync run(storage) {\n\t\t\tconst writes = await Promise.all(\n\t\t\t\tArray.from(\n\t\t\t\t\t{ length: 100 },\n\t\t\t\t\tasync (_, index): Promise<StorageWrite> => ({\n\t\t\t\t\t\ttype: \"entry\",\n\t\t\t\t\t\tvalue: {\n\t\t\t\t\t\t\tid: await storage.mintId<EntryId>(),\n\t\t\t\t\t\t\tconversationId: ROOT_CONVERSATION_ID,\n\t\t\t\t\t\t\tkind: \"benchmark.write\",\n\t\t\t\t\t\t\tdata: { index, text: \"x\".repeat(128) },\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t);\n\t\t\tawait storage.commit(writes, BACKGROUND_CONTEXT);\n\t\t\treturn writes.length;\n\t\t},\n\t},\n\t{\n\t\tname: \"commit mixed entry/task/submission/document\",\n\t\texpected: 4,\n\t\tasync run(storage) {\n\t\t\tconst entryId = await storage.mintId<EntryId>();\n\t\t\tconst taskId = await storage.mintId<TaskId<JsonValue>>();\n\t\t\tconst submissionId = await storage.mintId<SubmissionId>();\n\t\t\tconst documentId = await storage.mintId<DocumentId>();\n\t\t\tconst writes: readonly StorageWrite[] = [\n\t\t\t\t{\n\t\t\t\t\ttype: \"entry\",\n\t\t\t\t\tvalue: { id: entryId, conversationId: ROOT_CONVERSATION_ID, kind: \"benchmark.mixed\" },\n\t\t\t\t},\n\t\t\t\t{ type: \"task\", value: task(taskId, taskId) },\n\t\t\t\t{\n\t\t\t\t\ttype: \"submission\",\n\t\t\t\t\tvalue: {\n\t\t\t\t\t\tid: submissionId,\n\t\t\t\t\t\tconversationId: ROOT_CONVERSATION_ID,\n\t\t\t\t\t\trequestId: `benchmark-${submissionId}`,\n\t\t\t\t\t\ttype: \"write\",\n\t\t\t\t\t\tstatus: \"done\",\n\t\t\t\t\t\tentry: entryId,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttype: \"document.create\",\n\t\t\t\t\trecord: { id: documentId, kind: \"benchmark.mixed\", key: String(documentId), scope: { kind: \"session\" } },\n\t\t\t\t\tcontent: { kind: \"base\", version: 1, value: { entryId, taskId } },\n\t\t\t\t},\n\t\t\t];\n\t\t\tawait storage.commit(writes, BACKGROUND_CONTEXT);\n\t\t\treturn writes.length;\n\t\t},\n\t},\n];\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { StorageConformanceCase, StorageConformanceOptions } from "./types.ts";
|
|
2
|
+
/** Creates runner-independent cases. `withStorage` must call and await its callback exactly once per case. */
|
|
3
|
+
export declare function createStorageConformance(options: StorageConformanceOptions): readonly StorageConformanceCase[];
|
|
4
|
+
//# sourceMappingURL=storage-conformance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/storage-conformance.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAgC,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAsElH,8GAA8G;AAC9G,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,SAAS,sBAAsB,EAAE,CAgiD9G"}
|