Merge pull request #528 from ClusterCockpit/hotfix

further clarify plot titles
This commit is contained in:
Jan Eitzinger
2026-03-19 11:44:12 +01:00
committed by GitHub
6 changed files with 31 additions and 22 deletions

View File

@@ -676,10 +676,10 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF
// Use request-scoped cache: multiple aliases with same (filter, groupBy) // Use request-scoped cache: multiple aliases with same (filter, groupBy)
// but different sortBy/page hit the DB only once. // but different sortBy/page hit the DB only once.
if cache := getStatsGroupCache(ctx); cache != nil { if cache := getStatsGroupCache(ctx); cache != nil {
key := statsCacheKey(filter, groupBy) key := statsCacheKey(filter, groupBy, reqFields)
var allStats []*model.JobsStatistics var allStats []*model.JobsStatistics
allStats, err = cache.getOrCompute(key, func() ([]*model.JobsStatistics, error) { allStats, err = cache.getOrCompute(key, func() ([]*model.JobsStatistics, error) {
return r.Repo.JobsStatsGrouped(ctx, filter, nil, nil, groupBy, nil) return r.Repo.JobsStatsGrouped(ctx, filter, nil, nil, groupBy, reqFields)
}) })
if err == nil { if err == nil {
stats = sortAndPageStats(allStats, sortBy, page) stats = sortAndPageStats(allStats, sortBy, page)

View File

@@ -53,9 +53,9 @@ func getStatsGroupCache(ctx context.Context) *statsGroupCache {
return nil return nil
} }
// cacheKey builds a deterministic string key from filter + groupBy. // cacheKey builds a deterministic string key from filter + groupBy + reqFields.
func statsCacheKey(filter []*model.JobFilter, groupBy *model.Aggregate) string { func statsCacheKey(filter []*model.JobFilter, groupBy *model.Aggregate, reqFields map[string]bool) string {
return fmt.Sprintf("%v|%v", filter, *groupBy) return fmt.Sprintf("%v|%v|%v", filter, *groupBy, reqFields)
} }
// getOrCompute returns cached results for the given key, computing them on // getOrCompute returns cached results for the given key, computing them on

View File

@@ -187,6 +187,12 @@
}) })
); );
const topListData = $derived(
$topQuery?.data?.topList
?.toSorted((a, b) => b[sortSelection.key] - a[sortSelection.key])
.slice(0, 10) ?? [],
);
// Note: Different footprints than those saved in DB per Job -> Caused by Legacy Naming // Note: Different footprints than those saved in DB per Job -> Caused by Legacy Naming
let footprintsQuery = $derived( let footprintsQuery = $derived(
queryStore({ queryStore({
@@ -424,10 +430,12 @@
canvasId={`pie-${groupSelection.key}`} canvasId={`pie-${groupSelection.key}`}
size={colWidth1 / 1.9} size={colWidth1 / 1.9}
sliceLabel={sortSelection.label} sliceLabel={sortSelection.label}
quantities={$topQuery.data.topList.map( quantities={topListData.map(
(t) => t[sortSelection.key], (t) => t[sortSelection.key],
)} )}
entities={$topQuery.data.topList.map((t) => scrambleNames ? scramble(t.id) : t.id)} entities={topListData.map((t) =>
scrambleNames ? scramble(t.id) : t.id)
}
/> />
{/if} {/if}
{/key} {/key}
@@ -454,7 +462,7 @@
</select> </select>
</th> </th>
</tr> </tr>
{#each $topQuery.data.topList as te, i} {#each topListData as te, i}
<tr> <tr>
<td><Icon name="circle-fill" style="color: {colors['colorblind'][i]};" /></td> <td><Icon name="circle-fill" style="color: {colors['colorblind'][i]};" /></td>
{#if groupSelection.key == "user"} {#if groupSelection.key == "user"}

View File

@@ -53,7 +53,6 @@
const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false const useCbColors = getContext("cc-config")?.plotConfiguration_colorblindMode || false
/* States */ /* States */
let pagingState = $state({page: 1, itemsPerPage: 10}) // Top 10
let from = $state(new Date(Date.now() - 5 * 60 * 1000)); let from = $state(new Date(Date.now() - 5 * 60 * 1000));
let clusterFrom = $state(new Date(Date.now() - (8 * 60 * 60 * 1000))); let clusterFrom = $state(new Date(Date.now() - (8 * 60 * 60 * 1000)));
let to = $state(new Date(Date.now())); let to = $state(new Date(Date.now()));
@@ -201,11 +200,9 @@
query: gql` query: gql`
query ( query (
$filter: [JobFilter!]! $filter: [JobFilter!]!
$paging: PageRequest!
) { ) {
jobsStatistics( jobsStatistics(
filter: $filter filter: $filter
page: $paging
sortBy: TOTALJOBS sortBy: TOTALJOBS
groupBy: PROJECT groupBy: PROJECT
) { ) {
@@ -215,12 +212,17 @@
} }
`, `,
variables: { variables: {
filter: [{ cluster: { eq: presetCluster} }, { state: ["running"] }], filter: [{ cluster: { eq: presetCluster} }, { state: ["running"] }]
paging: pagingState // Top 10
}, },
requestPolicy: "network-only" requestPolicy: "network-only"
})); }));
const topProjectJobs = $derived(
$topJobsQuery?.data?.jobsStatistics
?.toSorted((a, b) => b.totalJobs - a.totalJobs)
.slice(0, 10) ?? [],
);
const clusterInfo = $derived.by(() => { const clusterInfo = $derived.by(() => {
let rawInfos = {}; let rawInfos = {};
if ($initq?.data?.clusters) { if ($initq?.data?.clusters) {
@@ -365,7 +367,6 @@
from = new Date(Date.now() - 5 * 60 * 1000); from = new Date(Date.now() - 5 * 60 * 1000);
to = new Date(Date.now()); to = new Date(Date.now());
clusterFrom = new Date(Date.now() - (8 * 60 * 60 * 1000)) clusterFrom = new Date(Date.now() - (8 * 60 * 60 * 1000))
pagingState = { page:1, itemsPerPage: 10 };
if (interval) stackedFrom += Math.floor(interval / 1000); if (interval) stackedFrom += Math.floor(interval / 1000);
else stackedFrom += 1 // Workaround: TimeSelection not linked, just trigger new data on manual refresh else stackedFrom += 1 // Workaround: TimeSelection not linked, just trigger new data on manual refresh
@@ -490,10 +491,12 @@
canvasId="hpcpie-jobs-projects" canvasId="hpcpie-jobs-projects"
size={colWidthJobs * 0.75} size={colWidthJobs * 0.75}
sliceLabel={'Jobs'} sliceLabel={'Jobs'}
quantities={$topJobsQuery.data.jobsStatistics.map( quantities={topProjectJobs.map(
(tp) => tp['totalJobs'], (tp) => tp['totalJobs'],
)} )}
entities={$topJobsQuery.data.jobsStatistics.map((tp) => scrambleNames ? scramble(tp.id) : tp.id)} entities={topProjectJobs.map((tp) =>
scrambleNames ? scramble(tp.id) : tp.id)
}
/> />
</div> </div>
</Col> </Col>
@@ -504,7 +507,7 @@
<th style="padding-left: 0.5rem;">Project</th> <th style="padding-left: 0.5rem;">Project</th>
<th>Jobs</th> <th>Jobs</th>
</tr> </tr>
{#each $topJobsQuery.data.jobsStatistics as tp, i} {#each topProjectJobs as tp, i}
<tr> <tr>
<td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td> <td><Icon name="circle-fill" style="color: {legendColors(i)};" /></td>
<td> <td>
@@ -577,7 +580,7 @@
xlabel="Time" xlabel="Time"
ylabel="Nodes" ylabel="Nodes"
yunit = "#Count" yunit = "#Count"
title = "Health States" title = "Metric Health"
stateType = "Health" stateType = "Health"
/> />
{/key} {/key}

View File

@@ -409,14 +409,14 @@
<div> <div>
{#key $statesTimed?.data?.healthStates} {#key $statesTimed?.data?.healthStates}
<h4 class="text-center"> <h4 class="text-center">
{cluster.charAt(0).toUpperCase() + cluster.slice(1)} Health States Over Time {cluster.charAt(0).toUpperCase() + cluster.slice(1)} Metric Health Over Time
</h4> </h4>
<Stacked <Stacked
data={$statesTimed?.data?.healthStates} data={$statesTimed?.data?.healthStates}
xlabel="Time" xlabel="Time"
ylabel="Nodes" ylabel="Nodes"
yunit = "#Count" yunit = "#Count"
title = "Health States" title = "Metric Health"
stateType = "Health" stateType = "Health"
/> />
{/key} {/key}

View File

@@ -45,7 +45,6 @@
const durationBinOptions = ["1m", "10m", "1h", "6h", "12h"]; const durationBinOptions = ["1m", "10m", "1h", "6h", "12h"];
/* State Init */ /* State Init */
let pagingState = $state({ page: 1, itemsPerPage: 10 }); // Top 10
let selectedHistograms = $state([]); // Dummy For Refresh let selectedHistograms = $state([]); // Dummy For Refresh
let colWidthJobs = $state(0); let colWidthJobs = $state(0);
let colWidthNodes = $state(0); let colWidthNodes = $state(0);
@@ -208,7 +207,6 @@
<Refresher <Refresher
initially={120} initially={120}
onRefresh={() => { onRefresh={() => {
pagingState = { page: 1, itemsPerPage: 10 };
selectedHistograms = [...$state.snapshot(selectedHistograms)]; selectedHistograms = [...$state.snapshot(selectedHistograms)];
}} }}
/> />