Files

177 lines
7.4 KiB
JavaScript
Raw Permalink Normal View History

/**
* Analysis of which rules use reachability optimization
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { BigGraphGenerator } from '../helpers/big-graph-generator.js';
test('analyzes which rules use reachability optimization', async () => {
if (process.env.TEST_DEBUG === '1') console.log('🔍 Analyzing which rules use reachability optimization...');
const generator = new BigGraphGenerator();
const graphData = generator.generateGraph('enterprise');
const arbiter = generator.loadIntoArbiter(graphData);
// Initialize reachability checker
await arbiter.initializeReachabilityChecker({
strategy: 'auto',
twoHopOptions: {},
treeCoverOptions: {}
});
// Get initial reachability stats
const initialStats = arbiter.getReachabilityStats();
if (process.env.TEST_DEBUG === '1') console.log(`Initial reachability stats: ${initialStats.totalQueries} queries`);
// Test different rule types
const testCases = [
{
name: 'DirectRule',
relation: 'can_read',
description: 'Direct relations (should use fast path)'
},
{
name: 'ChainRule',
relation: 'can_read_via_role',
description: 'Chain relations (should use reachability)'
},
{
name: 'MultiHopRule',
relation: 'can_access_multi_hop',
description: 'Multi-hop relations (should use reachability)'
},
{
name: 'ParentRule',
relation: 'can_read', // Parent rule uses direct relations
description: 'Parent relations (might use reachability)'
},
{
name: 'TupleToUsersetRule',
relation: 'can_read',
description: 'Tuple-to-userset relations (might use reachability)'
}
];
const results = {};
for (const testCase of testCases) {
if (process.env.TEST_DEBUG === '1') console.log(`\\n=== TESTING ${testCase.name} ===`);
if (process.env.TEST_DEBUG === '1') console.log(`Description: ${testCase.description}`);
// Reset reachability stats
const beforeStats = arbiter.getReachabilityStats();
// Test the rule
const testRelations = graphData.relations.filter(r =>
r.src.startsWith('user:') && r.dst.startsWith('doc:') &&
r.relation === 'can_read'
).slice(0, 3);
const startTime = Date.now();
let queryCount = 0;
const latencies = [];
for (const relation of testRelations) {
const queryStart = process.hrtime.bigint();
try {
const result = arbiter.check(relation.src, testCase.relation, relation.dst);
const queryEnd = process.hrtime.bigint();
const latency = Number(queryEnd - queryStart) / 1000000;
latencies.push(latency);
queryCount++;
} catch (error) {
queryCount++;
}
}
const duration = Date.now() - startTime;
const qps = (queryCount / duration) * 1000;
const avgLatency = latencies.length > 0 ?
latencies.reduce((a, b) => a + b, 0) / latencies.length : 0;
// Get reachability stats after
const afterStats = arbiter.getReachabilityStats();
const reachabilityQueries = afterStats.totalQueries - beforeStats.totalQueries;
const twoHopHits = afterStats.twoHopHits - beforeStats.twoHopHits;
const treeCoverHits = afterStats.treeCoverHits - beforeStats.treeCoverHits;
const fallbackQueries = afterStats.fallbackQueries - beforeStats.fallbackQueries;
results[testCase.name] = {
qps,
avgLatency,
reachabilityQueries,
twoHopHits,
treeCoverHits,
fallbackQueries,
usesReachability: reachabilityQueries > 0
};
if (process.env.TEST_DEBUG === '1') console.log(` QPS: ${qps.toFixed(2)}`);
if (process.env.TEST_DEBUG === '1') console.log(` Avg Latency: ${avgLatency.toFixed(3)}ms`);
if (process.env.TEST_DEBUG === '1') console.log(` Reachability Queries: ${reachabilityQueries}`);
if (process.env.TEST_DEBUG === '1') console.log(` TwoHop Hits: ${twoHopHits}`);
if (process.env.TEST_DEBUG === '1') console.log(` TreeCover Hits: ${treeCoverHits}`);
if (process.env.TEST_DEBUG === '1') console.log(` Fallback Queries: ${fallbackQueries}`);
if (process.env.TEST_DEBUG === '1') console.log(` Uses Reachability: ${reachabilityQueries > 0 ? '✅ YES' : '❌ NO'}`);
}
// Analysis
if (process.env.TEST_DEBUG === '1') console.log('\\n=== REACHABILITY OPTIMIZATION ANALYSIS ===');
const rulesUsingReachability = Object.entries(results)
.filter(([name, result]) => result.usesReachability)
.map(([name, result]) => name);
const rulesNotUsingReachability = Object.entries(results)
.filter(([name, result]) => !result.usesReachability)
.map(([name, result]) => name);
if (process.env.TEST_DEBUG === '1') console.log(`\\n✅ Rules USING reachability optimization:`);
rulesUsingReachability.forEach(rule => {
const result = results[rule];
if (process.env.TEST_DEBUG === '1') console.log(` - ${rule}: ${result.reachabilityQueries} queries, ${result.treeCoverHits} tree-cover hits`);
});
if (process.env.TEST_DEBUG === '1') console.log(`\\n❌ Rules NOT using reachability optimization:`);
rulesNotUsingReachability.forEach(rule => {
const result = results[rule];
if (process.env.TEST_DEBUG === '1') console.log(` - ${rule}: ${result.qps.toFixed(2)} QPS, ${result.avgLatency.toFixed(3)}ms avg`);
});
// Performance comparison
if (process.env.TEST_DEBUG === '1') console.log(`\\n=== PERFORMANCE COMPARISON ===`);
const reachabilityRules = rulesUsingReachability.map(rule => ({ name: rule, ...results[rule] }));
const nonReachabilityRules = rulesNotUsingReachability.map(rule => ({ name: rule, ...results[rule] }));
if (reachabilityRules.length > 0) {
const avgReachabilityQPS = reachabilityRules.reduce((sum, rule) => sum + rule.qps, 0) / reachabilityRules.length;
if (process.env.TEST_DEBUG === '1') console.log(`Average QPS for rules WITH reachability: ${avgReachabilityQPS.toFixed(2)}`);
}
if (nonReachabilityRules.length > 0) {
const avgNonReachabilityQPS = nonReachabilityRules.reduce((sum, rule) => sum + rule.qps, 0) / nonReachabilityRules.length;
if (process.env.TEST_DEBUG === '1') console.log(`Average QPS for rules WITHOUT reachability: ${avgNonReachabilityQPS.toFixed(2)}`);
}
// Recommendations
if (process.env.TEST_DEBUG === '1') console.log(`\\n=== RECOMMENDATIONS ===`);
if (rulesNotUsingReachability.length > 0) {
if (process.env.TEST_DEBUG === '1') console.log(`Consider optimizing these rules with reachability indexes:`);
rulesNotUsingReachability.forEach(rule => {
if (process.env.TEST_DEBUG === '1') console.log(` - ${rule}: Could benefit from reachability optimization`);
});
} else {
if (process.env.TEST_DEBUG === '1') console.log(`All tested rules are using reachability optimization! 🎉`);
}
// Final stats
const finalStats = arbiter.getReachabilityStats();
if (process.env.TEST_DEBUG === '1') console.log(`\\n=== FINAL REACHABILITY STATS ===`);
if (process.env.TEST_DEBUG === '1') console.log(`Total queries: ${finalStats.totalQueries}`);
if (process.env.TEST_DEBUG === '1') console.log(`TwoHop hits: ${finalStats.twoHopHits}`);
if (process.env.TEST_DEBUG === '1') console.log(`TreeCover hits: ${finalStats.treeCoverHits}`);
if (process.env.TEST_DEBUG === '1') console.log(`Fallback queries: ${finalStats.fallbackQueries}`);
if (process.env.TEST_DEBUG === '1') console.log(`Strategy: ${finalStats.strategy}`);
});