From 2e8ca761038d8787d16b9670a327b668f436fc8b Mon Sep 17 00:00:00 2001 From: Anso Date: Mon, 4 May 2026 13:36:01 -0400 Subject: [PATCH] fix(backend): batch-insert stress test metrics to avoid per-insert fsync timeout (#911) --- .../src/__tests__/database-metrics.test.ts | 25 ++++++++++--------- backend/src/services/DatabaseService.ts | 12 +++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/backend/src/__tests__/database-metrics.test.ts b/backend/src/__tests__/database-metrics.test.ts index 89a3d8be..1162dfb0 100644 --- a/backend/src/__tests__/database-metrics.test.ts +++ b/backend/src/__tests__/database-metrics.test.ts @@ -347,18 +347,19 @@ describe('DatabaseService - stress tests', () => { it('handles 1000+ metrics and cleanup bounds growth', () => { const now = Date.now(); - // Insert 1200 metrics spread across 2 hours - for (let i = 0; i < 1200; i++) { - db.addContainerMetric({ - container_id: `stress-container-${i % 10}`, - stack_name: 'stress-stack', - cpu_percent: Math.random() * 100, - memory_mb: Math.random() * 1024, - net_rx_mb: Math.random() * 10, - net_tx_mb: Math.random() * 10, - timestamp: now - (i * 6000), // Every 6 seconds over ~2 hours - }); - } + // Insert 1200 metrics spread across 2 hours in a single transaction. + // Individual auto-committed inserts trigger a disk fsync each, making + // 1200 separate calls impractically slow on spinning disks and Windows. + const metrics = Array.from({ length: 1200 }, (_, i) => ({ + container_id: `stress-container-${i % 10}`, + stack_name: 'stress-stack', + cpu_percent: Math.random() * 100, + memory_mb: Math.random() * 1024, + net_rx_mb: Math.random() * 10, + net_tx_mb: Math.random() * 10, + timestamp: now - (i * 6000), // Every 6 seconds over ~2 hours + })); + db.bulkAddContainerMetrics(metrics); // Cleanup with 1 hour retention db.cleanupOldMetrics(1); diff --git a/backend/src/services/DatabaseService.ts b/backend/src/services/DatabaseService.ts index 4ce1e73f..d103f3da 100644 --- a/backend/src/services/DatabaseService.ts +++ b/backend/src/services/DatabaseService.ts @@ -1798,6 +1798,18 @@ export class DatabaseService { stmt.run(metric.container_id, metric.stack_name, metric.cpu_percent, metric.memory_mb, metric.net_rx_mb, metric.net_tx_mb, metric.timestamp); } + public bulkAddContainerMetrics(metrics: Omit[]): void { + const stmt = this.db.prepare( + 'INSERT INTO container_metrics (container_id, stack_name, cpu_percent, memory_mb, net_rx_mb, net_tx_mb, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)' + ); + const insertAll = this.db.transaction((items: Omit[]) => { + for (const m of items) { + stmt.run(m.container_id, m.stack_name, m.cpu_percent, m.memory_mb, m.net_rx_mb, m.net_tx_mb, m.timestamp); + } + }); + insertAll(metrics); + } + public getContainerMetrics(hoursLookback = 24): any[] { const cutoff = Date.now() - (hoursLookback * 60 * 60 * 1000); // Aggregate into 5-minute buckets (300000ms) to keep response size bounded.