refactor: separate resource refresh from activity sampling

- Remove "Measured X ago" timer display (adds no value)
- Change auto-refresh interval from 30s to 10s for real-time monitoring
- Add JUPYTERHUB_ACTIVITYMON_RESOURCES_UPDATE_INTERVAL env var (default 10s)
- Resource refresh updates status, CPU, memory, timers only
- Activity sampling is separate (controlled by background process)
- Remove formatTimeAgo function and related timestamp tracking
This commit is contained in:
stellarshenson
2026-01-20 20:39:10 +01:00
parent 094be29d6d
commit 9262e30fe8
4 changed files with 10 additions and 45 deletions

View File

@@ -198,3 +198,6 @@ This journal tracks substantive work on documents, diagrams, and documentation c
65. **Task - Activity table sorting and display improvements**: Added column sorting and improved Last Active display<br>
**Result**: Added clickable column sorting for User, CPU, Memory, Time Left, Last Active columns. Sorting cycles through descending → ascending → none (default). Sort icons (▲/▼) show current direction. Default sort is activity score descending. Null values sorted to end. Changed Last Active display to match admin page format with full words ("8 minutes ago", "14 days ago", "4 months ago") with proper singular/plural handling
66. **Task - Activity Monitor resource refresh separation**: Separated resource updates from activity sampling with 10-second refresh interval<br>
**Result**: **Major architectural change** - The Activity Monitor now has a clear separation between two distinct operations: (1) **Resource Updates** - Status (active/inactive/offline), CPU usage, memory usage, and idle culler timers are refreshed automatically every 10 seconds via frontend auto-refresh. This provides real-time monitoring without recording activity history. (2) **Activity Sampling** - Recording of user activity state for historical scoring is completely separate and will be controlled by a background process (not implemented yet). **UI Changes**: Removed "Measured X ago" timer display as it added no value and was confusing when data refreshed automatically. Removed lastMeasuredTimestamp tracking, measureTimeUpdateInterval timer, and formatTimeAgo function. Changed auto-refresh interval from 30 seconds to 10 seconds. **New Environment Variable**: Added `JUPYTERHUB_ACTIVITYMON_RESOURCES_UPDATE_INTERVAL` (default 10 seconds) to Dockerfile and settings_dictionary.yml for configuring resource refresh rate. **Key Clarification**: Viewing the Activity page or auto-refresh does NOT record activity samples - it only reads current state. Activity samples must be recorded by a separate mechanism (to be implemented) that runs independently of page views

View File

@@ -90,6 +90,7 @@ ENV JUPYTERHUB_IDLE_CULLER_MAX_EXTENSION=24
ENV JUPYTERHUB_ACTIVITYMON_RETENTION_DAYS=7
ENV JUPYTERHUB_ACTIVITYMON_HALF_LIFE=24
ENV JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER=60
ENV JUPYTERHUB_ACTIVITYMON_RESOURCES_UPDATE_INTERVAL=10
# Misc
ENV TF_CPP_MIN_LOG_LEVEL=3
ENV STELLARS_JUPYTERHUB_VERSION=${VERSION}

View File

@@ -93,6 +93,10 @@ Activity Monitor:
description: Minutes until user considered inactive (1-1440)
default: "60"
- name: JUPYTERHUB_ACTIVITYMON_RESOURCES_UPDATE_INTERVAL
description: Resource refresh interval in seconds (status, CPU, memory)
default: "10"
Branding:
- name: JUPYTERHUB_LOGO_URI
description: Custom logo URI

View File

@@ -26,7 +26,6 @@
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<span class="badge bg-secondary" id="active-count">0 active servers</span>
<span class="text-muted ms-2" id="last-updated"></span>
</div>
<div>
<button class="btn btn-outline-danger btn-sm me-2" id="reset-btn">
@@ -63,8 +62,6 @@ require(["jquery"], function($) {
"use strict";
var autoRefreshInterval = null;
var lastMeasuredTimestamp = null;
var measureTimeUpdateInterval = null;
var currentUsersData = []; // Store data for re-sorting
var sortColumn = null; // Current sort column
var sortDirection = null; // 'asc', 'desc', or null
@@ -99,16 +96,8 @@ require(["jquery"], function($) {
}
}
// Auto-refresh data every 30 seconds
autoRefreshInterval = setInterval(fetchActivityData, 30000);
// Update "Measured X ago" text every second
measureTimeUpdateInterval = setInterval(function() {
if (lastMeasuredTimestamp) {
var timeAgo = formatTimeAgo(lastMeasuredTimestamp);
$('#last-updated').text(timeAgo ? 'Measured ' + timeAgo : '');
}
}, 1000);
// Auto-refresh resources every 10 seconds (status, CPU, memory, timers)
autoRefreshInterval = setInterval(fetchActivityData, 10000);
// Manual refresh button
$('#refresh-btn').on('click', function() {
@@ -168,8 +157,6 @@ require(["jquery"], function($) {
function renderActivityTable(data) {
var users = data.users || [];
var timestamp = data.timestamp || new Date().toISOString();
var samplingStatus = data.sampling_status || '';
$('#loading-indicator').hide();
@@ -184,14 +171,6 @@ require(["jquery"], function($) {
// Update header info
$('#active-count').text(users.length + ' active server' + (users.length !== 1 ? 's' : ''));
lastMeasuredTimestamp = timestamp;
var timeAgo = formatTimeAgo(timestamp);
$('#last-updated').text(timeAgo ? 'Measured ' + timeAgo : '');
// Show sampling status if available
if (samplingStatus) {
$('#sampling-status').html('<span class="badge bg-info">' + escapeHtml(samplingStatus) + '</span>');
}
// Store data for sorting
currentUsersData = users;
@@ -372,28 +351,6 @@ require(["jquery"], function($) {
return html;
}
function formatTimeAgo(isoString) {
var date = new Date(isoString);
var now = new Date();
var diffMs = now - date;
var diffSec = Math.floor(diffMs / 1000);
var diffMin = Math.floor(diffSec / 60);
var diffHour = Math.floor(diffMin / 60);
if (diffSec <= 0) {
return ''; // Display nothing for 0 or negative
} else if (diffSec < 60) {
return diffSec + 's ago';
} else if (diffMin < 60) {
return diffMin + 'min ago';
} else if (diffHour < 24) {
return diffHour + 'h ago';
} else {
var diffDay = Math.floor(diffHour / 24);
return diffDay + 'd ago';
}
}
function getCookie(name) {
var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
if (match) return match[2];