fix(admin): smooth live rate label — window-accumulated rate, not instantaneous
Deploy to Production / deploy (push) Successful in 3m16s

With 1 s polls and batch traffic, delta * 60 flickers between 0 and 300.
Fix: live label shows accDelta / elapsed_in_window * 60000 — the rate
builds up continuously within each 5 s bucket. First 200 ms after flush
carries the previous window rate to avoid a zero jump.

Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
This commit is contained in:
Carsten Rehfeld
2026-05-17 01:20:15 +02:00
parent de597f1d8b
commit 435e1bda42
3 changed files with 15 additions and 5 deletions
@@ -150,6 +150,8 @@
var accDelta = 0; // accumulated since last chart flush var accDelta = 0; // accumulated since last chart flush
var lastActivity = Date.now(); // time of last non-zero delta var lastActivity = Date.now(); // time of last non-zero delta
var currentInterval = FAST_INTERVAL; var currentInterval = FAST_INTERVAL;
var lastFlushTime = Date.now();
var lastWindowRpm = 0; // rate from previous flush window
var ML = 36, MB = 18; var ML = 36, MB = 18;
var W = el.parentElement.clientWidth || 900; var W = el.parentElement.clientWidth || 900;
@@ -207,7 +209,11 @@
// Chart bucket: flush accumulated delta every 5 s regardless of poll rate // Chart bucket: flush accumulated delta every 5 s regardless of poll rate
// Cap at rateLimit so the area never visually exceeds 100% (live label shows true value) // Cap at rateLimit so the area never visually exceeds 100% (live label shows true value)
setInterval(function () { setInterval(function () {
var rpm = Math.min(Math.round(accDelta * (60000 / CHART_TICK)), rateLimit); var now = Date.now();
var elapsed = now - lastFlushTime;
var rpm = Math.min(Math.round(accDelta * (60000 / (elapsed || CHART_TICK))), rateLimit);
lastWindowRpm = rpm;
lastFlushTime = now;
buf.push(rpm); buf.shift(); buf.push(rpm); buf.shift();
path.attr('d', areaFn(buf)); path.attr('d', areaFn(buf));
accDelta = 0; accDelta = 0;
@@ -223,8 +229,12 @@
var delta = Math.max(0, total - lastTotal); var delta = Math.max(0, total - lastTotal);
accDelta += delta; accDelta += delta;
if (delta > 0) lastActivity = Date.now(); if (delta > 0) lastActivity = Date.now();
var rpm = elapsed > 0 ? Math.round(delta * (60000 / elapsed)) : 0; // Use accumulated rate over current window — smooth, not instantaneous per-poll delta
updateLabel(rpm); var windowMs = Date.now() - lastFlushTime;
var liveRpm = windowMs > 200
? Math.round(accDelta * (60000 / windowMs))
: lastWindowRpm; // too early in window — carry previous
updateLabel(liveRpm);
} }
lastTotal = total; lastTotal = total;
currentInterval = (Date.now() - lastActivity > IDLE_AFTER_MS) ? IDLE_INTERVAL : FAST_INTERVAL; currentInterval = (Date.now() - lastActivity > IDLE_AFTER_MS) ? IDLE_INTERVAL : FAST_INTERVAL;
@@ -106,6 +106,6 @@ nav {
<script>var __D = {statsJson.raw};</script> <script>var __D = {statsJson.raw};</script>
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
<script src="/admin.js?v=7"></script> <script src="/admin.js?v=8"></script>
</body> </body>
</html> </html>
@@ -151,6 +151,6 @@ button.promote:hover { background: #2ea043; }
<script>var __D = {dataJson.raw};</script> <script>var __D = {dataJson.raw};</script>
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
<script src="/admin.js?v=7"></script> <script src="/admin.js?v=8"></script>
</body> </body>
</html> </html>