bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
152 lines
4.7 KiB
JavaScript
152 lines
4.7 KiB
JavaScript
/**
|
|
* QUIXZOOM Full Replay - End-to-end test mot PostgreSQL
|
|
*
|
|
* Kör hela kedjan:
|
|
* PostgreSQL → Object Identity Engine → UKG → Evidence Layer → Temporal Layer
|
|
*/
|
|
|
|
const { Pool } = require('pg');
|
|
const ObjectIdentityPipeline = require('../identity-engine/pipeline');
|
|
|
|
class FullReplay {
|
|
constructor() {
|
|
this.db = new Pool({
|
|
host: 'localhost',
|
|
port: 5433,
|
|
user: 'quixzoom',
|
|
password: 'quixzoom2026',
|
|
database: 'quixzoom',
|
|
});
|
|
|
|
this.oie = new ObjectIdentityPipeline({
|
|
autoMergeThreshold: 0.50,
|
|
waitThreshold: 0.30,
|
|
});
|
|
|
|
this.metrics = {
|
|
startTime: Date.now(),
|
|
eventsProcessed: 0,
|
|
objectsCreated: 0,
|
|
objectsMerged: 0,
|
|
dbReads: 0,
|
|
dbWrites: 0,
|
|
latencies: [],
|
|
errors: [],
|
|
};
|
|
}
|
|
|
|
async run() {
|
|
console.log('[REPLAY] Starting full replay against PostgreSQL...\n');
|
|
|
|
// 1. Ladda alla observationer från databasen
|
|
console.log('[REPLAY] Loading observations from PostgreSQL...');
|
|
const startLoad = Date.now();
|
|
const observations = await this.loadObservations();
|
|
const loadTime = Date.now() - startLoad;
|
|
console.log(`[REPLAY] Loaded ${observations.length} observations in ${loadTime}ms`);
|
|
|
|
// 2. Kör replay
|
|
console.log('[REPLAY] Processing observations through OIE...\n');
|
|
const startProcess = Date.now();
|
|
|
|
for (let i = 0; i < observations.length; i++) {
|
|
const obs = observations[i];
|
|
const eventStart = Date.now();
|
|
|
|
try {
|
|
// Konvertera DB-format till OIE-format
|
|
const oieObservation = {
|
|
id: obs.observation_id,
|
|
objectType: obs.source_type === 'zoomer' ? 'street_lamp' : 'unknown',
|
|
location: {
|
|
lat: obs.latitude,
|
|
lng: obs.longitude,
|
|
},
|
|
gpsAccuracy: obs.gps_accuracy || 3,
|
|
timestamp: obs.created_at,
|
|
attributes: obs.ai_analysis?.attributes || {},
|
|
};
|
|
|
|
// Processa genom OIE
|
|
const result = await this.oie.process(oieObservation);
|
|
|
|
if (result.action === 'new_object') {
|
|
this.metrics.objectsCreated++;
|
|
} else if (result.action === 'merge') {
|
|
this.metrics.objectsMerged++;
|
|
}
|
|
|
|
this.metrics.eventsProcessed++;
|
|
this.metrics.latencies.push(Date.now() - eventStart);
|
|
|
|
// Logga var 1000:e
|
|
if ((i + 1) % 1000 === 0) {
|
|
this.logProgress(i + 1, observations.length);
|
|
}
|
|
|
|
} catch (error) {
|
|
this.metrics.errors.push({
|
|
observation: obs.observation_id,
|
|
error: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
const processTime = Date.now() - startProcess;
|
|
|
|
// 3. Sammanfattning
|
|
console.log('\n=== REPLAY COMPLETE ===');
|
|
this.printSummary(processTime);
|
|
|
|
await this.db.end();
|
|
}
|
|
|
|
async loadObservations() {
|
|
const result = await this.db.query(`
|
|
SELECT observation_id, source_type, source_id,
|
|
latitude, longitude, gps_accuracy,
|
|
ai_analysis, created_at
|
|
FROM observations
|
|
ORDER BY created_at
|
|
LIMIT 10000
|
|
`);
|
|
this.metrics.dbReads++;
|
|
return result.rows;
|
|
}
|
|
|
|
logProgress(current, total) {
|
|
const elapsed = Date.now() - this.metrics.startTime;
|
|
const rate = current / (elapsed / 1000);
|
|
const percent = ((current / total) * 100).toFixed(1);
|
|
|
|
process.stdout.write(`\r[REPLAY] ${percent}% | ${current}/${total} | ${rate.toFixed(0)} events/s | Objects: ${this.oie.objects.size}`);
|
|
}
|
|
|
|
printSummary(processTime) {
|
|
const totalTime = Date.now() - this.metrics.startTime;
|
|
const avgLatency = this.metrics.latencies.reduce((a, b) => a + b, 0) / this.metrics.latencies.length;
|
|
const sortedLatencies = [...this.metrics.latencies].sort((a, b) => a - b);
|
|
const p99 = sortedLatencies[Math.floor(sortedLatencies.length * 0.99)];
|
|
|
|
console.log(`\nTotal time: ${totalTime}ms`);
|
|
console.log(`Processing time: ${processTime}ms`);
|
|
console.log(`Events processed: ${this.metrics.eventsProcessed}`);
|
|
console.log(`Events/sec: ${(this.metrics.eventsProcessed / (processTime / 1000)).toFixed(0)}`);
|
|
console.log(`Objects created: ${this.metrics.objectsCreated}`);
|
|
console.log(`Objects merged: ${this.metrics.objectsMerged}`);
|
|
console.log(`Final object count: ${this.oie.objects.size}`);
|
|
console.log(`Avg latency: ${avgLatency.toFixed(2)}ms`);
|
|
console.log(`P99 latency: ${p99}ms`);
|
|
console.log(`Errors: ${this.metrics.errors.length}`);
|
|
|
|
if (this.metrics.errors.length > 0) {
|
|
console.log('\nFirst 5 errors:');
|
|
this.metrics.errors.slice(0, 5).forEach(e => console.log(` ${e.observation}: ${e.error}`));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kör
|
|
const replay = new FullReplay();
|
|
replay.run().catch(console.error);
|