From 0bddd620d68249712743146b41275331cefeec9d Mon Sep 17 00:00:00 2001 From: Vegard Stikbakke Date: Fri, 14 Aug 2026 12:50:26 +0200 Subject: [PATCH] feat(agent): benchmark session repository lifecycle --- .../benchmark/session/session-repo.bench.ts | 15 +++++ .../session/testing/benchmark/session-repo.ts | 60 ++++++++++++++++++- .../src/harness/session/testing/index.ts | 1 + 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/packages/agent/benchmark/session/session-repo.bench.ts b/packages/agent/benchmark/session/session-repo.bench.ts index c8fc65e89..ceef46719 100644 --- a/packages/agent/benchmark/session/session-repo.bench.ts +++ b/packages/agent/benchmark/session/session-repo.bench.ts @@ -1,6 +1,7 @@ import { SESSION_REPO_CATALOG_BENCHMARK_DATASETS, SESSION_REPO_CATALOG_READ_BENCHMARK_SCENARIOS, + SESSION_REPO_CATALOG_WRITE_BENCHMARK_SCENARIOS, SESSION_REPO_FORK_BENCHMARK_DATASETS, SESSION_REPO_FORK_WRITE_BENCHMARK_SCENARIOS, seedSessionRepoCatalogBenchmark, @@ -21,6 +22,20 @@ await registerReadBenchmarks({ }, }); +await registerWriteBenchmarks({ + targets: sessionRepoBenchmarkTargets, + scenarios: SESSION_REPO_CATALOG_WRITE_BENCHMARK_SCENARIOS, + prepare(fixture, scenario) { + return scenario.prepare(fixture.repo); + }, + expectedResult(scenario) { + return scenario.expectedResult; + }, + run(operation) { + return operation.run(); + }, +}); + const forkBenchmarks = SESSION_REPO_FORK_BENCHMARK_DATASETS.flatMap((dataset) => SESSION_REPO_FORK_WRITE_BENCHMARK_SCENARIOS.map((scenario) => ({ name: `${scenario.name} (${dataset.name})`, diff --git a/packages/agent/src/harness/session/testing/benchmark/session-repo.ts b/packages/agent/src/harness/session/testing/benchmark/session-repo.ts index e495b3c96..9b89d03e5 100644 --- a/packages/agent/src/harness/session/testing/benchmark/session-repo.ts +++ b/packages/agent/src/harness/session/testing/benchmark/session-repo.ts @@ -13,6 +13,16 @@ interface SessionRepoCatalogReadBenchmarkScenario { run(repo: SessionRepo): Promise; } +interface SessionRepoCatalogWriteBenchmarkOperation { + run(): Promise; +} + +interface SessionRepoCatalogWriteBenchmarkScenario { + readonly name: string; + readonly expectedResult: number; + prepare(repo: SessionRepo): Promise; +} + interface SessionRepoForkWriteBenchmarkScenario { readonly name: string; expectedResult(dataset: StorageBenchmarkDataset): number; @@ -24,7 +34,7 @@ export function sessionRepoBenchmarkSessionId(index: number): string { return `benchmark-session-${index.toString().padStart(8, "0")}`; } -const FORK_SOURCE_SESSION_ID = sessionRepoBenchmarkSessionId(0); +const BENCHMARK_SESSION_ID = sessionRepoBenchmarkSessionId(0); const FORK_DESTINATION_SESSION_ID = sessionRepoBenchmarkSessionId(1); function createCatalogDataset(scale: string, sessionCount: number): SessionRepoCatalogBenchmarkDataset { @@ -68,6 +78,52 @@ export const SESSION_REPO_CATALOG_READ_BENCHMARK_SCENARIOS: readonly SessionRepo }, ]; +/** Shared catalog writes. Every invocation receives an equivalent independently prepared repository. */ +export const SESSION_REPO_CATALOG_WRITE_BENCHMARK_SCENARIOS: readonly SessionRepoCatalogWriteBenchmarkScenario[] = [ + { + name: "create empty session", + expectedResult: 1, + prepare(repo) { + return Promise.resolve({ + async run() { + const session = await repo.create({ id: BENCHMARK_SESSION_ID }); + return session.metadata.id === BENCHMARK_SESSION_ID ? 1 : 0; + }, + }); + }, + }, + { + name: "open closed empty session", + expectedResult: 1, + async prepare(repo) { + const session = await repo.create({ id: BENCHMARK_SESSION_ID }); + const { metadata } = session; + await session.close(); + return { + async run() { + const reopened = await repo.open(metadata); + return reopened.metadata.id === metadata.id ? 1 : 0; + }, + }; + }, + }, + { + name: "delete closed empty session", + expectedResult: 1, + async prepare(repo) { + const session = await repo.create({ id: BENCHMARK_SESSION_ID }); + const { metadata } = session; + await session.close(); + return { + async run() { + await repo.delete(metadata); + return 1; + }, + }; + }, + }, +]; + /** Initial fork timing uses a bounded source because each iteration owns an equivalent seeded repository. */ export const SESSION_REPO_FORK_BENCHMARK_DATASETS: readonly StorageBenchmarkDataset[] = [ STORAGE_BENCHMARK_DATASETS[0]!, @@ -79,7 +135,7 @@ export async function seedSessionRepoForkBenchmark( repo: SessionRepo, dataset: StorageBenchmarkDataset, ): Promise { - const session = await repo.create({ id: FORK_SOURCE_SESSION_ID }); + const session = await repo.create({ id: BENCHMARK_SESSION_ID }); for (const transaction of generateStorageBenchmarkSeedTransactions(dataset)) { await session.mutate("main", (mutator) => mutator.commit(transaction)); } diff --git a/packages/agent/src/harness/session/testing/index.ts b/packages/agent/src/harness/session/testing/index.ts index 28143a871..8128bc879 100644 --- a/packages/agent/src/harness/session/testing/index.ts +++ b/packages/agent/src/harness/session/testing/index.ts @@ -2,6 +2,7 @@ export { STORAGE_BENCHMARK_DATASETS } from "./benchmark/datasets.ts"; export { SESSION_REPO_CATALOG_BENCHMARK_DATASETS, SESSION_REPO_CATALOG_READ_BENCHMARK_SCENARIOS, + SESSION_REPO_CATALOG_WRITE_BENCHMARK_SCENARIOS, SESSION_REPO_FORK_BENCHMARK_DATASETS, SESSION_REPO_FORK_WRITE_BENCHMARK_SCENARIOS, type SessionRepoCatalogBenchmarkDataset,