fix: active sessions query, chart update performance
- Active sessions query now finds truly active sessions (started anytime, no session.end ever) instead of only today's sessions - Use uPlot setData() for live WS updates instead of destroying and recreating the chart on every event - Destroy chart only on window change so it recreates with new scale Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+22
-10
@@ -1137,6 +1137,11 @@
|
|||||||
|
|
||||||
async function loadTimeseries() {
|
async function loadTimeseries() {
|
||||||
try {
|
try {
|
||||||
|
// Destroy chart so it's recreated with new window scale
|
||||||
|
if (dashboardChart) {
|
||||||
|
dashboardChart.destroy();
|
||||||
|
dashboardChart = null;
|
||||||
|
}
|
||||||
const data = await api('/v1/stats/timeseries?window=' + dashboardState.window);
|
const data = await api('/v1/stats/timeseries?window=' + dashboardState.window);
|
||||||
if (!isCurrentPath('/')) return;
|
if (!isCurrentPath('/')) return;
|
||||||
dashboardState.timeseries = data;
|
dashboardState.timeseries = data;
|
||||||
@@ -1146,28 +1151,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildChartData() {
|
||||||
|
const ts = dashboardState.timeseries;
|
||||||
|
if (!ts || !ts.series || ts.series.length === 0) return null;
|
||||||
|
return [
|
||||||
|
ts.series.map(b => Math.floor(new Date(b.ts).getTime() / 1000)),
|
||||||
|
ts.series.map(b => b.runs),
|
||||||
|
ts.series.map(b => b.tools),
|
||||||
|
ts.series.map(b => b.errors),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function renderTimeseriesChart() {
|
function renderTimeseriesChart() {
|
||||||
const container = document.getElementById('dash-chart');
|
const container = document.getElementById('dash-chart');
|
||||||
if (!container || !dashboardState.timeseries) return;
|
if (!container || !dashboardState.timeseries) return;
|
||||||
|
|
||||||
const ts = dashboardState.timeseries;
|
const data = buildChartData();
|
||||||
if (!ts.series || ts.series.length === 0) {
|
if (!data) {
|
||||||
container.innerHTML = '<p class="empty-state" style="padding:2rem">No data for this window</p>';
|
container.innerHTML = '<p class="empty-state" style="padding:2rem">No data for this window</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If chart already exists, just update the data
|
||||||
if (dashboardChart) {
|
if (dashboardChart) {
|
||||||
dashboardChart.destroy();
|
dashboardChart.setData(data);
|
||||||
dashboardChart = null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
const timestamps = ts.series.map(b => Math.floor(new Date(b.ts).getTime() / 1000));
|
|
||||||
const runs = ts.series.map(b => b.runs);
|
|
||||||
const tools = ts.series.map(b => b.tools);
|
|
||||||
const errors = ts.series.map(b => b.errors);
|
|
||||||
|
|
||||||
const width = container.clientWidth || 600;
|
const width = container.clientWidth || 600;
|
||||||
const height = 200;
|
const height = 200;
|
||||||
|
|
||||||
@@ -1217,7 +1229,7 @@
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
dashboardChart = new uPlot(opts, [timestamps, runs, tools, errors], container);
|
dashboardChart = new uPlot(opts, data, container);
|
||||||
|
|
||||||
if (dashboardResizeObserver) {
|
if (dashboardResizeObserver) {
|
||||||
dashboardResizeObserver.disconnect();
|
dashboardResizeObserver.disconnect();
|
||||||
|
|||||||
@@ -36,23 +36,21 @@ func (d *DB) GetSummary(ctx context.Context) (*Summary, error) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
midnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
midnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||||
|
|
||||||
// Active sessions: sessions that started today but have not ended today
|
// Active sessions: sessions with a session.start but no session.end (ever)
|
||||||
activeQ := `
|
activeQ := `
|
||||||
SELECT COUNT(DISTINCT session_id)
|
SELECT COUNT(DISTINCT session_id)
|
||||||
FROM events
|
FROM events
|
||||||
WHERE type = 'session.start'
|
WHERE type = 'session.start'
|
||||||
AND ts >= $1
|
|
||||||
AND session_id IS NOT NULL
|
AND session_id IS NOT NULL
|
||||||
AND session_id NOT IN (
|
AND session_id NOT IN (
|
||||||
SELECT DISTINCT session_id
|
SELECT DISTINCT session_id
|
||||||
FROM events
|
FROM events
|
||||||
WHERE type = 'session.end'
|
WHERE type = 'session.end'
|
||||||
AND ts >= $1
|
|
||||||
AND session_id IS NOT NULL
|
AND session_id IS NOT NULL
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
var activeSessions int
|
var activeSessions int
|
||||||
if err := d.sql.QueryRowContext(ctx, activeQ, midnight).Scan(&activeSessions); err != nil {
|
if err := d.sql.QueryRowContext(ctx, activeQ).Scan(&activeSessions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user