import { performance } from 'perf_hooks'; import { Arbiter } from '../src/index.js'; console.log('⛓️ ChainRule Performance Benchmark Analysis\n'); // Benchmark utilities class ChainBenchmark { constructor(name) { this.name = name; this.results = []; this.labels = []; } async run(fn, iterations = 1000, label = '') { // Warmup for (let i = 0; i < Math.min(10, iterations / 10); i++) { try { await fn(); } catch (e) { throw e; } } // Actual benchmark const times = []; const totalStart = performance.now(); for (let i = 0; i < iterations; i++) { try { const start = performance.now(); await fn(); const end = performance.now(); times.push(end - start); } catch (e) { throw e; } } const totalTime = performance.now() - totalStart; const avg = times.reduce((a, b) => a + b, 0) / times.length; const min = Math.min(...times); const max = Math.max(...times); const p50 = times.sort((a, b) => a - b)[Math.floor(times.length * 0.5)]; const p95 = times[Math.floor(times.length * 0.95)]; const p99 = times[Math.floor(times.length * 0.99)]; const qps = Math.round(iterations / (totalTime / 1000)); this.results.push({ avg: avg.toFixed(3), min: min.toFixed(3), max: max.toFixed(3), p50: p50.toFixed(3), p95: p95.toFixed(3), p99: p99.toFixed(3), qps: qps }); this.labels.push(label); return { avg, min, max, p50, p95, p99, qps }; } report() { console.log('Configuration | Avg (ms) | P50 (ms) | P95 (ms) | P99 (ms) | QPS | Speedup'); console.log('---------------------------------|----------|----------|----------|----------|---------|--------'); const baselineQPS = this.results[0].qps; this.results.forEach((result, i) => { const label = this.labels[i] || `Test ${i + 1}`; const speedup = i === 0 ? '1.00x' : `${(result.qps / baselineQPS).toFixed(2)}x`; console.log(`${label.padEnd(32)} | ${result.avg.padStart(8)} | ${result.p50.padStart(8)} | ${result.p95.padStart(8)} | ${result.p99.padStart(8)} | ${result.qps.toString().padStart(7)} | ${speedup.padStart(6)}`); }); } } // Enterprise organizational data generator function setupEnterpriseChainGraph(scale = 'medium') { const scales = { small: { users: 200, groups: 50, projects: 20, departments: 8, budgets: 100 }, medium: { users: 2000, groups: 200, projects: 100, departments: 15, budgets: 500 }, large: { users: 10000, groups: 1000, projects: 500, departments: 25, budgets: 2000 }, xlarge: { users: 20000, groups: 5000, projects: 2000, departments: 40, budgets: 8000 }, xxlarge: { users: 100000, groups: 10000, projects: 5000, departments: 50, budgets: 20000 } }; const config = scales[scale]; console.log(`🏢 Setting up ${scale} enterprise graph: ${config.users} users, ${config.groups} groups, ${config.projects} projects`); const arbiter = new Arbiter({ fastConstructionMode: true, enableInference: false // Disable by default for clean baseline measurements }); // Create organizational hierarchy: users → groups → departments → divisions → company const entities = { users: [], groups: [], departments: [], divisions: ['engineering', 'sales', 'marketing', 'finance', 'operations'], projects: [], budgets: [], resources: [], facilities: [] }; // Generate users for (let i = 0; i < config.users; i++) { const userKey = `user:emp${i}`; entities.users.push(userKey); arbiter.addNode(userKey, 'user'); } // Generate groups (teams within departments) for (let i = 0; i < config.groups; i++) { const groupKey = `group:team${i}`; entities.groups.push(groupKey); arbiter.addNode(groupKey, 'group'); } // Generate departments for (let i = 0; i < config.departments; i++) { const deptKey = `dept:dept${i}`; entities.departments.push(deptKey); arbiter.addNode(deptKey, 'department'); } // Generate divisions entities.divisions.forEach(div => { const divKey = `division:${div}`; arbiter.addNode(divKey, 'division'); }); // Add company root arbiter.addNode('company:acme', 'company'); // Generate projects for (let i = 0; i < config.projects; i++) { const projectKey = `project:proj${i}`; entities.projects.push(projectKey); arbiter.addNode(projectKey, 'project'); } // Generate budgets with values for (let i = 0; i < config.budgets; i++) { const budgetKey = `budget:budget${i}`; const value = Math.floor(Math.random() * 5000000) + 100000; // $100K to $5M entities.budgets.push({ key: budgetKey, value }); arbiter.addNode(budgetKey, 'budget'); } // Generate resources (servers, databases, etc.) for (let i = 0; i < config.projects / 2; i++) { const resourceKey = `resource:res${i}`; const cost = Math.floor(Math.random() * 100000) + 5000; // $5K to $100K entities.resources.push({ key: resourceKey, cost }); arbiter.addNode(resourceKey, 'resource'); } // Generate facilities const facilityNames = ['hq', 'east-office', 'west-office', 'remote', 'datacenter']; facilityNames.forEach(name => { const facilityKey = `facility:${name}`; entities.facilities.push(facilityKey); arbiter.addNode(facilityKey, 'facility'); }); console.log(' 🔗 Building organizational chains...'); // Build 5-level organizational hierarchy: user → group → department → division → company // User → Group membership entities.users.forEach(user => { // Each user belongs to 1-3 groups const groupCount = Math.floor(Math.random() * 3) + 1; for (let i = 0; i < groupCount; i++) { const group = entities.groups[Math.floor(Math.random() * entities.groups.length)]; arbiter.addRelation(user, 'member_of', group); } }); // Group → Department membership entities.groups.forEach(group => { const dept = entities.departments[Math.floor(Math.random() * entities.departments.length)]; arbiter.addRelation(group, 'belongs_to', dept); }); // Department → Division membership entities.departments.forEach(dept => { const division = entities.divisions[Math.floor(Math.random() * entities.divisions.length)]; arbiter.addRelation(dept, 'part_of', `division:${division}`); }); // Division → Company membership entities.divisions.forEach(div => { arbiter.addRelation(`division:${div}`, 'part_of', 'company:acme'); }); // Project chains: user → group → project → budget entities.projects.forEach(project => { // Projects owned by groups const ownerGroup = entities.groups[Math.floor(Math.random() * entities.groups.length)]; arbiter.addRelation(ownerGroup, 'manages', project); // Projects have budgets const budget = entities.budgets[Math.floor(Math.random() * entities.budgets.length)]; arbiter.addRelation(project, 'has_budget', budget.key, { value: budget.value }); // Projects have resource costs if (Math.random() < 0.7) { const resource = entities.resources[Math.floor(Math.random() * entities.resources.length)]; arbiter.addRelation(project, 'uses_resource', resource.key, { cost: resource.cost }); } }); // Facility chains: user → facility, department → facility entities.users.forEach(user => { const facility = entities.facilities[Math.floor(Math.random() * entities.facilities.length)]; arbiter.addRelation(user, 'located_in', facility); }); entities.departments.forEach(dept => { const facility = entities.facilities[Math.floor(Math.random() * entities.facilities.length)]; arbiter.addRelation(dept, 'operates_in', facility); }); // Configure basic relation types const relationTypes = [ 'member_of', 'belongs_to', 'part_of', 'manages', 'has_budget', 'uses_resource', 'located_in', 'operates_in', 'can_access', 'has_permission' ]; relationTypes.forEach(rel => { arbiter.setRelationConfig(rel, { type: 'direct' }); }); console.log(' ✅ Enterprise chain graph ready'); // Initialize PLTC for reachability checks console.log(' ⚡ Initializing PLTC indices...'); const initStart = Date.now(); arbiter.graphManager.initializeReachabilityChecker(); const initTime = Date.now() - initStart; console.log(` ✅ PLTC initialized in ${initTime}ms`); return { arbiter, ...entities }; } // Benchmark 1: Chain Length Performance async function benchmarkChainLength() { console.log('📏 Benchmark 1: Chain Length Performance Impact\n'); const graph = setupEnterpriseChainGraph('medium'); const benchmark = new ChainBenchmark('Chain Length Performance'); // Baseline: Direct access (no chain) graph.arbiter.setRelationConfig('direct_access', { type: 'direct', relation: 'can_access' }); // Create a substantial number of direct access relations for realistic testing // Instead of 100 random relations, create relations for every user to some projects const directAccessPairs = []; graph.users.forEach((user, i) => { // Each user has direct access to 1-3 projects const numProjects = Math.floor(Math.random() * 3) + 1; for (let j = 0; j < numProjects; j++) { const project = graph.projects[(i * 7 + j) % graph.projects.length]; // Deterministic but spread out graph.arbiter.addRelation(user, 'can_access', project); directAccessPairs.push({ user, project }); } }); console.log(` ✅ Created ${directAccessPairs.length} direct access relations (${(directAccessPairs.length / (graph.users.length * graph.projects.length) * 100).toFixed(1)}% coverage)`); await benchmark.run(() => { // Test EXISTING relations for accurate performance measurement const pair = directAccessPairs[Math.floor(Math.random() * directAccessPairs.length)]; return graph.arbiter.check(pair.user, 'direct_access', pair.project, { noInfer: true }); }, 1000, 'Direct Access (baseline)'); // 2-step chain: user → group → project graph.arbiter.setRelationConfig('chain_2_step', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ] }); // Build collection of valid 2-step chain paths for testing const validChain2Paths = []; graph.users.forEach(user => { // Find groups this user belongs to const userGroups = graph.arbiter.relationManager.getRelationsFromSrc(user, 'member_of'); userGroups.forEach(groupRel => { // Find projects this group manages const groupProjects = graph.arbiter.relationManager.getRelationsFromSrc(groupRel.object, 'manages'); groupProjects.forEach(projectRel => { validChain2Paths.push({ user, project: projectRel.object }); }); }); }); console.log(` ✅ Found ${validChain2Paths.length} valid 2-step chain paths (user→group→project)`); await benchmark.run(() => { // Test EXISTING chain paths for accurate performance measurement if (validChain2Paths.length === 0) { // Fallback to random if no valid paths found const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_2_step', project, { noInfer: true }); } else { const path = validChain2Paths[Math.floor(Math.random() * validChain2Paths.length)]; return graph.arbiter.check(path.user, 'chain_2_step', path.project, { noInfer: true }); } }, 1000, '2-Step Chain (user→group→project)'); // 3-step chain: user → group → department → division graph.arbiter.setRelationConfig('chain_3_step', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'belongs_to', direction: 'out' }, { relation: 'part_of', direction: 'out' } ] }); // Build collection of valid 3-step chain paths for testing const validChain3Paths = []; graph.users.forEach(user => { // Find groups this user belongs to const userGroups = graph.arbiter.relationManager.getRelationsFromSrc(user, 'member_of'); userGroups.forEach(groupRel => { // Find departments this group belongs to const groupDepts = graph.arbiter.relationManager.getRelationsFromSrc(groupRel.object, 'belongs_to'); groupDepts.forEach(deptRel => { // Find divisions this department is part of const deptDivisions = graph.arbiter.relationManager.getRelationsFromSrc(deptRel.object, 'part_of'); deptDivisions.forEach(divisionRel => { validChain3Paths.push({ user, division: divisionRel.object }); }); }); }); }); console.log(` ✅ Found ${validChain3Paths.length} valid 3-step chain paths (user→group→dept→division)`); await benchmark.run(() => { // Test EXISTING chain paths for accurate performance measurement if (validChain3Paths.length === 0) { // Fallback to random if no valid paths found const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)]; return graph.arbiter.check(user, 'chain_3_step', `division:${division}`, { noInfer: true }); } else { const path = validChain3Paths[Math.floor(Math.random() * validChain3Paths.length)]; return graph.arbiter.check(path.user, 'chain_3_step', path.division, { noInfer: true }); } }, 1000, '3-Step Chain (user→group→dept→division)'); // 4-step chain: user → group → project → budget → facility (create a valid 4-step path) // First add budget→facility relationships to create valid 4-step chains graph.budgets.forEach(budget => { const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)]; graph.arbiter.addRelation(budget.key, 'allocated_to', facility); }); graph.arbiter.setRelationConfig('allocated_to', { type: 'direct' }); graph.arbiter.setRelationConfig('chain_4_step', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, // user → group { relation: 'manages', direction: 'out' }, // group → project { relation: 'has_budget', direction: 'out' }, // project → budget { relation: 'allocated_to', direction: 'out' } // budget → facility ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)]; return graph.arbiter.check(user, 'chain_4_step', facility, { noInfer: true }); }, 1000, '4-Step Chain (user→group→project→budget→facility)'); // 5-step chain: user → group → department → division → company → facility // Add company→facility relationship for valid 5-step chain graph.facilities.forEach(facility => { graph.arbiter.addRelation('company:acme', 'operates', facility); }); graph.arbiter.setRelationConfig('operates', { type: 'direct' }); graph.arbiter.setRelationConfig('chain_5_step', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, // user → group { relation: 'belongs_to', direction: 'out' }, // group → department { relation: 'part_of', direction: 'out' }, // department → division { relation: 'part_of', direction: 'out' }, // division → company { relation: 'operates', direction: 'out' } // company → facility ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)]; return graph.arbiter.check(user, 'chain_5_step', facility, { noInfer: true }); }, 1000, '5-Step Chain (user→group→dept→div→company→facility)'); benchmark.report(); } // Benchmark 2: ChainRule vs Traditional Approaches async function benchmarkVsTraditional() { console.log('⚔️ Benchmark 2: ChainRule vs Traditional Authorization\n'); const graph = setupEnterpriseChainGraph('medium'); const benchmark = new ChainBenchmark('ChainRule vs Traditional'); // Traditional ParentRule approach graph.arbiter.setRelationConfig('traditional_parent', { type: 'parent', parentRelation: 'manages', relation: 'member_of' }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'traditional_parent', project, { noInfer: true }); }, 1000, 'ParentRule (baseline)'); // Equivalent ChainRule approach graph.arbiter.setRelationConfig('chain_equivalent', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_equivalent', project, { noInfer: true }); }, 1000, 'ChainRule (equivalent logic)'); // MultiHopRule approach graph.arbiter.setRelationConfig('multihop_approach', { type: 'multi_hop', relation: 'member_of', maxDepth: 3, pathAggregation: 'max' }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'multihop_approach', project, { noInfer: true }); }, 1000, 'MultiHopRule (flexible paths)'); // ChainRule with reverse direction graph.arbiter.setRelationConfig('chain_reverse', { type: 'chain', steps: [ { relation: 'manages', direction: 'in' }, { relation: 'member_of', direction: 'in' } ] }); await benchmark.run(() => { const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; const user = graph.users[Math.floor(Math.random() * graph.users.length)]; return graph.arbiter.check(project, 'chain_reverse', user, { noInfer: true }); }, 1000, 'ChainRule (reverse direction)'); benchmark.report(); } // Benchmark 3: Value Extraction Performance async function benchmarkValueExtraction() { console.log('💰 Benchmark 3: Value Extraction Performance\n'); const graph = setupEnterpriseChainGraph('medium'); const benchmark = new ChainBenchmark('Value Extraction Performance'); // Chain without value extraction (baseline) graph.arbiter.setRelationConfig('chain_no_values', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'has_budget', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)]; return graph.arbiter.check(user, 'chain_no_values', budget.key); }, 1000, 'Chain (no value extraction)'); // Chain with value extraction - SUM aggregation graph.arbiter.setRelationConfig('chain_sum_values', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'has_budget', direction: 'out' } ], extractValues: true, extractFrom: 2, extractRelation: 'has_budget', valueAggregation: 'sum' }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)]; return graph.arbiter.check(user, 'chain_sum_values', budget.key); }, 1000, 'Chain with SUM aggregation'); // Chain with value extraction - MAX aggregation graph.arbiter.setRelationConfig('chain_max_values', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'has_budget', direction: 'out' } ], extractValues: true, extractFrom: 2, extractRelation: 'has_budget', valueAggregation: 'max' }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)]; return graph.arbiter.check(user, 'chain_max_values', budget.key); }, 1000, 'Chain with MAX aggregation'); // Chain with value extraction - MIN aggregation graph.arbiter.setRelationConfig('chain_min_values', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'has_budget', direction: 'out' } ], extractValues: true, extractFrom: 2, extractRelation: 'has_budget', valueAggregation: 'min' }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)]; return graph.arbiter.check(user, 'chain_min_values', budget.key); }, 1000, 'Chain with MIN aggregation'); // Chain with OWA fusion aggregation graph.arbiter.setRelationConfig('chain_owa_values', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'has_budget', direction: 'out' } ], extractValues: true, extractFrom: 2, extractRelation: 'has_budget', valueAggregation: 'optimistic', owaWeights: [0.7, 0.5, 0.3, 0.1] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)]; return graph.arbiter.check(user, 'chain_owa_values', budget.key); }, 1000, 'Chain with OWA aggregation'); benchmark.report(); } // Benchmark 4: Direction and Pattern Performance async function benchmarkDirectionPatterns() { console.log('🧭 Benchmark 4: Chain Direction and Pattern Performance\n'); const graph = setupEnterpriseChainGraph('medium'); const benchmark = new ChainBenchmark('Direction and Pattern Performance'); // Forward chain (baseline) graph.arbiter.setRelationConfig('forward_chain', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'belongs_to', direction: 'out' }, { relation: 'part_of', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)]; return graph.arbiter.check(user, 'forward_chain', `division:${division}`); }, 1000, 'Forward Chain (baseline)'); // Backward chain graph.arbiter.setRelationConfig('backward_chain', { type: 'chain', steps: [ { relation: 'part_of', direction: 'in' }, { relation: 'belongs_to', direction: 'in' }, { relation: 'member_of', direction: 'in' } ] }); await benchmark.run(() => { const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)]; const user = graph.users[Math.floor(Math.random() * graph.users.length)]; return graph.arbiter.check(`division:${division}`, 'backward_chain', user); }, 1000, 'Backward Chain'); // Mixed direction chain graph.arbiter.setRelationConfig('mixed_chain', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, // user → group { relation: 'belongs_to', direction: 'in' }, // group ← department { relation: 'operates_in', direction: 'out' } // department → facility ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)]; return graph.arbiter.check(user, 'mixed_chain', facility); }, 1000, 'Mixed Direction Chain'); // Parallel chains pattern (testing multiple chain endpoints) graph.arbiter.setRelationConfig('parallel_chains', { union: [ { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ] }, { type: 'chain', steps: [ { relation: 'located_in', direction: 'out' }, { relation: 'operates_in', direction: 'in' }, { relation: 'belongs_to', direction: 'in' }, { relation: 'manages', direction: 'out' } ] } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'parallel_chains', project); }, 1000, 'Parallel Chains (Union)'); benchmark.report(); } // Benchmark 5: ChainRule with Inference async function benchmarkChainInference() { console.log('🧠 Benchmark 5: ChainRule with Inference Integration\n'); const graph = setupEnterpriseChainGraph('small'); // Smaller graph for inference const benchmark = new ChainBenchmark('ChainRule Inference Performance'); // Remove some relationships to create inference opportunities const relationships = []; for (let i = 0; i < 50; i++) { const user = graph.users[i]; const group = graph.groups[Math.floor(Math.random() * Math.min(20, graph.groups.length))]; relationships.push({ user, group }); graph.arbiter.relationManager.removeRelation(user, 'member_of', group); } // Chain without inference (baseline) graph.arbiter.setRelationConfig('chain_no_inference', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ], allowInference: false }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_no_inference', project, { noInfer: true }); }, 500, 'Chain (no inference)'); // Chain with basic inference graph.arbiter.setRelationConfig('chain_basic_inference', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ], allowInference: true }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_basic_inference', project); }, 500, 'Chain (basic inference)'); // Chain with inference and reliability threshold graph.arbiter.setRelationConfig('chain_reliable_inference', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ], allowInference: true, minReliability: 0.7 }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_reliable_inference', project); }, 500, 'Chain (high reliability inference)'); benchmark.report(); } // Benchmark 7: Real-World Enterprise Scenarios async function benchmarkEnterpriseScenarios() { console.log('🏢 Benchmark 7: Real-World Enterprise Authorization Scenarios\n'); const graph = setupEnterpriseChainGraph('medium'); const benchmark = new ChainBenchmark('Enterprise Scenarios'); // Scenario 1: Budget Authorization (user → group → project → budget >= threshold) graph.arbiter.setRelationConfig('budget_authorization', { type: 'relational_comparator', leftOperand: { rule: { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'has_budget', direction: 'out' } ], extractValues: true, extractFrom: 2, extractRelation: 'has_budget', valueAggregation: 'sum' }, extractValue: true }, rightOperand: { rule: { type: 'direct', relation: 'has_value' }, extractValue: true }, comparator: '>=', fallbackBehavior: 'deny' }); // Add budget thresholds for (let i = 0; i < 50; i++) { const threshold = Math.floor(Math.random() * 1000000) + 50000; graph.arbiter.addRelation(`threshold:${i}`, 'has_value', `value:${threshold}`, { value: threshold }); } await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const threshold = `threshold:${Math.floor(Math.random() * 50)}`; return graph.arbiter.check(user, 'budget_authorization', threshold); }, 500, 'Budget Authorization Chain'); // Scenario 2: Facility Access (user → department → facility) graph.arbiter.setRelationConfig('facility_access', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'belongs_to', direction: 'out' }, { relation: 'operates_in', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)]; return graph.arbiter.check(user, 'facility_access', facility); }, 1000, 'Facility Access Chain'); // Scenario 3: Resource Approval (user → group → project → resource) graph.arbiter.setRelationConfig('resource_approval', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' }, { relation: 'uses_resource', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const resource = graph.resources[Math.floor(Math.random() * graph.resources.length)]; return graph.arbiter.check(user, 'resource_approval', resource.key); }, 1000, 'Resource Approval Chain'); // Scenario 4: Cross-Division Access (complex multi-step authorization) graph.arbiter.setRelationConfig('cross_division_access', { union: [ { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'belongs_to', direction: 'out' }, { relation: 'part_of', direction: 'out' } ] }, { type: 'chain', steps: [ { relation: 'located_in', direction: 'out' }, { relation: 'operates_in', direction: 'in' }, { relation: 'part_of', direction: 'out' } ] } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)]; return graph.arbiter.check(user, 'cross_division_access', `division:${division}`); }, 1000, 'Cross-Division Access (Union)'); benchmark.report(); } // Main benchmark runner async function runChainRuleBenchmarks() { console.log('🚀 Starting ChainRule Performance Benchmarks...\n'); try { await benchmarkChainLength(); await benchmarkVsTraditional(); await benchmarkValueExtraction(); await benchmarkDirectionPatterns(); await benchmarkChainInference(); await benchmarkEnterpriseScenarios(); console.log('\n✅ All ChainRule benchmarks completed successfully!'); console.log('\n🚀 X-LARGE SCALE BENCHMARKS (xlarge)'); await benchmarkChainLengthScale('xlarge', 100); await benchmarkVsTraditionalScale('xlarge', 100); console.log('\n🚀 XX-LARGE SCALE BENCHMARKS (xxlarge)'); await benchmarkChainLengthScale('xxlarge', 50); await benchmarkVsTraditionalScale('xxlarge', 50); } catch (error) { console.error('❌ Benchmark failed:', error); process.exit(1); } } // Run if this file is executed directly if (import.meta.url === `file://${process.argv[1]}`) { runChainRuleBenchmarks(); } export { runChainRuleBenchmarks }; // Add helper functions for large scale runs: async function benchmarkChainLengthScale(scale, iterations) { console.log(`\n📏 [${scale.toUpperCase()}] Chain Length Performance Impact`); const graph = setupEnterpriseChainGraph(scale); const benchmark = new ChainBenchmark(`Chain Length Performance (${scale})`); // Baseline: Direct access (no chain) graph.arbiter.setRelationConfig('direct_access', { type: 'direct', relation: 'can_access' }); const directAccessPairs = []; graph.users.forEach((user, i) => { const numProjects = Math.floor(Math.random() * 3) + 1; for (let j = 0; j < numProjects; j++) { const project = graph.projects[(i * 7 + j) % graph.projects.length]; graph.arbiter.addRelation(user, 'can_access', project); directAccessPairs.push({ user, project }); } }); await benchmark.run(() => { const pair = directAccessPairs[Math.floor(Math.random() * directAccessPairs.length)]; return graph.arbiter.check(pair.user, 'direct_access', pair.project, { noInfer: true }); }, iterations, 'Direct Access (baseline)'); // 2-step chain graph.arbiter.setRelationConfig('chain_2_step', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_2_step', project, { noInfer: true }); }, iterations, '2-Step Chain (user→group→project)'); benchmark.report(); } async function benchmarkVsTraditionalScale(scale, iterations) { console.log(`\n⚔️ [${scale.toUpperCase()}] ChainRule vs Traditional Authorization`); const graph = setupEnterpriseChainGraph(scale); const benchmark = new ChainBenchmark(`ChainRule vs Traditional (${scale})`); // Traditional ParentRule approach graph.arbiter.setRelationConfig('traditional_parent', { type: 'parent', parentRelation: 'manages', relation: 'member_of' }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'traditional_parent', project, { noInfer: true }); }, iterations, 'ParentRule (baseline)'); // Equivalent ChainRule approach graph.arbiter.setRelationConfig('chain_equivalent', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'manages', direction: 'out' } ] }); await benchmark.run(() => { const user = graph.users[Math.floor(Math.random() * graph.users.length)]; const project = graph.projects[Math.floor(Math.random() * graph.projects.length)]; return graph.arbiter.check(user, 'chain_equivalent', project, { noInfer: true }); }, iterations, 'ChainRule (equivalent logic)'); benchmark.report(); }