fix(frontend): drop dead resample trigger/resolutions config

config.ResampleConfig stopped emitting the trigger and resolutions fields,
but MetricPlot still read them. resampleResolutions and resampleMinimum
were therefore always null, which made the array-based resolution branch in
the setScale hook unreachable and the timestep !== resampleMinimum guard
always true. Keep only the policy-based branch and derive the zoom trigger
straight from targetPoints.

configs/config-large.json still carried the removed minimum-points, trigger
and resolutions keys, which DisallowUnknownFields now rejects, so starting
against it aborted. Replace them with the current keys and add a test that
loads the file, so the example configs cannot drift out of the schema
unnoticed again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 12:17:34 +02:00
co-authored by Claude Opus 5
parent a5c1fdf21f
commit 769da61570
4 changed files with 31 additions and 22 deletions
+2 -3
View File
@@ -4,9 +4,8 @@
"short-running-jobs-duration": 300, "short-running-jobs-duration": 300,
"emission-constant": 317, "emission-constant": 317,
"resampling": { "resampling": {
"minimum-points": 600, "default-policy": "medium",
"trigger": 30, "default-algo": "average"
"resolutions": [600, 300, 120, 60]
}, },
"api-subjects": { "api-subjects": {
"subject-job-event": "cc.job.event", "subject-job-event": "cc.job.event",
+19
View File
@@ -41,6 +41,25 @@ func TestInitMinimal(t *testing.T) {
} }
} }
// config-large.json carried the removed resampling keys (minimum-points,
// trigger, resolutions), which DisallowUnknownFields rejects.
func TestInitLarge(t *testing.T) {
fp := "../../configs/config-large.json"
ccconf.Init(fp)
if cfg := ccconf.GetPackageConfig("main"); cfg != nil {
Init(cfg)
} else {
cclog.Abort("Main configuration must be present")
}
if Keys.EnableResampling == nil {
t.Fatal("resampling config missing")
}
if Keys.EnableResampling.DefaultAlgo != "average" {
t.Errorf("wrong default algo\ngot: %s \nwant: average", Keys.EnableResampling.DefaultAlgo)
}
}
func TestTargetPointsForPolicy(t *testing.T) { func TestTargetPointsForPolicy(t *testing.T) {
tests := []struct { tests := []struct {
policy string policy string
@@ -73,10 +73,11 @@
const subClusterTopology = $derived(getContext("getHardwareTopology")(cluster, subCluster)); const subClusterTopology = $derived(getContext("getHardwareTopology")(cluster, subCluster));
const metricConfig = $derived(getContext("getMetricConfig")(cluster, subCluster, metric)); const metricConfig = $derived(getContext("getMetricConfig")(cluster, subCluster, metric));
const usesMeanStatsSeries = $derived((statisticsSeries?.mean && statisticsSeries.mean.length != 0)); const usesMeanStatsSeries = $derived((statisticsSeries?.mean && statisticsSeries.mean.length != 0));
const resampleTrigger = $derived(resampleConfig?.trigger ? Number(resampleConfig.trigger) : (resampleConfig?.targetPoints ? Math.floor(resampleConfig.targetPoints / 4) : null)); const nativeTimestep = $derived(metricConfig?.timestep || timestep);
const resampleResolutions = $derived(resampleConfig?.resolutions ? [...resampleConfig.resolutions] : null);
const resampleMinimum = $derived(resampleConfig?.resolutions ? Math.min(...resampleConfig.resolutions) : null);
const resampleTargetPoints = $derived(resampleConfig?.targetPoints ? Number(resampleConfig.targetPoints) : null); const resampleTargetPoints = $derived(resampleConfig?.targetPoints ? Number(resampleConfig.targetPoints) : null);
// Zoom in far enough that fewer than a quarter of the target point count is
// visible, and a finer resolution is requested from the backend.
const resampleTrigger = $derived(resampleTargetPoints ? Math.floor(resampleTargetPoints / 4) : null);
const useStatsSeries = $derived(!!statisticsSeries); // Display Stats Series By Default if Exists const useStatsSeries = $derived(!!statisticsSeries); // Display Stats Series By Default if Exists
const thresholds = $derived(findJobAggregationThresholds( const thresholds = $derived(findJobAggregationThresholds(
subClusterTopology, subClusterTopology,
@@ -515,21 +516,11 @@
(u, key) => { // If ZoomResample is Configured && Not System/Node View (u, key) => { // If ZoomResample is Configured && Not System/Node View
if (resampleConfig && !forNode && key === 'x') { if (resampleConfig && !forNode && key === 'x') {
const numX = (u.series[0].idxs[1] - u.series[0].idxs[0]) const numX = (u.series[0].idxs[1] - u.series[0].idxs[0])
if (numX <= resampleTrigger && timestep !== resampleMinimum) { if (resampleTargetPoints && numX <= resampleTrigger) {
let newRes; // Compute the resolution for the visible window
if (resampleTargetPoints && !resampleResolutions) { const visibleDuration = (u.scales.x.max - u.scales.x.min);
// Policy-based: compute resolution dynamically from visible window let newRes = Math.ceil(visibleDuration / resampleTargetPoints / nativeTimestep) * nativeTimestep;
const visibleDuration = (u.scales.x.max - u.scales.x.min); if (newRes < nativeTimestep) newRes = nativeTimestep;
const nativeTimestep = metricConfig?.timestep || timestep;
newRes = Math.ceil(visibleDuration / resampleTargetPoints / nativeTimestep) * nativeTimestep;
if (newRes < nativeTimestep) newRes = nativeTimestep;
} else if (resampleResolutions) {
// Array-based: find closest configured resolution
const target = (numX * timestep) / resampleTrigger;
newRes = resampleResolutions.reduce(function(prev, curr) {
return (Math.abs(curr - target) < Math.abs(prev - target) ? curr : prev);
});
}
// Prevents non-required dispatches // Prevents non-required dispatches
if (newRes && timestep !== newRes) { if (newRes && timestep !== newRes) {
onZoom({ onZoom({
+1 -1
View File
@@ -294,7 +294,7 @@ type Page struct {
FilterPresets map[string]any // For pages with the Filter component, this can be used to set initial filters. FilterPresets map[string]any // For pages with the Filter component, this can be used to set initial filters.
Infos map[string]any // For generic use (e.g. username for /monitoring/user/<id>, job id for /monitoring/job/<id>) Infos map[string]any // For generic use (e.g. username for /monitoring/user/<id>, job id for /monitoring/job/<id>)
Config map[string]any // UI settings for the currently logged in user (e.g. line width, ...) Config map[string]any // UI settings for the currently logged in user (e.g. line width, ...)
Resampling *config.ResampleConfig // If not nil, defines resampling trigger and resolutions Resampling *config.ResampleConfig // If not nil, defines the target point count for zoom resampling
Redirect string // The originally requested URL, for intermediate login handling Redirect string // The originally requested URL, for intermediate login handling
FooterLinks FooterLinks // Resolved legal links for the site footer FooterLinks FooterLinks // Resolved legal links for the site footer
} }