blob: 5df8bd1cd135ed53cc63ce29710b0cb65f07fea9 [file]
/*
* 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.
*/
// Opt-in measurement, not a CI latency assertion. No production instrumentation.
import assert from 'node:assert/strict';
import { fork, execFileSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { lstat, mkdir, mkdtemp, readdir, rm, writeFile } from 'node:fs/promises';
import { cpus, release, tmpdir, totalmem } from 'node:os';
import { join } from 'node:path';
import { setTimeout as sleep } from 'node:timers/promises';
import { FakeBackend } from '@maka/runtime/test-only/fake-backend';
import { SessionManager } from '@maka/runtime/session-manager';
import { resolveStorageRoot, tryAcquireInteractiveRootOwner } from '@maka/storage/root-authority';
import { openInteractiveExecutionStoresForWrite } from '@maka/storage/execution-stores';
import { openInteractiveArtifactStoreForWrite } from '@maka/storage/artifact-stores';
import { openInteractiveRuntimePolicyStoresForWrite } from '@maka/storage/runtime-policy-stores';
import { openFileSessionRepository } from '@maka/storage/file-session-repository';
import { NodeSessionBundleFileService } from '@maka/storage/session-bundle-file-service';
import { localExecutionPersistenceProvider } from '../../storage/dist/local-execution-persistence.js';
import { SnapshotOperationGate } from '../../storage/dist/snapshot-operation-gate.js';
import { createExecutionRuntimeHostComposition } from '../dist/server/execution-composition.js';
import { SessionAdmissionGate } from '../dist/server/session-admission-gate.js';
const MiB = 1024 * 1024;
const profiles = [
{ name: 'small', turns: 8, files: 16, fileBytes: 4096, artifactBytes: 64 * 1024 },
{ name: 'many-files', turns: 64, files: 1000, fileBytes: 4096, artifactBytes: 2 * MiB },
{ name: 'large-payload', turns: 128, files: 8, fileBytes: 8 * MiB, artifactBytes: 16 * MiB },
];
const limits = {
maxCompressedBytes: 256 * MiB,
maxDecompressedTarBytes: 512 * MiB,
maxPayloadBytes: 256 * MiB,
maxFileBytes: 128 * MiB,
maxEntryCount: 10000,
maxManifestBytes: 4 * MiB,
maxStateIdentityBytes: 64 * 1024,
maxPathBytes: 255,
maxPathDepth: 32,
};
const operationContext = {
hostEpoch: 'checkpoint-benchmark',
connectionId: 'benchmark-client',
principal: 'local_os_user',
acquireResidency: () => ({ release() {} }),
};
// Same Host admission/ledger paths without FakeBackend's UI streaming delays.
class BenchmarkBackend extends FakeBackend {
async *send(input) {
const common = { turnId: input.turnId, ts: Date.now() };
yield {
...common,
type: 'text_complete',
id: randomUUID(),
messageId: randomUUID(),
text: input.text,
};
yield { ...common, type: 'complete', id: randomUUID(), stopReason: 'end_turn' };
}
}
if (process.argv[2] === '--worker') {
const profile = profiles.find((item) => item.name === process.argv[3]);
assert.ok(profile, 'Unknown benchmark profile');
const result = await runFixture(profile, process.argv[4] === 'resource');
process.send(result);
process.disconnect();
} else {
const smoke = process.argv.includes('--smoke');
assert.ok(
process.argv.slice(2).every((arg) => arg === '--smoke'),
'Unknown argument',
);
const report = {
schemaVersion: 1,
measuredAt: new Date().toISOString(),
revision: execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(),
harnessSha256: createHash('sha256')
.update(readFileSync(new URL(import.meta.url)))
.digest('hex'),
environment: {
node: process.version,
platform: process.platform,
arch: process.arch,
kernel: release(),
cpu: cpus()[0].model,
memoryBytes: totalmem(),
},
smoke,
samplesPerProfile: smoke ? 1 : 5,
profiles: [],
};
for (const profile of smoke ? profiles.slice(0, 1) : profiles) {
// Fresh processes prevent previous profiles' RSS high-water from leaking.
// One whole fixture warmup is discarded; OS caches are not flushed.
await runChild(profile.name, 'timing');
const samples = [];
for (let index = 0; index < report.samplesPerProfile; index++) {
process.stderr.write('Measuring ' + profile.name + ' ' + (index + 1) + '\n');
samples.push(await runChild(profile.name, 'timing'));
}
// Directory scans perturb I/O; keep them out of the latency samples.
const resources = await runChild(profile.name, 'resource');
report.profiles.push({ ...profile, samples, resources });
}
console.log(JSON.stringify(report, null, 2));
}
async function runChild(profile, mode) {
return new Promise((resolve, reject) => {
const child = fork(new URL(import.meta.url), ['--worker', profile, mode], {
execArgv: ['--expose-gc'],
stdio: ['ignore', 'ignore', 'inherit', 'ipc'],
timeout: 180000,
});
let result;
child.on('message', (message) => {
result = message;
});
child.on('error', reject);
child.on('exit', (code, signal) => {
if (code !== 0 || !result) reject(new Error('Benchmark failed: ' + code + '/' + signal));
else resolve(result);
});
});
}
async function runFixture(profile, resourceMode) {
// No configurable path: deletion is restricted to this generated fixture.
const root = await mkdtemp(join(tmpdir(), 'maka-checkpoint-benchmark-'));
const workspace = join(root, 'workspace');
await mkdir(workspace);
const owner = await tryAcquireInteractiveRootOwner(
await resolveStorageRoot({ path: join(root, 'state'), kind: 'interactive' }),
);
assert.ok(owner);
const checkpointRoot = join(root, 'checkpoints');
let composition;
let manager;
let active;
let fenceHeld = false;
let stopDisk;
const restores = [];
// Benchmark-only wrappers always call the real method, preserving errors.
function wrap(object, name, wrapper) {
const original = object[name];
assert.equal(typeof original, 'function', name);
object[name] = wrapper(original);
restores.push(() => {
object[name] = original;
});
}
async function timed(name, operation) {
const sample = active;
if (!sample) return operation();
const start = performance.now();
try {
return await operation();
} finally {
sample[name] = (sample[name] ?? 0) + performance.now() - start;
}
}
async function diskSample() {
if (!resourceMode || !active) return;
const sample = active;
const usage = await directoryUsage(checkpointRoot);
sample.peakCheckpointLogicalBytes = Math.max(sample.peakCheckpointLogicalBytes, usage.bytes);
sample.peakCheckpointAllocatedBytes = Math.max(
sample.peakCheckpointAllocatedBytes,
usage.allocatedBytes,
);
}
try {
const policyStores = await openInteractiveRuntimePolicyStoresForWrite(owner.lease);
const policy = await policyStores.runtimePolicy.getSnapshot();
const changed = await policyStores.runtimePolicy.mutate({
expectedRevision: policy.revision,
operation: { kind: 'set_privacy', value: { incognitoActive: true } },
});
assert.equal(changed.kind, 'committed');
wrap(
SessionManager.prototype,
'recoverInterruptedSessionsStrict',
(original) =>
async function (...args) {
manager = this;
return original.apply(this, args);
},
);
wrap(
SessionAdmissionGate.prototype,
'run',
(original) =>
function (id, operation) {
return original.call(this, id, (lease) =>
timed('admissionHeldMs', () => operation(lease)),
);
},
);
wrap(
SnapshotOperationGate.prototype,
'exclusive',
(original) =>
function (operation) {
// Includes drain time, when the barrier already blocks new writes.
return timed('providerFenceMs', () =>
original.call(this, async () => {
fenceHeld = true;
try {
return await operation();
} finally {
fenceHeld = false;
}
}),
);
},
);
wrap(
NodeSessionBundleFileService.prototype,
'pack',
(original) =>
async function (...args) {
assert.equal(fenceHeld, false, 'Packing must run after the live-state fence is released');
const result = await timed('packMs', () => original.apply(this, args));
// Capture the private-copy + archive coexistence point before cleanup.
await diskSample();
return result;
},
);
const repository = await openFileSessionRepository({
storageRoot: join(checkpointRoot, 'repository'),
});
for (const name of ['createSession', 'commit', 'checkoutCurrent']) {
wrap(
Object.getPrototypeOf(repository),
name,
(original) =>
function (...args) {
return timed(name + 'Ms', () => original.apply(this, args));
},
);
}
wrap(
Object.getPrototypeOf(repository.objectStore),
'publish',
(original) =>
async function (...args) {
const result = await timed('objectPublishMs', () => original.apply(this, args));
await diskSample();
return result;
},
);
// Observe the actual selected Local provider, not a replacement snapshot.
const provider = {
async open(input) {
const persistence = await localExecutionPersistenceProvider.open(input);
const original = persistence.createSnapshotStatePreparer.bind(persistence);
return {
...persistence,
createSnapshotStatePreparer(lease) {
const state = original(lease);
return {
prepareState: (request) => timed('stateCopyMs', () => state.prepareState(request)),
};
},
};
},
};
composition = await createExecutionRuntimeHostComposition(
{
owner,
hostEpoch: operationContext.hostEpoch,
acquireResidency: () => ({ release() {} }),
retainUntilProcessExit: () => {},
requestDrain: () => {},
},
{ bootstrapRuntimePolicy: false },
{
primaryBackendFactory: (context) => new BenchmarkBackend(context),
executionPersistenceProvider: provider,
checkpointPublication: {
directory: checkpointRoot,
limits,
workspace: {
// Private fixture, sole writer; NOT an arbitrary-directory adapter.
runExclusive: (_input, operation) => timed('workspaceFenceMs', operation),
},
privateStagingRootAuthority:
process.platform === 'win32'
? { verifyPrivateStagingRoot: async ({ canonicalPath }) => ({ canonicalPath }) }
: undefined,
},
},
);
await composition.recover();
assert.ok(manager);
const session = await manager.createSession({
cwd: workspace,
llmConnectionId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
llmConnectionSlug: 'fake',
model: 'fake-model',
permissionMode: 'bypass',
name: profile.name,
});
for (let index = 0; index < profile.turns; index++) {
const turnId = 'turn-' + index;
const started = await composition.handlers['turn.start'](
{
sessionId: session.id,
turnId,
content: { text: 'benchmark ' + index + ' ' + 'x'.repeat(1024) },
},
operationContext,
);
assert.equal(started.ok, true);
const deadline = performance.now() + 10000;
while (true) {
const messages = await manager.getMessages(session.id);
const state = messages.findLast(
(message) => message.type === 'turn_state' && message.turnId === turnId,
);
if (state?.status === 'completed') break;
assert.ok(state?.status !== 'failed' && state?.status !== 'aborted');
assert.ok(performance.now() < deadline, 'Turn did not settle');
await sleep(5);
}
}
const stores = await openInteractiveExecutionStoresForWrite(owner.lease, provider);
const artifacts = await openInteractiveArtifactStoreForWrite(owner.lease);
await artifacts.create({
sessionId: session.id,
turnId: 'turn-0',
kind: 'file',
source: 'user_upload',
name: 'payload.bin',
content: payload(profile.artifactBytes, 999),
});
for (let index = 0; index < profile.files; index++) {
await writeFile(join(workspace, 'file-' + index + '.bin'), payload(profile.fileBytes, index));
}
const sourceUsage = await directoryUsage(root);
const messages = await manager.getMessages(session.id);
const runs = await manager.listInvocations(session.id);
const source = {
logicalBytes: sourceUsage.bytes,
allocatedBytes: sourceUsage.allocatedBytes,
messages: messages.length,
runs: runs.length,
};
assert.equal(runs.length, profile.turns);
const results = {};
let previous;
for (const operation of ['create', 'commit', 'replay']) {
if (operation === 'commit') {
// Force a new snapshot, not just immutable-object deduplication.
await writeFile(join(workspace, 'after-first-checkpoint.txt'), 'new live state');
}
global.gc?.();
const before = await directoryUsage(checkpointRoot);
active = {
peakCheckpointLogicalBytes: before.bytes,
peakCheckpointAllocatedBytes: before.allocatedBytes,
};
const sample = active;
sample.rssBeforeBytes = process.memoryUsage.rss();
sample.sampledPeakRssBytes = sample.rssBeforeBytes;
sample.processHighWaterBeforeBytes = process.resourceUsage().maxRSS * 1024;
const rssTimer = setInterval(() => {
sample.sampledPeakRssBytes = Math.max(
sample.sampledPeakRssBytes,
process.memoryUsage.rss(),
);
}, 5);
let diskRunning = true;
let diskError;
stopDisk = (async () => {
if (!resourceMode) return;
while (diskRunning) {
await diskSample();
await sleep(20);
}
})().catch((error) => {
diskError = error;
});
const started = performance.now();
let result;
try {
result = await composition.sessionCheckpoints.publish({
sessionId: session.id,
requestId: operation === 'create' ? 'first' : 'second',
});
sample.totalMs = performance.now() - started;
} finally {
clearInterval(rssTimer);
diskRunning = false;
await stopDisk;
}
if (diskError) throw diskError;
sample.processHighWaterAfterBytes = process.resourceUsage().maxRSS * 1024;
sample.sampledPeakRssBytes = Math.max(sample.sampledPeakRssBytes, process.memoryUsage.rss());
await diskSample();
const after = await directoryUsage(checkpointRoot);
sample.retainedCheckpointLogicalBytes = after.bytes;
sample.retainedCheckpointAllocatedBytes = after.allocatedBytes;
sample.checkpointBytesBefore = before.bytes;
sample.archiveBytes = result.committed.checkpoint.value.compatibilityBundle.bytes;
assert.equal(result.committed.ref.revision, operation === 'create' ? 'r1' : 'r2');
assert.equal(result.stagingCleanup, 'released');
assert.equal(result.bundleCleanup, 'released');
if (operation === 'replay') {
assert.deepEqual(result, previous);
assert.equal(sample.providerFenceMs, undefined);
assert.equal(sample.packMs, undefined);
} else {
assert.ok(sample.providerFenceMs > 0 && sample.stateCopyMs > 0 && sample.packMs > 0);
assert.ok(sample[operation === 'create' ? 'createSessionMs' : 'commitMs'] > 0);
}
previous = result;
results[operation] = sample;
active = undefined;
}
// Verify bounded cleanup, including all temporary archive copies.
assert.deepEqual(await readdir(join(checkpointRoot, 'bundles')), []);
assert.deepEqual(await readdir(join(checkpointRoot, 'staging')), []);
assert.equal((await stores.sessionStore.readHeader(session.id)).cwd, workspace);
return { resourceMode, source, results };
} finally {
await stopDisk;
for (const restore of restores.reverse()) restore();
composition?.beginDrain();
await composition?.close();
await owner.close();
await rm(owner.controlDirectory, { recursive: true, force: true });
await rm(root, { recursive: true, force: true });
}
}
function payload(bytes, seed) {
// Deterministic high-entropy blocks; avoid misleading all-zero compression.
const buffer = Buffer.allocUnsafe(bytes);
for (let offset = 0; offset < bytes; offset += 32) {
createHash('sha256')
.update(seed + ':' + offset)
.digest()
.copy(buffer, offset);
}
return buffer;
}
async function directoryUsage(root) {
let bytes = 0;
let allocatedBytes = 0;
const seen = new Set();
async function visit(path) {
let info;
try {
info = await lstat(path);
} catch (error) {
if (error.code === 'ENOENT') return; // Concurrent owned temporary cleanup.
throw error;
}
if (info.isDirectory()) {
let children;
try {
children = await readdir(path);
} catch (error) {
if (error.code === 'ENOENT') return;
throw error;
}
for (const child of children) await visit(join(path, child));
} else {
assert.ok(info.isFile(), 'Benchmark never follows symbolic links');
const identity = info.dev + ':' + info.ino;
if (seen.has(identity)) return; // Publication uses temporary hard links.
seen.add(identity);
bytes += info.size;
allocatedBytes += info.blocks * 512;
}
}
await visit(root);
return { bytes, allocatedBytes };
}