mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 14:51:40 +00:00
187 lines
5.6 KiB
TypeScript
187 lines
5.6 KiB
TypeScript
// Benchmarks generation-aware raw transcript reads against a 100k-event session.
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import {
|
|
loadTranscriptEventsSync,
|
|
readTranscriptRawDelta,
|
|
} from "../src/config/sessions/session-accessor.js";
|
|
import {
|
|
closeOpenClawAgentDatabasesForTest,
|
|
openOpenClawAgentDatabase,
|
|
} from "../src/state/openclaw-agent-db.js";
|
|
import { closeOpenClawStateDatabaseForTest } from "../src/state/openclaw-state-db.js";
|
|
|
|
const EVENT_COUNT = 100_000;
|
|
const DELTA_COUNT = 20;
|
|
const RUNS = 12;
|
|
|
|
type Timing = {
|
|
p50Ms: number;
|
|
p95Ms: number;
|
|
};
|
|
|
|
function percentile(values: readonly number[], fraction: number): number {
|
|
const sorted = values.toSorted((left, right) => left - right);
|
|
const index = Math.min(sorted.length - 1, Math.floor(sorted.length * fraction));
|
|
return Number((sorted[index] ?? 0).toFixed(3));
|
|
}
|
|
|
|
function measure(operation: () => void): Timing {
|
|
const values: number[] = [];
|
|
for (let index = 0; index < RUNS; index += 1) {
|
|
const startedAt = performance.now();
|
|
operation();
|
|
values.push(performance.now() - startedAt);
|
|
}
|
|
return { p50Ms: percentile(values, 0.5), p95Ms: percentile(values, 0.95) };
|
|
}
|
|
|
|
function seedTranscript(
|
|
database: ReturnType<typeof openOpenClawAgentDatabase>,
|
|
sessionId: string,
|
|
sessionKey: string,
|
|
): void {
|
|
database.db.exec("BEGIN IMMEDIATE");
|
|
try {
|
|
const now = Date.now();
|
|
database.db
|
|
.prepare(
|
|
`INSERT INTO sessions (session_id, session_key, session_scope, created_at, updated_at)
|
|
VALUES (?, ?, 'conversation', ?, ?)`,
|
|
)
|
|
.run(sessionId, sessionKey, now, now);
|
|
database.db
|
|
.prepare(
|
|
`INSERT INTO session_transcript_generations (session_id, generation, updated_at)
|
|
VALUES (?, 'benchmark-generation', ?)`,
|
|
)
|
|
.run(sessionId, now);
|
|
const insert = database.db.prepare(
|
|
`INSERT INTO transcript_events (session_id, seq, event_json, created_at)
|
|
VALUES (?, ?, ?, ?)`,
|
|
);
|
|
for (let seq = 0; seq < EVENT_COUNT; seq += 1) {
|
|
insert.run(
|
|
sessionId,
|
|
seq,
|
|
JSON.stringify({ id: `event-${seq}`, text: "x".repeat(64), type: "custom" }),
|
|
now + seq,
|
|
);
|
|
}
|
|
database.db.exec("COMMIT");
|
|
} catch (error) {
|
|
database.db.exec("ROLLBACK");
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function appendDelta(
|
|
database: ReturnType<typeof openOpenClawAgentDatabase>,
|
|
sessionId: string,
|
|
): void {
|
|
const insert = database.db.prepare(
|
|
`INSERT INTO transcript_events (session_id, seq, event_json, created_at)
|
|
VALUES (?, ?, ?, ?)`,
|
|
);
|
|
database.db.exec("BEGIN IMMEDIATE");
|
|
try {
|
|
for (let offset = 0; offset < DELTA_COUNT; offset += 1) {
|
|
const seq = EVENT_COUNT + offset;
|
|
insert.run(
|
|
sessionId,
|
|
seq,
|
|
JSON.stringify({ id: `delta-${offset}`, text: "delta", type: "custom" }),
|
|
Date.now() + offset,
|
|
);
|
|
}
|
|
database.db.exec("COMMIT");
|
|
} catch (error) {
|
|
database.db.exec("ROLLBACK");
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function readFrontierCursor(scope: Parameters<typeof readTranscriptRawDelta>[0]): string {
|
|
let cursor: string | undefined;
|
|
for (;;) {
|
|
const page = readTranscriptRawDelta(scope, {
|
|
...(cursor ? { cursor } : {}),
|
|
maxBytes: 64 * 1024 * 1024,
|
|
maxEvents: 10_000,
|
|
});
|
|
if (page.kind !== "page") {
|
|
throw new Error(`unexpected cursor bootstrap result: ${page.kind}`);
|
|
}
|
|
cursor = page.cursor;
|
|
if (!page.hasMore) {
|
|
return cursor;
|
|
}
|
|
}
|
|
}
|
|
|
|
function main(): void {
|
|
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-transcript-bench-"));
|
|
const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir };
|
|
const agentId = "benchmark";
|
|
const sessionId = "cursor-benchmark";
|
|
const sessionKey = `agent:${agentId}:cursor-benchmark`;
|
|
try {
|
|
const database = openOpenClawAgentDatabase({ agentId, env });
|
|
seedTranscript(database, sessionId, sessionKey);
|
|
const scope = { agentId, sessionId, sessionKey, storePath: database.path };
|
|
|
|
const bootstrap = measure(() => {
|
|
const page = readTranscriptRawDelta(scope, { maxBytes: 1_000_000, maxEvents: 1_000 });
|
|
if (page.kind !== "page" || page.events.length !== 1_000) {
|
|
throw new Error("raw bootstrap did not return 1,000 events");
|
|
}
|
|
});
|
|
const activeMemoryBounded = measure(() => {
|
|
const page = readTranscriptRawDelta(scope, {
|
|
maxBytes: 50 * 1024 * 1024,
|
|
maxEvents: 2_000,
|
|
});
|
|
if (page.kind !== "page" || page.events.length !== 2_000) {
|
|
throw new Error("active-memory bounds did not return 2,000 events");
|
|
}
|
|
});
|
|
const frontierCursor = readFrontierCursor(scope);
|
|
appendDelta(database, sessionId);
|
|
const shortDelta = measure(() => {
|
|
const page = readTranscriptRawDelta(scope, {
|
|
cursor: frontierCursor,
|
|
maxBytes: 1_000_000,
|
|
maxEvents: 100,
|
|
});
|
|
if (page.kind !== "page" || page.events.length !== DELTA_COUNT || page.hasMore) {
|
|
throw new Error("raw delta did not return the appended frontier");
|
|
}
|
|
});
|
|
const fullRead = measure(() => {
|
|
if (loadTranscriptEventsSync(scope).length !== EVENT_COUNT + DELTA_COUNT) {
|
|
throw new Error("full transcript read returned the wrong row count");
|
|
}
|
|
});
|
|
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
events: EVENT_COUNT,
|
|
appendedEvents: DELTA_COUNT,
|
|
runs: RUNS,
|
|
timings: { activeMemoryBounded, bootstrap, fullRead, shortDelta },
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
} finally {
|
|
closeOpenClawAgentDatabasesForTest();
|
|
closeOpenClawStateDatabaseForTest();
|
|
fs.rmSync(stateDir, { force: true, recursive: true });
|
|
}
|
|
}
|
|
|
|
main();
|