initial commit: @arbiter/core authorization engine with js-rigor hardening
Zanzibar-style authorization graph engine (direct/chain/TTU/defeasible/ binary modes, condensed snapshots, value relations) with 39 rigor test campaigns. Includes fixes for snapshot binary writer/reader format mismatch (snapshot-of-snapshot corruption), possibility write-boundary validation, empty-graph snapshot serialization, relation lookup cache direction collision, config-redefinition cache invalidation, binary threshold semantics, defeasible compiled routing, and comparator reason whitelisting.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { CondensedGraph } from '../src/core/CondensedGraph.js';
|
||||
import { performance } from 'node:perf_hooks';
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = new Map();
|
||||
for (let i = 2; i < argv.length; i++) {
|
||||
const value = argv[i];
|
||||
if (!value.startsWith('--')) continue;
|
||||
const [key, inline] = value.slice(2).split('=');
|
||||
if (inline !== undefined) {
|
||||
args.set(key, inline);
|
||||
continue;
|
||||
}
|
||||
const next = argv[i + 1];
|
||||
if (next && !next.startsWith('--')) {
|
||||
args.set(key, next);
|
||||
i++;
|
||||
} else {
|
||||
args.set(key, true);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv);
|
||||
const edges = Number(args.get('edges') || 200000);
|
||||
const users = Number(args.get('users') || 10000);
|
||||
const docs = Number(args.get('docs') || 50000);
|
||||
const queryCount = Number(args.get('queries') || 10000);
|
||||
|
||||
console.log('CondensedGraph load bench');
|
||||
console.log(` edges: ${edges}`);
|
||||
console.log(` users: ${users}`);
|
||||
console.log(` docs: ${docs}`);
|
||||
console.log(` queries: ${queryCount}`);
|
||||
|
||||
const graph = new CondensedGraph();
|
||||
const buildStart = performance.now();
|
||||
for (let i = 0; i < edges; i++) {
|
||||
graph.addEdge(
|
||||
`user:${i % users}`,
|
||||
['owner', 'editor', 'viewer'][i % 3],
|
||||
`doc:${i % docs}`
|
||||
);
|
||||
}
|
||||
const buildTime = performance.now() - buildStart;
|
||||
|
||||
const finalizeStart = performance.now();
|
||||
graph.finalizePerfectHash();
|
||||
graph.finalizeWaveletAdjacency({ dropAdjacencyList: true });
|
||||
const finalizeTime = performance.now() - finalizeStart;
|
||||
|
||||
const serializeStart = performance.now();
|
||||
const buffer = graph.toBinary();
|
||||
const serializeTime = performance.now() - serializeStart;
|
||||
|
||||
const heapBeforeLoad = process.memoryUsage().heapUsed;
|
||||
const loadStart = performance.now();
|
||||
const loaded = CondensedGraph.fromBinary(buffer);
|
||||
const loadTime = performance.now() - loadStart;
|
||||
const heapAfterLoad = process.memoryUsage().heapUsed;
|
||||
|
||||
const usersArr = Array.from({ length: users }, (_, i) => `user:${i}`);
|
||||
const docsArr = Array.from({ length: docs }, (_, i) => `doc:${i}`);
|
||||
|
||||
const queryStart = performance.now();
|
||||
let hits = 0;
|
||||
for (let i = 0; i < queryCount; i++) {
|
||||
const user = usersArr[i % users];
|
||||
const doc = docsArr[i % docs];
|
||||
const owner = loaded.findEdge(user, 'owner', doc);
|
||||
const editor = loaded.findEdge(user, 'editor', doc);
|
||||
const viewer = loaded.findEdge(user, 'viewer', doc);
|
||||
if (owner !== null || editor !== null || viewer !== null) hits++;
|
||||
}
|
||||
const queryTime = performance.now() - queryStart;
|
||||
const heapAfterQueries = process.memoryUsage().heapUsed;
|
||||
|
||||
const stats = loaded.getStats();
|
||||
const bufferBytes = buffer.byteLength;
|
||||
const heapDeltaLoadBytes = heapAfterLoad - heapBeforeLoad;
|
||||
const heapDeltaQueryBytes = heapAfterQueries - heapAfterLoad;
|
||||
const toMb = (bytes) => (bytes / 1024 / 1024).toFixed(2);
|
||||
|
||||
console.log('\nResults');
|
||||
console.log(` build time: ${buildTime.toFixed(2)} ms`);
|
||||
console.log(` finalize time: ${finalizeTime.toFixed(2)} ms`);
|
||||
console.log(` serialize time: ${serializeTime.toFixed(2)} ms`);
|
||||
console.log(` load time: ${loadTime.toFixed(2)} ms`);
|
||||
console.log(` snapshot size: ${(bufferBytes / 1024 / 1024).toFixed(2)} MB`);
|
||||
console.log(` heap delta (load): ${toMb(heapDeltaLoadBytes)} MB`);
|
||||
console.log(` heap delta (post-query): ${toMb(heapDeltaQueryBytes)} MB`);
|
||||
console.log(` bytes/edge (snapshot): ${(bufferBytes / edges).toFixed(2)}`);
|
||||
console.log(` stats bytes/edge: ${stats.bytesPerEdge.toFixed(2)}`);
|
||||
console.log(` query time: ${queryTime.toFixed(2)} ms`);
|
||||
console.log(` avg query: ${(queryTime / queryCount * 1000).toFixed(3)} µs`);
|
||||
console.log(` queries/sec: ${(queryCount / (queryTime / 1000)).toFixed(0)}`);
|
||||
console.log(` hits: ${hits}`);
|
||||
Reference in New Issue
Block a user