From 3ff1a93a5268a4f38d43a6a5ee55159e808330a0 Mon Sep 17 00:00:00 2001 From: Ken Ahrens Date: Wed, 10 Jun 2026 13:02:49 -0400 Subject: [PATCH] Add scheduled error spikes at :10 and :40 each hour Every hour the sim client cranks error injection probability from 5% to 60% for 2 minutes at :10 and :40. This guarantees a visible error spike in any 30-minute demo window without manual intervention. Configurable via ERROR_SPIKE_MINUTES, ERROR_SPIKE_PROBABILITY, ERROR_SPIKE_DURATION, ERROR_SPIKE_ENABLED env vars. Co-Authored-By: Claude Opus 4.6 --- simulation-client/src/config/index.js | 9 +++++++++ simulation-client/src/workflows/SimulationManager.js | 8 ++++++++ simulation-client/src/workflows/UserWorkflow.js | 11 ++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/simulation-client/src/config/index.js b/simulation-client/src/config/index.js index 80fb317..bf473fd 100644 --- a/simulation-client/src/config/index.js +++ b/simulation-client/src/config/index.js @@ -64,6 +64,15 @@ const config = { errorInjection: { enabled: (process.env.ERROR_INJECTION_ENABLED || 'true') === 'true', probability: parseFloat(process.env.ERROR_INJECTION_PROBABILITY) || 0.05 + }, + + // Scheduled error spikes: crank error probability at fixed minutes each hour + // so every 30-min demo window has a visible spike to investigate. + errorSpike: { + enabled: (process.env.ERROR_SPIKE_ENABLED || 'true') === 'true', + probability: parseFloat(process.env.ERROR_SPIKE_PROBABILITY) || 0.6, + minuteMarks: (process.env.ERROR_SPIKE_MINUTES || '10,40').split(',').map(Number), + durationMinutes: parseInt(process.env.ERROR_SPIKE_DURATION) || 2 } }, diff --git a/simulation-client/src/workflows/SimulationManager.js b/simulation-client/src/workflows/SimulationManager.js index 43b972e..2f17c85 100644 --- a/simulation-client/src/workflows/SimulationManager.js +++ b/simulation-client/src/workflows/SimulationManager.js @@ -295,6 +295,7 @@ class SimulationManager { runtimeMs: runtime, activeSessions: this.activeSessions.size, burstActive: this.burstActive, + errorSpikeActive: this.isErrorSpikeActive(), currentConcurrency: this.getCurrentConcurrency(), totalSessions: this.metrics.totalSessions, successfulSessions: this.metrics.successfulSessions, @@ -311,6 +312,13 @@ class SimulationManager { }; } + isErrorSpikeActive() { + const spike = config.simulation.errorSpike; + if (!spike.enabled) return false; + const minute = new Date().getMinutes(); + return spike.minuteMarks.some(m => minute >= m && minute < m + spike.durationMinutes); + } + delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/simulation-client/src/workflows/UserWorkflow.js b/simulation-client/src/workflows/UserWorkflow.js index d08aeaa..daf41a1 100644 --- a/simulation-client/src/workflows/UserWorkflow.js +++ b/simulation-client/src/workflows/UserWorkflow.js @@ -512,7 +512,16 @@ class UserWorkflow { // (The gateway FaultInjectionFilter adds independent 500/503 noise.) async generateErrorTraffic(user) { if (!config.simulation.errorInjection.enabled) return; - if (Math.random() >= config.simulation.errorInjection.probability) return; + + let effectiveProbability = config.simulation.errorInjection.probability; + const spike = config.simulation.errorSpike; + if (spike.enabled) { + const minute = new Date().getMinutes(); + if (spike.minuteMarks.some(m => minute >= m && minute < m + spike.durationMinutes)) { + effectiveProbability = spike.probability; + } + } + if (Math.random() >= effectiveProbability) return; const account = user.accounts && user.accounts.length > 0 ? user.accounts[0] : null;