From 96f9c5584d52e4d05a0c68c70ded7988320f507f Mon Sep 17 00:00:00 2001 From: Ji-minhyeok Date: Mon, 27 Apr 2026 23:26:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=EB=8C=80=EA=B8=B0=EC=97=B4=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=88=9C=EC=84=9C=20=EB=B3=B4=EC=A1=B4=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A7=84=EB=8B=A8=20=EB=A1=9C=EA=B7=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue/LegacyLessonAdmissionScheduler.java | 4 +- .../issue/LessonAdmissionScheduler.java | 44 +++++++++++++++---- .../issue/LessonWaitingRoomService.java | 7 ++- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/threestar/trainus/domain/lesson/issue/LegacyLessonAdmissionScheduler.java b/src/main/java/com/threestar/trainus/domain/lesson/issue/LegacyLessonAdmissionScheduler.java index bb243802..25dbec2d 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/issue/LegacyLessonAdmissionScheduler.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/issue/LegacyLessonAdmissionScheduler.java @@ -46,7 +46,7 @@ public void admitUsers() { private void processAdmissionForLesson(Long lessonId) { // 해당 레슨 대기열에서 인원 추출 - Set requestIds = waitingRoomService.dequeue(lessonId, ADMIT_BATCH_SIZE); + List requestIds = waitingRoomService.dequeue(lessonId, ADMIT_BATCH_SIZE); if (requestIds.isEmpty()) { return; @@ -69,7 +69,7 @@ private void processAdmissionForLesson(Long lessonId) { if (parts.length < 3) continue; - String requestId = requestIds.toArray(new String[0])[i]; + String requestId = requestIds.get(i); Long userId = Long.parseLong(parts[2]); Long originalTimestamp = parts.length >= 4 ? Long.parseLong(parts[3]) : System.currentTimeMillis(); diff --git a/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonAdmissionScheduler.java b/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonAdmissionScheduler.java index 60965cec..9219319f 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonAdmissionScheduler.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonAdmissionScheduler.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; @@ -56,7 +57,7 @@ private void processAdmissionForLesson(Long lessonId) { } // 해당 레슨 대기열에서 인원 추출 - Set requestIds = waitingRoomService.dequeue(lessonId, ADMIT_BATCH_SIZE); + List requestIds = waitingRoomService.dequeue(lessonId, ADMIT_BATCH_SIZE); if (requestIds.isEmpty()) { return; @@ -68,7 +69,16 @@ private void processAdmissionForLesson(Long lessonId) { .toList(); List statusInfos = mqRedisTemplate.opsForValue().multiGet(statusKeys); - if (statusInfos == null) return; + if (statusInfos == null) { + log.warn( + "Admission diagnostics. lessonId={}, dequeuedCount={}, statusKeyCount={}, statusInfos=null. Admission skipped after dequeue.", + lessonId, requestIds.size(), statusKeys.size()); + return; + } + + AtomicInteger statusNullCount = new AtomicInteger(); + AtomicInteger invalidStatusCount = new AtomicInteger(); + AtomicInteger xaddAttemptCount = new AtomicInteger(); // Pipelining 방식으로 요청 상태 변경 / Stream ADD 로직 일괄 처리 mqRedisTemplate.executePipelined(new SessionCallback() { @@ -76,14 +86,27 @@ private void processAdmissionForLesson(Long lessonId) { public Object execute(RedisOperations operations) { for (int i = 0; i < statusKeys.size(); i++) { String info = statusInfos.get(i); - if (info == null) continue; + if (info == null) { + statusNullCount.incrementAndGet(); + continue; + } String[] parts = info.split(":"); - if (parts.length < 3) continue; - - String requestId = requestIds.toArray(new String[0])[i]; - Long userId = Long.parseLong(parts[2]); - Long originalTimestamp = parts.length >= 4 ? Long.parseLong(parts[3]) : System.currentTimeMillis(); + if (parts.length < 3) { + invalidStatusCount.incrementAndGet(); + continue; + } + + String requestId = requestIds.get(i); + Long userId; + Long originalTimestamp; + try { + userId = Long.parseLong(parts[2]); + originalTimestamp = parts.length >= 4 ? Long.parseLong(parts[3]) : System.currentTimeMillis(); + } catch (NumberFormatException e) { + invalidStatusCount.incrementAndGet(); + continue; + } // 상태 변경 (SET) String statusKey = statusKeys.get(i); @@ -97,11 +120,16 @@ public Object execute(RedisOperations operations) { content.put("requestId", requestId); content.put("timestamp", String.valueOf(originalTimestamp)); operations.opsForStream().add(LessonApplyStreamConstant.STREAM_KEY, content); + xaddAttemptCount.incrementAndGet(); } return null; } }); + log.info( + "Admission diagnostics. lessonId={}, dequeuedCount={}, statusKeyCount={}, statusNullCount={}, invalidStatusCount={}, xaddAttemptCount={}", + lessonId, requestIds.size(), statusKeys.size(), statusNullCount.get(), invalidStatusCount.get(), + xaddAttemptCount.get()); log.debug("Admitted {} users to MQ via pipeline for lesson: {}", requestIds.size(), lessonId); } } diff --git a/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonWaitingRoomService.java b/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonWaitingRoomService.java index f020627f..fc25b7a2 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonWaitingRoomService.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/issue/LessonWaitingRoomService.java @@ -1,6 +1,8 @@ package com.threestar.trainus.domain.lesson.issue; import java.time.Duration; +import java.util.Comparator; +import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Qualifier; @@ -51,12 +53,13 @@ public Optional getRank(Long lessonId, String requestId) { } // 특정 레슨 대기열에서 가장 오래된 N명을 꺼내기 - public java.util.Set dequeue(Long lessonId, long count) { + public List dequeue(Long lessonId, long count) { String waitingRoomKey = String.format(LessonApplyStreamConstant.WAITING_ROOM_KEY, lessonId); return coreRedisTemplate.opsForZSet() .popMin(waitingRoomKey, count) .stream() + .sorted(Comparator.comparing(tuple -> tuple.getScore() == null ? Double.MAX_VALUE : tuple.getScore())) .map(tuple -> tuple.getValue()) - .collect(java.util.stream.Collectors.toSet()); + .toList(); } } From 0962545e2ece3922a71c6231fa7827271fe27f28 Mon Sep 17 00:00:00 2001 From: Ji-minhyeok Date: Tue, 28 Apr 2026 23:54:29 +0900 Subject: [PATCH 2/2] =?UTF-8?q?chore:=20Grafana=20=EB=A7=88=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=ED=8C=A8=EB=84=90=20=EA=B5=AC=EC=84=B1?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../grafana/dashboards/trainus-dashboard.json | 838 ++++++++++++++++-- 1 file changed, 776 insertions(+), 62 deletions(-) diff --git a/prometheus-grafana/grafana/dashboards/trainus-dashboard.json b/prometheus-grafana/grafana/dashboards/trainus-dashboard.json index 84dc8187..38786f09 100644 --- a/prometheus-grafana/grafana/dashboards/trainus-dashboard.json +++ b/prometheus-grafana/grafana/dashboards/trainus-dashboard.json @@ -3,7 +3,10 @@ "list": [ { "builtIn": 1, - "datasource": { "type": "grafana", "uid": "-- Grafana --" }, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", @@ -15,187 +18,898 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 1, - "id": null, + "id": 4, "links": [], "panels": [ { "collapsed": false, - "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, "id": 100, "panels": [], "title": "System Resources (Threads & Connections) [$env]", "type": "row" }, { - "datasource": { "type": "prometheus", "uid": "prometheus-default" }, + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, "description": "API 서버의 Tomcat 스레드 상태 (Busy, Current, Max)", "fieldConfig": { "defaults": { - "color": { "mode": "palette-classic" }, - "custom": { "drawStyle": "line", "fillOpacity": 10, "lineWidth": 2 }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, "unit": "short" - } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 }, - "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 1, "options": { - "legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" }, - "tooltip": { "mode": "multi", "sort": "none" } + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } }, + "pluginVersion": "11.5.2", "targets": [ - { "expr": "tomcat_threads_busy_threads{env=\"$env\", role=\"api\"}", "legendFormat": "Busy: {{instance}}", "refId": "A" }, - { "expr": "tomcat_threads_current_threads{env=\"$env\", role=\"api\"}", "legendFormat": "Current: {{instance}}", "refId": "B" }, - { "expr": "tomcat_threads_config_max_threads{env=\"$env\", role=\"api\"}", "legendFormat": "Max: {{instance}}", "refId": "C" } + { + "expr": "tomcat_threads_busy_threads{env=\"$env\", role=\"api\"}", + "legendFormat": "Busy: {{instance}}", + "refId": "A" + }, + { + "expr": "tomcat_threads_current_threads{env=\"$env\", role=\"api\"}", + "legendFormat": "Current: {{instance}}", + "refId": "B" + }, + { + "expr": "tomcat_threads_config_max_threads{env=\"$env\", role=\"api\"}", + "legendFormat": "Max: {{instance}}", + "refId": "C" + } ], "title": "API Tomcat Threads", "type": "timeseries" }, { - "datasource": { "type": "prometheus", "uid": "prometheus-default" }, + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, "description": "API 서버의 HikariCP 커넥션 풀 상태 (Active, Idle, Pending)", "fieldConfig": { "defaults": { - "color": { "mode": "palette-classic" }, - "custom": { "drawStyle": "line", "fillOpacity": 15, "lineWidth": 2 }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 15, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, "unit": "short" - } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 }, - "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 3, "options": { - "legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" }, - "tooltip": { "mode": "multi", "sort": "none" } + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } }, + "pluginVersion": "11.5.2", "targets": [ - { "expr": "hikaricp_connections_active{env=\"$env\", role=\"api\"}", "legendFormat": "Active: {{instance}}", "refId": "A" }, - { "expr": "hikaricp_connections_idle{env=\"$env\", role=\"api\"}", "legendFormat": "Idle: {{instance}}", "refId": "B" }, - { "expr": "hikaricp_connections_pending{env=\"$env\", role=\"api\"}", "legendFormat": "Pending: {{instance}}", "refId": "C" } + { + "expr": "hikaricp_connections_active{env=\"$env\", role=\"api\"}", + "legendFormat": "Active: {{instance}}", + "refId": "A" + }, + { + "expr": "hikaricp_connections_idle{env=\"$env\", role=\"api\"}", + "legendFormat": "Idle: {{instance}}", + "refId": "B" + }, + { + "expr": "hikaricp_connections_pending{env=\"$env\", role=\"api\"}", + "legendFormat": "Pending: {{instance}}", + "refId": "C" + } ], "title": "API HikariCP Connections", "type": "timeseries" }, { "collapsed": false, - "gridPos": { "h": 1, "w": 24, "x": 0, "y": 9 }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, "id": 101, "panels": [], "title": "Core Business Metrics (MQ & Defense) [$env]", "type": "row" }, { - "datasource": { "type": "prometheus", "uid": "prometheus-default" }, + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, "fieldConfig": { "defaults": { - "color": { "mode": "palette-classic" }, - "custom": { "drawStyle": "line", "fillOpacity": 10, "lineWidth": 2 }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, "unit": "s" - } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 10 }, - "gridPos": { "h": 8, "w": 16, "x": 0, "y": 10 }, "id": 120, "options": { - "legend": { "calcs": ["max", "lastNotNull"], "displayMode": "table", "placement": "bottom" }, - "tooltip": { "mode": "single" } + "legend": { + "calcs": [ + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } }, + "pluginVersion": "11.5.2", "targets": [ - { "expr": "max(lesson_apply_latency_seconds_max{env=\"$env\"}) by (instance, instance_id)", "legendFormat": "Latency: {{instance}}", "refId": "A" } + { + "expr": "max(lesson_apply_latency_seconds_max{env=\"$env\"}) by (instance, instance_id)", + "legendFormat": "Latency: {{instance}}", + "refId": "A" + } ], "title": "Async Processing Latency (Max)", "type": "timeseries" }, { - "datasource": { "type": "prometheus", "uid": "prometheus-default" }, + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, "fieldConfig": { "defaults": { - "color": { "mode": "thresholds" }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ - { "color": "green", "value": null }, - { "color": "red", "value": 1 } + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } ] - } + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 10 + }, + "id": 122, + "options": { + "legend": { + "calcs": [ + "max", + "lastNotNull", + "mean" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "editorMode": "code", + "expr": " sum by (instance, instance_id) (\n rate(lesson_apply_latency_seconds_sum{env=\"$env\"}[$__rate_interval])\n )\n /\n sum by (instance, instance_id) (\n rate(lesson_apply_latency_seconds_count{env=\"$env\"}[$__rate_interval])\n )", + "legendFormat": "Latency: {{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Async Processing Latency (average))", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 10 + }, + "id": 123, + "options": { + "legend": { + "calcs": [ + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", + "targets": [ + { + "editorMode": "code", + "expr": "sum(\n rate(lesson_apply_latency_seconds_count{env=\"$env\"}[$__rate_interval])\n)", + "legendFormat": "Latency: {{instance}}", + "range": true, + "refId": "A" } + ], + "title": "Async Processing Throughput", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 10 }, - "gridPos": { "h": 8, "w": 8, "x": 16, "y": 10 }, "id": 121, "options": { - "legend": { "calcs": ["max", "lastNotNull"], "displayMode": "table", "placement": "bottom" }, - "tooltip": { "mode": "single" } + "legend": { + "calcs": [ + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } }, + "pluginVersion": "11.5.2", "targets": [ - { "expr": "sum(max_over_time(lesson_apply_total{env=\"$env\", result=\"pre_filter_reject\"}[$__range]) - min_over_time(lesson_apply_total{env=\"$env\", result=\"pre_filter_reject\"}[$__range]))", "legendFormat": "Rejected (Exact Count)", "refId": "A" } + { + "expr": "sum(max_over_time(lesson_apply_total{env=\"$env\", result=\"pre_filter_reject\"}[$__range]) - min_over_time(lesson_apply_total{env=\"$env\", result=\"pre_filter_reject\"}[$__range]))", + "legendFormat": "Rejected (Exact Count)", + "refId": "A" + } ], "title": "[Defend] Pre-filter Reject (Real-time Steps)", "type": "timeseries" }, { "collapsed": false, - "gridPos": { "h": 1, "w": 24, "x": 0, "y": 18 }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, "id": 102, "panels": [], "title": "Throughput & Latency (HTTP) [$env]", "type": "row" }, { - "datasource": { "type": "prometheus", "uid": "prometheus-default" }, + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, "fieldConfig": { "defaults": { - "color": { "mode": "palette-classic" }, - "custom": { "drawStyle": "line", "lineWidth": 2 }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, "unit": "reqps" - } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 19 }, - "gridPos": { "h": 8, "w": 12, "x": 0, "y": 19 }, "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", "targets": [ - { "expr": "sum(rate(http_server_requests_seconds_count{env=\"$env\"}[1m])) by (uri, instance)", "legendFormat": "{{uri}} ({{instance}})", "refId": "A" } + { + "expr": "sum(rate(http_server_requests_seconds_count{env=\"$env\"}[1m])) by (uri, instance)", + "legendFormat": "{{uri}} ({{instance}})", + "refId": "A" + } ], "title": "Request Throughput (RPS)", "type": "timeseries" }, { - "datasource": { "type": "prometheus", "uid": "prometheus-default" }, + "datasource": { + "type": "prometheus", + "uid": "prometheus-default" + }, "fieldConfig": { "defaults": { - "color": { "mode": "palette-classic" }, - "custom": { "drawStyle": "line", "lineWidth": 2 }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, "unit": "s" - } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "/health (172.31.3.198:8082)" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 19 }, - "gridPos": { "h": 8, "w": 12, "x": 12, "y": 19 }, "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.2", "targets": [ - { "expr": "rate(http_server_requests_seconds_sum{env=\"$env\"}[1m]) / rate(http_server_requests_seconds_count{env=\"$env\"}[1m])", "legendFormat": "{{uri}} ({{instance}})", "refId": "A" } + { + "expr": "rate(http_server_requests_seconds_sum{env=\"$env\"}[1m]) / rate(http_server_requests_seconds_count{env=\"$env\"}[1m])", + "legendFormat": "{{uri}} ({{instance}})", + "refId": "A" + } ], "title": "Average Response Time", "type": "timeseries" } ], + "preload": false, "refresh": "5s", "schemaVersion": 40, + "tags": [], "templating": { "list": [ { - "current": { "selected": true, "text": "local", "value": "local" }, - "hide": 0, + "current": { + "text": "production", + "value": "production" + }, "includeAll": false, "label": "Environment", - "multi": false, "name": "env", "options": [ - { "selected": true, "text": "local", "value": "local" }, - { "selected": false, "text": "production", "value": "production" }, - { "selected": false, "text": "baseline", "value": "baseline" } + { + "selected": false, + "text": "local", + "value": "local" + }, + { + "selected": true, + "text": "production", + "value": "production" + }, + { + "selected": false, + "text": "baseline", + "value": "baseline" + } ], "query": "local,production,baseline", "type": "custom" } ] }, - "time": { "from": "now-15m", "to": "now" }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", "title": "TrainUs Master Dashboard", "uid": "trainus-master-v1", - "version": 4 + "version": 12, + "weekStart": "" }