diff --git a/apix-admin/src/main/resources/META-INF/resources/admin.js b/apix-admin/src/main/resources/META-INF/resources/admin.js
index e64366b..1cb0f27 100644
--- a/apix-admin/src/main/resources/META-INF/resources/admin.js
+++ b/apix-admin/src/main/resources/META-INF/resources/admin.js
@@ -147,11 +147,9 @@
var buf = new Array(BUCKETS).fill(0);
var lastTotal = null;
- var accDelta = 0; // accumulated since last chart flush
- var lastActivity = Date.now(); // time of last non-zero delta
+ var lastActivity = Date.now();
var currentInterval = FAST_INTERVAL;
- var lastFlushTime = Date.now();
- var lastWindowRpm = 0; // rate from previous flush window
+ var recentPolls = []; // {t, delta} — sliding window entries
var ML = 36, MB = 18;
var W = el.parentElement.clientWidth || 900;
@@ -206,25 +204,25 @@
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() {
- var windowMs = Math.max(Date.now() - lastFlushTime, 1);
- return Math.min(Math.round(accDelta * (60000 / windowMs)), rateLimit);
+ var now = Date.now();
+ 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() {
var view = buf.slice(1).concat([liveRpm()]);
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 () {
- var rpm = liveRpm();
- lastWindowRpm = rpm;
- lastFlushTime = Date.now();
- buf.push(rpm); buf.shift();
- accDelta = 0;
+ buf.push(liveRpm()); buf.shift();
}, CHART_TICK);
async function poll() {
@@ -235,12 +233,13 @@
var total = Number(d.totalRequests) || 0;
if (lastTotal !== null) {
var delta = Math.max(0, total - lastTotal);
- accDelta += delta;
- if (delta > 0) lastActivity = Date.now();
- var windowMs = Date.now() - lastFlushTime;
- var rpm = windowMs > 200 ? liveRpm() : lastWindowRpm;
+ if (delta > 0) {
+ recentPolls.push({t: Date.now(), delta: delta});
+ lastActivity = Date.now();
+ }
+ var rpm = liveRpm();
updateLabel(rpm);
- redraw(); // update chart on every poll, not just every 5 s
+ redraw();
}
lastTotal = total;
currentInterval = (Date.now() - lastActivity > IDLE_AFTER_MS) ? IDLE_INTERVAL : FAST_INTERVAL;
diff --git a/apix-admin/src/main/resources/templates/DashboardResource/global.html b/apix-admin/src/main/resources/templates/DashboardResource/global.html
index 41fcd20..ce8e6d7 100644
--- a/apix-admin/src/main/resources/templates/DashboardResource/global.html
+++ b/apix-admin/src/main/resources/templates/DashboardResource/global.html
@@ -106,6 +106,6 @@ nav {
-
+