mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-08-31 00:47:15 +02:00
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:
@@ -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",
|
||||||
|
|||||||
@@ -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) {
|
|
||||||
// Policy-based: compute resolution dynamically from visible window
|
|
||||||
const visibleDuration = (u.scales.x.max - u.scales.x.min);
|
const visibleDuration = (u.scales.x.max - u.scales.x.min);
|
||||||
const nativeTimestep = metricConfig?.timestep || timestep;
|
let newRes = Math.ceil(visibleDuration / resampleTargetPoints / nativeTimestep) * nativeTimestep;
|
||||||
newRes = Math.ceil(visibleDuration / resampleTargetPoints / nativeTimestep) * nativeTimestep;
|
|
||||||
if (newRes < nativeTimestep) newRes = 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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user