From ca663e6f00b5a03d220f2f10e2cf458776950b46 Mon Sep 17 00:00:00 2001 From: alchemistReturns Date: Thu, 29 Jan 2026 15:17:45 +0600 Subject: [PATCH] add visual alert for order service latency --- services/api-gateway/src/index.ts | 7 +++++++ services/frontend/src/App.tsx | 21 +++++++++++++++++++ services/order-service/src/index.ts | 32 +++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/services/api-gateway/src/index.ts b/services/api-gateway/src/index.ts index a17a4ac..dd5e7b9 100644 --- a/services/api-gateway/src/index.ts +++ b/services/api-gateway/src/index.ts @@ -70,6 +70,13 @@ app.use('/api/metrics/inventory', createProxyMiddleware({ pathRewrite: { '^/api/metrics/inventory': '/metrics' } })); +// Service Stats (Custom) +app.use('/api/stats/orders', createProxyMiddleware({ + target: ORDER_SERVICE_URL, + changeOrigin: true, + pathRewrite: { '^/api/stats/orders': '/stats' } +})); + app.get('/health', (req, res) => { res.json({ status: 'API Gateway UP' }); }); diff --git a/services/frontend/src/App.tsx b/services/frontend/src/App.tsx index 14c88f7..84e4c11 100644 --- a/services/frontend/src/App.tsx +++ b/services/frontend/src/App.tsx @@ -51,6 +51,20 @@ function App() { return () => clearInterval(interval); }, []); + const [avgLatency, setAvgLatency] = useState(0); + + useEffect(() => { + const statsInterval = setInterval(async () => { + try { + const res = await axios.get(`${API_GATEWAY_URL}/stats/orders`); + setAvgLatency(res.data.averageLatency); + } catch (e) { + console.error("Failed to fetch stats"); + } + }, 2000); + return () => clearInterval(statsInterval); + }, []); + const [queuedOrderIds, setQueuedOrderIds] = useState([]); useEffect(() => { @@ -158,6 +172,13 @@ function App() {
Order Service: {health.order}
+
0.1 ? 'var(--danger)' : 'var(--success)', + color: 'white', + transition: 'background-color 0.3s' + }}> + Avg Latency (30s): {avgLatency.toFixed(2)}s +
diff --git a/services/order-service/src/index.ts b/services/order-service/src/index.ts index 920e927..225f181 100644 --- a/services/order-service/src/index.ts +++ b/services/order-service/src/index.ts @@ -75,6 +75,38 @@ app.use((req, res, next) => { next(); }); +// Rolling Window Stats for Alerting +const WINDOW_SIZE_MS = 30000; +let requestDurations: { time: number, duration: number }[] = []; + +// Periodic Cleanup +setInterval(() => { + const now = Date.now(); + requestDurations = requestDurations.filter(r => now - r.time <= WINDOW_SIZE_MS); +}, 5000); + +app.use((req, res, next) => { + const start = Date.now(); + res.on('finish', () => { + const duration = (Date.now() - start) / 1000; // in seconds + requestDurations.push({ time: Date.now(), duration }); + }); + next(); +}); + +// Stats Endpoint +app.get('/stats', (req, res) => { + const now = Date.now(); + // Ensure we are looking at fresh data (though cleanup runs periodically) + const validDurations = requestDurations.filter(r => now - r.time <= WINDOW_SIZE_MS); + + const count = validDurations.length; + const totalDuration = validDurations.reduce((sum, r) => sum + r.duration, 0); + const average = count > 0 ? totalDuration / count : 0; + + res.json({ averageLatency: average, requestCount: count }); +}); + // Health Check app.get('/health', async (req: Request, res: Response) => { try {