Initial commit
This commit is contained in:
263
tests/performance.test.ts
Normal file
263
tests/performance.test.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* performance.test.ts — Performance tests for lock operations.
|
||||
*
|
||||
* Tests measure latency of:
|
||||
* - Single lock acquisition (median, p95)
|
||||
* - Single lock release
|
||||
* - Conflict checking
|
||||
* - Bulk acquire/release
|
||||
* - Lock info retrieval
|
||||
* - Registry operation cycle
|
||||
*
|
||||
* Thresholds define acceptable performance bounds.
|
||||
*
|
||||
* @module file-claiming/performance.test
|
||||
*/
|
||||
|
||||
import { performance } from "node:perf_hooks";
|
||||
import {
|
||||
assert,
|
||||
mockOwner,
|
||||
TEST_FILE_A,
|
||||
} from "./test-utils.ts";
|
||||
|
||||
// Performance thresholds (ms)
|
||||
const THRESHOLDS = {
|
||||
lockAcquisition: 10,
|
||||
lockRelease: 5,
|
||||
conflictCheck: 5,
|
||||
bulkAcquire: 100,
|
||||
bulkRelease: 50,
|
||||
lockInfo: 5,
|
||||
registryOperation: 10,
|
||||
conflictResolution: 5,
|
||||
cleanup: 20,
|
||||
minThroughputPerMs: 0.01,
|
||||
};
|
||||
|
||||
// Lazy module loading
|
||||
let _acq: any = null;
|
||||
let _reg: any = null;
|
||||
let _cfg: any = null;
|
||||
|
||||
function getAcq() { if (!_acq) _acq = require("../src/lock-acquisition"); return _acq; }
|
||||
function getReg() { if (!_reg) _reg = require("../index"); return _reg; }
|
||||
function getCfg() { if (!_cfg) _cfg = require("../src/config"); return _cfg; }
|
||||
|
||||
function resetAll(): void {
|
||||
getReg().resetRegistry();
|
||||
getCfg().resetConfig();
|
||||
}
|
||||
|
||||
function measureTimeSync<T>(fn: () => T): { result: T; elapsedMs: number } {
|
||||
const start = performance.now();
|
||||
const result = fn();
|
||||
return { result, elapsedMs: performance.now() - start };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Single lock acquisition latency
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testSingleAcquisitionLatency() {
|
||||
resetAll();
|
||||
const { acquireLock } = getAcq();
|
||||
const owner = mockOwner("agent", "perf-test");
|
||||
|
||||
const times: number[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const path = `/tmp/perf-lock-${i}.ts`;
|
||||
const { elapsedMs } = measureTimeSync(() => {
|
||||
acquireLock({ path, lockType: "write", owner, autoReleaseTTL: 300_000 });
|
||||
});
|
||||
times.push(elapsedMs);
|
||||
}
|
||||
|
||||
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
const sorted = [...times].sort((a, b) => a - b);
|
||||
const median = sorted[Math.floor(sorted.length / 2)];
|
||||
const p95 = sorted[Math.floor(sorted.length * 0.95)];
|
||||
|
||||
assert(avg < THRESHOLDS.lockAcquisition, `Avg ${avg.toFixed(3)}ms < ${THRESHOLDS.lockAcquisition}ms`);
|
||||
console.log(` ✅ Single acquisition: avg=${avg.toFixed(3)}ms median=${median.toFixed(3)}ms p95=${p95.toFixed(3)}ms`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Single lock release latency
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testSingleReleaseLatency() {
|
||||
resetAll();
|
||||
const { acquireLock } = getAcq();
|
||||
const registry = getReg().getClaimRegistry();
|
||||
const owner = mockOwner("agent", "perf-release");
|
||||
|
||||
const claimIds: string[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const result = acquireLock({ path: `/tmp/perf-rel-${i}.ts`, lockType: "write", owner });
|
||||
if (result.claim) claimIds.push(result.claim.id);
|
||||
}
|
||||
|
||||
const times: number[] = [];
|
||||
for (const claimId of claimIds) {
|
||||
const { elapsedMs } = measureTimeSync(() => registry.release(claimId));
|
||||
times.push(elapsedMs);
|
||||
}
|
||||
|
||||
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
assert(avg < THRESHOLDS.lockRelease, `Avg release ${avg.toFixed(3)}ms < ${THRESHOLDS.lockRelease}ms`);
|
||||
console.log(` ✅ Single release: avg=${avg.toFixed(3)}ms`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Conflict check latency
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testConflictCheckLatency() {
|
||||
resetAll();
|
||||
const { acquireLock } = getAcq();
|
||||
const registry = getReg().getClaimRegistry();
|
||||
const owner = mockOwner("agent", "perf-c");
|
||||
const other = mockOwner("agent", "perf-o");
|
||||
|
||||
acquireLock({ path: TEST_FILE_A, lockType: "write", owner });
|
||||
|
||||
const times: number[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const { elapsedMs } = measureTimeSync(() =>
|
||||
registry.checkConflict(TEST_FILE_A, "write", other)
|
||||
);
|
||||
times.push(elapsedMs);
|
||||
}
|
||||
|
||||
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
assert(avg < THRESHOLDS.conflictCheck, `Avg conflict check ${avg.toFixed(3)}ms < ${THRESHOLDS.conflictCheck}ms`);
|
||||
console.log(` ✅ Conflict check: avg=${avg.toFixed(3)}ms`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Bulk acquisition throughput
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testBulkAcquisition() {
|
||||
resetAll();
|
||||
const { acquireLock } = getAcq();
|
||||
const owner = mockOwner("agent", "perf-bulk");
|
||||
|
||||
const count = 500;
|
||||
const start = performance.now();
|
||||
for (let i = 0; i < count; i++) {
|
||||
acquireLock({ path: `/tmp/perf-bulk-${i}.ts`, lockType: "write", owner, autoReleaseTTL: 300_000 });
|
||||
}
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
assert(elapsed < THRESHOLDS.bulkAcquire, `Bulk ${count} locks: ${elapsed.toFixed(0)}ms < ${THRESHOLDS.bulkAcquire}ms`);
|
||||
console.log(` ✅ Bulk acquire ${count} locks: ${elapsed.toFixed(0)}ms (${(count/elapsed).toFixed(2)} ops/ms)`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Bulk release
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testBulkRelease() {
|
||||
resetAll();
|
||||
const { acquireLock } = getAcq();
|
||||
const registry = getReg().getClaimRegistry();
|
||||
const owner = mockOwner("agent", "perf-bulk-rel");
|
||||
|
||||
for (let i = 0; i < 500; i++) {
|
||||
acquireLock({ path: `/tmp/perf-bulkr-${i}.ts`, lockType: "write", owner });
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
registry.releaseAllByOwner(owner);
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
assert(elapsed < THRESHOLDS.bulkRelease, `Bulk release: ${elapsed.toFixed(0)}ms < ${THRESHOLDS.bulkRelease}ms`);
|
||||
console.log(` ✅ Bulk release: ${elapsed.toFixed(0)}ms`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Lock info retrieval
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testLockInfoLatency() {
|
||||
resetAll();
|
||||
const { acquireLock, getLockInfo } = getAcq();
|
||||
const owner = mockOwner("agent", "perf-info");
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
acquireLock({ path: `/tmp/perf-inf-${i}.ts`, lockType: i % 2 === 0 ? "write" : "read", owner });
|
||||
}
|
||||
|
||||
const times: number[] = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const { elapsedMs } = measureTimeSync(() => getLockInfo(`/tmp/perf-inf-${i}.ts`));
|
||||
times.push(elapsedMs);
|
||||
}
|
||||
|
||||
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
assert(avg < THRESHOLDS.lockInfo, `Avg lock info ${avg.toFixed(3)}ms < ${THRESHOLDS.lockInfo}ms`);
|
||||
console.log(` ✅ Lock info: avg=${avg.toFixed(3)}ms`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test: Registry operation cycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function testRegistryOperationCycle() {
|
||||
resetAll();
|
||||
const registry = getReg().getClaimRegistry();
|
||||
const owner = mockOwner("agent", "perf-cycle");
|
||||
|
||||
const times: number[] = [];
|
||||
for (let i = 0; i < 500; i++) {
|
||||
const claimId = `cycle-${i}`;
|
||||
const path = `/tmp/perf-cyc-${i}.ts`;
|
||||
const { elapsedMs } = measureTimeSync(() => {
|
||||
registry.acquire({ id: claimId, path, lockType: "write", status: "active", owner, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() });
|
||||
registry.checkConflict(path, "write", owner);
|
||||
registry.release(claimId);
|
||||
});
|
||||
times.push(elapsedMs);
|
||||
}
|
||||
|
||||
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
assert(avg < THRESHOLDS.registryOperation, `Avg cycle ${avg.toFixed(3)}ms < ${THRESHOLDS.registryOperation}ms`);
|
||||
console.log(` ✅ Registry cycle: avg=${avg.toFixed(3)}ms`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test runner
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function runTests() {
|
||||
console.log("Running Performance Tests\n");
|
||||
console.log("Thresholds:");
|
||||
for (const [key, val] of Object.entries(THRESHOLDS)) {
|
||||
console.log(` ${key}: ${val}ms`);
|
||||
}
|
||||
console.log("");
|
||||
|
||||
const tests = [
|
||||
testSingleAcquisitionLatency,
|
||||
testSingleReleaseLatency,
|
||||
testConflictCheckLatency,
|
||||
testBulkAcquisition,
|
||||
testBulkRelease,
|
||||
testLockInfoLatency,
|
||||
testRegistryOperationCycle,
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
try {
|
||||
test();
|
||||
} catch (err) {
|
||||
console.error(`\n❌ Performance test ${test.name} failed: ${err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
console.log("\n✅ All performance tests passed!");
|
||||
}
|
||||
|
||||
runTests();
|
||||
Reference in New Issue
Block a user