From a89e615052b42ae241483398bd206df1d57fc63f Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 19 Aug 2026 13:20:09 +0100 Subject: [PATCH 1/2] fix: normalise dashboard TPS graph by the configured refresh interval The Transactions per second chart plotted the raw xact_commit/xact_rollback delta between two polls without dividing by the elapsed time, so the value was only correct when the refresh interval was 1 second; at any other interval it showed transactions per interval instead of per second. Closes #10273 --- web/pgadmin/dashboard/static/js/Graphs.jsx | 17 ++++++++++++++--- .../javascript/dashboard/graphs_spec.js | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/web/pgadmin/dashboard/static/js/Graphs.jsx b/web/pgadmin/dashboard/static/js/Graphs.jsx index 7f2eae22825..aed2ea25f76 100644 --- a/web/pgadmin/dashboard/static/js/Graphs.jsx +++ b/web/pgadmin/dashboard/static/js/Graphs.jsx @@ -64,16 +64,27 @@ export function statsReducer(state, action) { action.counterData = action.incoming; } + /* When the counter represents a rate (e.g. transactions per second), + * the raw delta between two polls must be normalised by the number of + * seconds elapsed between them, otherwise it only reads correctly when + * the refresh interval happens to be 1 second. + */ + let rate = action.rate || 1; + let newState = {}; Object.keys(action.incoming).forEach(label => { + let value = action.incoming[label]; + if(action.counter) { + value = (action.incoming[label] - action.counterData[label]) / rate; + } if(state[label]) { newState[label] = [ - action.counter ? action.incoming[label] - action.counterData[label] : action.incoming[label], + value, ...state[label].slice(0, X_AXIS_LENGTH-1), ]; } else { newState[label] = [ - action.counter ? action.incoming[label] - action.counterData[label] : action.incoming[label], + value, ]; } }); @@ -169,7 +180,7 @@ export default function Graphs({preferences, sid, did, pageVisible, enablePoll=t let data = resp.data; setErrorMsg(null); sessionStatsReduce({incoming: data['session_stats']}); - tpsStatsReduce({incoming: data['tps_stats'], counter: true, counterData: counterData['tps_stats']}); + tpsStatsReduce({incoming: data['tps_stats'], counter: true, counterData: counterData['tps_stats'], rate: preferences['tps_stats_refresh']}); tiStatsReduce({incoming: data['ti_stats'], counter: true, counterData: counterData['ti_stats']}); toStatsReduce({incoming: data['to_stats'], counter: true, counterData: counterData['to_stats']}); bioStatsReduce({incoming: data['bio_stats'], counter: true, counterData: counterData['bio_stats']}); diff --git a/web/regression/javascript/dashboard/graphs_spec.js b/web/regression/javascript/dashboard/graphs_spec.js index d02cd3488fa..6e2aa4a5b0d 100644 --- a/web/regression/javascript/dashboard/graphs_spec.js +++ b/web/regression/javascript/dashboard/graphs_spec.js @@ -72,6 +72,25 @@ describe('Graphs.js', ()=>{ expect(state).toEqual(newState); }); + it('with incoming with counter and rate', ()=>{ + let state = { + 'Label1': [1], 'Label2': [2], + }; + let action = { + incoming: { + 'Label1': 11, 'Label2': 23, + }, + counter: true, + counterData: {'Label1': 1, 'Label2': 3}, + rate: 5, + }; + let newState = { + 'Label1': [2, 1], 'Label2': [4, 2], + }; + state = statsReducer(state, action); + expect(state).toEqual(newState); + }); + it('with reset', ()=>{ let state = { 'Label1': [0, 1], 'Label2': [1, 2], From 9748b320f7052900c10416f2ad266ea560c92306 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 19 Aug 2026 13:30:31 +0100 Subject: [PATCH 2/2] fix: reset TPS counter baseline when the refresh interval changes Changing the tps_stats_refresh preference reset the displayed TPS history but kept the previous absolute counter reading, so the next delta was computed against a stale baseline whilst being divided by the new interval, mis-scaling the first post-change data point. Addresses CodeRabbit review on #10324 (issue #10273). --- web/pgadmin/dashboard/static/js/Graphs.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web/pgadmin/dashboard/static/js/Graphs.jsx b/web/pgadmin/dashboard/static/js/Graphs.jsx index aed2ea25f76..30175820c30 100644 --- a/web/pgadmin/dashboard/static/js/Graphs.jsx +++ b/web/pgadmin/dashboard/static/js/Graphs.jsx @@ -125,6 +125,14 @@ export default function Graphs({preferences, sid, did, pageVisible, enablePoll=t } if(prevPrefernces['tps_stats_refresh'] != preferences['tps_stats_refresh']) { tpsStatsReduce({reset:chartsDefault['tps_stats']}); + /* The rate divisor is changing, so the previous counter baseline + * can no longer be used to compute the next delta. + */ + setCounterData((prevCounterData)=>{ + const nextCounterData = {...prevCounterData}; + delete nextCounterData['tps_stats']; + return nextCounterData; + }); calcPollDelay = true; } if(prevPrefernces['ti_stats_refresh'] != preferences['ti_stats_refresh']) {