Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
179 lines
6.1 KiB
JavaScript
179 lines
6.1 KiB
JavaScript
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
import { performance } from 'node:perf_hooks';
|
|
import { mkdtemp, rm } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { createSessionStore } from '@maka/storage/session-store';
|
|
import { ClientSessionSubscription } from '../dist/client/session-subscription.js';
|
|
import { SESSION_CONTINUITY_SCHEMA_VERSION } from '../dist/protocol/index.js';
|
|
import {
|
|
createSessionTranscriptBootstrap,
|
|
readSessionTranscriptPage,
|
|
} from '../dist/server/session-transcript-pager.js';
|
|
|
|
const BOOTSTRAP_BYTES = 16 * 1024;
|
|
const RTT_MS = Number.parseInt(process.env.MAKA_TRANSCRIPT_BENCHMARK_RTT_MS ?? '20', 10);
|
|
const cases = [
|
|
{ name: '5k-small-messages', messages: 5_000, textBytes: 96 },
|
|
{ name: '15MiB-single-message', messages: 1, textBytes: 15 * 1024 * 1024 },
|
|
{ name: '17MiB-transcript', totalBytes: 17 * 1024 * 1024, textBytes: 4 * 1024 },
|
|
{ name: '64MiB-transcript', totalBytes: 64 * 1024 * 1024, textBytes: 4 * 1024 },
|
|
];
|
|
|
|
const results = [];
|
|
for (const fixture of cases) results.push(await runFixture(fixture));
|
|
console.table(results);
|
|
|
|
async function runFixture(fixture) {
|
|
const messages = buildMessages(fixture);
|
|
const root = await mkdtemp(join(tmpdir(), 'maka-transcript-benchmark-'));
|
|
const store = createSessionStore(root);
|
|
const setupAt = performance.now();
|
|
const session = await store.create({
|
|
cwd: root,
|
|
backend: 'fake',
|
|
llmConnectionSlug: 'fake',
|
|
model: 'fake-model',
|
|
permissionMode: 'ask',
|
|
name: fixture.name,
|
|
labels: [],
|
|
});
|
|
await store.appendMessages(session.id, messages);
|
|
const setupMs = performance.now() - setupAt;
|
|
const reader = sqliteReader(store);
|
|
try {
|
|
const openedAt = performance.now();
|
|
const throughSequence = await reader.readDurableHighWater(session.id);
|
|
const { bootstrap, state } = await createSessionTranscriptBootstrap({
|
|
reader,
|
|
sessionId: session.id,
|
|
subscriptionId: `benchmark-${fixture.name}`,
|
|
throughSequence,
|
|
rootTurn: null,
|
|
activeAssistantStreams: [],
|
|
maxBytes: BOOTSTRAP_BYTES,
|
|
});
|
|
const bootstrapCpuMs = performance.now() - openedAt;
|
|
let pageRequests = 0;
|
|
let transferredRawBytes = bootstrap.durable.rawBytes + bootstrap.overlay.rawBytes;
|
|
const subscription = new ClientSessionSubscription(
|
|
{
|
|
hostEpoch: 'benchmark-host',
|
|
subscriptionId: state.subscriptionId,
|
|
nextSequence: 1,
|
|
activeAssistantStreams: [],
|
|
transcript: bootstrap,
|
|
snapshot: {
|
|
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
|
|
session: {
|
|
sessionId: state.sessionId,
|
|
metadataRevision: 1,
|
|
status: 'active',
|
|
createdAt: 1,
|
|
isArchived: false,
|
|
},
|
|
projectionRevision: 1,
|
|
rootTurn: null,
|
|
goal: null,
|
|
queue: {
|
|
hostEpoch: 'benchmark-host',
|
|
queueRevision: 0,
|
|
steering: [],
|
|
followup: [],
|
|
},
|
|
interactions: { pending: [] },
|
|
},
|
|
},
|
|
async () => undefined,
|
|
async (request) => {
|
|
pageRequests += 1;
|
|
const page = await readSessionTranscriptPage({ reader, state, request });
|
|
transferredRawBytes += page.rawBytes;
|
|
return page;
|
|
},
|
|
);
|
|
const materializeAt = performance.now();
|
|
const materialized = await subscription.loadTranscript((value) => value);
|
|
const materializeCpuMs = performance.now() - materializeAt;
|
|
if (materialized.length !== messages.length) {
|
|
throw new Error(
|
|
`${fixture.name} materialized ${materialized.length}/${messages.length} messages`,
|
|
);
|
|
}
|
|
const wireRequests = 1 + pageRequests;
|
|
return {
|
|
fixture: fixture.name,
|
|
messages: messages.length,
|
|
rawMiB: decimalMiB(transferredRawBytes),
|
|
bootstrapKiB: decimalKiB(bootstrap.durable.rawBytes + bootstrap.overlay.rawBytes),
|
|
wireRequests,
|
|
pageRequests,
|
|
setupMs: setupMs.toFixed(1),
|
|
bootstrapCpuMs: bootstrapCpuMs.toFixed(1),
|
|
materializeCpuMs: materializeCpuMs.toFixed(1),
|
|
modeledRttFloorMs: wireRequests * RTT_MS,
|
|
};
|
|
} finally {
|
|
await store.close?.();
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
function buildMessages(fixture) {
|
|
const messages = [];
|
|
let encodedBytes = 0;
|
|
const targetCount = fixture.messages ?? Number.POSITIVE_INFINITY;
|
|
while (
|
|
messages.length < targetCount &&
|
|
(fixture.totalBytes === undefined || encodedBytes < fixture.totalBytes)
|
|
) {
|
|
const index = messages.length;
|
|
const message = {
|
|
type: 'user',
|
|
id: `message-${index}`,
|
|
turnId: `turn-${index}`,
|
|
ts: index + 1,
|
|
text: 'x'.repeat(fixture.textBytes),
|
|
};
|
|
messages.push(message);
|
|
encodedBytes += Buffer.byteLength(JSON.stringify(message), 'utf8');
|
|
}
|
|
return messages;
|
|
}
|
|
|
|
function sqliteReader(store) {
|
|
return {
|
|
readDurableHighWater: (sessionId) => store.readTranscriptHighWaterSnapshot(sessionId),
|
|
readDurablePage: (sessionId, request) => store.readTranscriptPageSnapshot(sessionId, request),
|
|
readDurableMessagesById: (sessionId, messageIds, throughSequence) =>
|
|
store.readTranscriptMessagesSnapshot(sessionId, messageIds, throughSequence),
|
|
readActiveOverlay: async () => [],
|
|
};
|
|
}
|
|
|
|
function decimalMiB(bytes) {
|
|
return (bytes / (1024 * 1024)).toFixed(2);
|
|
}
|
|
|
|
function decimalKiB(bytes) {
|
|
return (bytes / 1024).toFixed(2);
|
|
}
|