diff --git a/configs/config-large.json b/configs/config-large.json index f57b32d1..79ba02a3 100644 --- a/configs/config-large.json +++ b/configs/config-large.json @@ -4,9 +4,8 @@ "short-running-jobs-duration": 300, "emission-constant": 317, "resampling": { - "minimum-points": 600, - "trigger": 30, - "resolutions": [600, 300, 120, 60] + "default-policy": "medium", + "default-algo": "average" }, "api-subjects": { "subject-job-event": "cc.job.event", diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 14eb570f..a2a3fb3d 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -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) { tests := []struct { policy string diff --git a/web/frontend/src/generic/plots/MetricPlot.svelte b/web/frontend/src/generic/plots/MetricPlot.svelte index e937f32f..63e31950 100644 --- a/web/frontend/src/generic/plots/MetricPlot.svelte +++ b/web/frontend/src/generic/plots/MetricPlot.svelte @@ -73,10 +73,11 @@ const subClusterTopology = $derived(getContext("getHardwareTopology")(cluster, subCluster)); const metricConfig = $derived(getContext("getMetricConfig")(cluster, subCluster, metric)); 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 resampleResolutions = $derived(resampleConfig?.resolutions ? [...resampleConfig.resolutions] : null); - const resampleMinimum = $derived(resampleConfig?.resolutions ? Math.min(...resampleConfig.resolutions) : null); + const nativeTimestep = $derived(metricConfig?.timestep || timestep); 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 thresholds = $derived(findJobAggregationThresholds( subClusterTopology, @@ -515,21 +516,11 @@ (u, key) => { // If ZoomResample is Configured && Not System/Node View if (resampleConfig && !forNode && key === 'x') { const numX = (u.series[0].idxs[1] - u.series[0].idxs[0]) - if (numX <= resampleTrigger && timestep !== resampleMinimum) { - let newRes; - if (resampleTargetPoints && !resampleResolutions) { - // Policy-based: compute resolution dynamically from visible window - const visibleDuration = (u.scales.x.max - u.scales.x.min); - 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); - }); - } + if (resampleTargetPoints && numX <= resampleTrigger) { + // Compute the resolution for the visible window + const visibleDuration = (u.scales.x.max - u.scales.x.min); + let newRes = Math.ceil(visibleDuration / resampleTargetPoints / nativeTimestep) * nativeTimestep; + if (newRes < nativeTimestep) newRes = nativeTimestep; // Prevents non-required dispatches if (newRes && timestep !== newRes) { onZoom({ diff --git a/web/web.go b/web/web.go index 685256f9..4e695386 100644 --- a/web/web.go +++ b/web/web.go @@ -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. Infos map[string]any // For generic use (e.g. username for /monitoring/user/, job id for /monitoring/job/) 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 FooterLinks FooterLinks // Resolved legal links for the site footer }