fix(admin): sliding-window rate — no sawtooth drop at bucket boundaries
Deploy to Production / deploy (push) Successful in 3m20s
Deploy to Production / deploy (push) Successful in 3m20s
Replace fixed-window accDelta (resets to 0 every 5 s) with a sliding
window: recentPolls[] holds {t, delta} entries; liveRpm() sums all
entries within the last 5 s and extrapolates to rpm. Old entries age
out naturally so the rate decays smoothly with no hard reset jump.
Chart and label now update continuously without the 0-drop artifact.
Co-Authored-By: Mira Rehfeld <noreply@anthropic.com>
This commit is contained in:
@@ -147,11 +147,9 @@
|
|||||||
|
|
||||||
var buf = new Array(BUCKETS).fill(0);
|
var buf = new Array(BUCKETS).fill(0);
|
||||||
var lastTotal = null;
|
var lastTotal = null;
|
||||||
var accDelta = 0; // accumulated since last chart flush
|
var lastActivity = Date.now();
|
||||||
var lastActivity = Date.now(); // time of last non-zero delta
|
|
||||||
var currentInterval = FAST_INTERVAL;
|
var currentInterval = FAST_INTERVAL;
|
||||||
var lastFlushTime = Date.now();
|
var recentPolls = []; // {t, delta} — sliding window entries
|
||||||
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;
|
||||||
@@ -206,25 +204,25 @@
|
|||||||
if (elLive) elLive.textContent = fmt(rpm) + ' req/min (' + pct + '%)';
|
if (elLive) elLive.textContent = fmt(rpm) + ' req/min (' + pct + '%)';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the current in-progress bucket value (capped at rateLimit)
|
// Sliding-window rate: sum of deltas in the last CHART_TICK ms, extrapolated to rpm.
|
||||||
|
// No hard reset at bucket boundaries — rate transitions are smooth.
|
||||||
function liveRpm() {
|
function liveRpm() {
|
||||||
var windowMs = Math.max(Date.now() - lastFlushTime, 1);
|
var now = Date.now();
|
||||||
return Math.min(Math.round(accDelta * (60000 / windowMs)), rateLimit);
|
var cutoff = now - CHART_TICK;
|
||||||
|
while (recentPolls.length && recentPolls[0].t < cutoff) recentPolls.shift();
|
||||||
|
var sum = recentPolls.reduce(function (s, e) { return s + e.delta; }, 0);
|
||||||
|
return Math.min(Math.round(sum * (60000 / CHART_TICK)), rateLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redraw: historical buf + current live bucket at the right edge ("now")
|
// Redraw: historical buf + live bucket at the right edge
|
||||||
function redraw() {
|
function redraw() {
|
||||||
var view = buf.slice(1).concat([liveRpm()]);
|
var view = buf.slice(1).concat([liveRpm()]);
|
||||||
path.attr('d', areaFn(view));
|
path.attr('d', areaFn(view));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every 5 s: commit the current bucket into buf, reset accumulator
|
// Every 5 s: snapshot the current sliding-window rate into the history buffer
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
var rpm = liveRpm();
|
buf.push(liveRpm()); buf.shift();
|
||||||
lastWindowRpm = rpm;
|
|
||||||
lastFlushTime = Date.now();
|
|
||||||
buf.push(rpm); buf.shift();
|
|
||||||
accDelta = 0;
|
|
||||||
}, CHART_TICK);
|
}, CHART_TICK);
|
||||||
|
|
||||||
async function poll() {
|
async function poll() {
|
||||||
@@ -235,12 +233,13 @@
|
|||||||
var total = Number(d.totalRequests) || 0;
|
var total = Number(d.totalRequests) || 0;
|
||||||
if (lastTotal !== null) {
|
if (lastTotal !== null) {
|
||||||
var delta = Math.max(0, total - lastTotal);
|
var delta = Math.max(0, total - lastTotal);
|
||||||
accDelta += delta;
|
if (delta > 0) {
|
||||||
if (delta > 0) lastActivity = Date.now();
|
recentPolls.push({t: Date.now(), delta: delta});
|
||||||
var windowMs = Date.now() - lastFlushTime;
|
lastActivity = Date.now();
|
||||||
var rpm = windowMs > 200 ? liveRpm() : lastWindowRpm;
|
}
|
||||||
|
var rpm = liveRpm();
|
||||||
updateLabel(rpm);
|
updateLabel(rpm);
|
||||||
redraw(); // update chart on every poll, not just every 5 s
|
redraw();
|
||||||
}
|
}
|
||||||
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=9"></script>
|
<script src="/admin.js?v=10"></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=9"></script>
|
<script src="/admin.js?v=10"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user