mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-02-18 08:51:45 +01:00
complete review of context initialization and access, streamlining
This commit is contained in:
@@ -39,10 +39,6 @@
|
||||
} = $props();
|
||||
|
||||
/* Const Init */
|
||||
const ccconfig = getContext("cc-config");
|
||||
const initialized = getContext("initialized");
|
||||
const globalMetrics = getContext("globalMetrics");
|
||||
const usePaging = ccconfig?.jobList_usePaging || false;
|
||||
const jobInfoColumnWidth = 250;
|
||||
const client = getContextClient();
|
||||
const query = gql`
|
||||
@@ -100,11 +96,18 @@
|
||||
let headerPaddingTop = $state(0);
|
||||
let jobs = $state([]);
|
||||
let page = $state(1);
|
||||
let itemsPerPage = $state(usePaging ? (ccconfig?.jobList_jobsPerPage || 10) : 10);
|
||||
let triggerMetricRefresh = $state(false);
|
||||
let tableWidth = $state(0);
|
||||
|
||||
/* Derived */
|
||||
const initialized = $derived(getContext("initialized") || false);
|
||||
const ccconfig = $derived(initialized ? getContext("cc-config") : null);
|
||||
const globalMetrics = $derived(initialized ? getContext("globalMetrics") : null);
|
||||
const clusterInfos = $derived(initialized ? getContext("clusters"): null);
|
||||
const resampleConfig = $derived(initialized ? getContext("resampling") : null);
|
||||
const usePaging = $derived(ccconfig?.jobList_usePaging || false);
|
||||
|
||||
let itemsPerPage = $derived(usePaging ? (ccconfig?.jobList_jobsPerPage || 10) : 10);
|
||||
let filter = $derived([...filterBuffer]);
|
||||
let paging = $derived({ itemsPerPage, page });
|
||||
const plotWidth = $derived.by(() => {
|
||||
@@ -274,7 +277,7 @@
|
||||
style="width: {plotWidth}px; padding-top: {headerPaddingTop}px"
|
||||
>
|
||||
{metric}
|
||||
{#if $initialized}
|
||||
{#if initialized}
|
||||
({getUnit(metric)})
|
||||
{/if}
|
||||
</th>
|
||||
@@ -292,7 +295,8 @@
|
||||
</tr>
|
||||
{:else}
|
||||
{#each jobs as job (job.id)}
|
||||
<JobListRow {triggerMetricRefresh} {job} {metrics} {plotWidth} {showFootprint} previousSelect={selectedJobs.includes(job.id)}
|
||||
<JobListRow {triggerMetricRefresh} {job} {metrics} {plotWidth} {showFootprint} {globalMetrics} {clusterInfos} {resampleConfig}
|
||||
previousSelect={selectedJobs.includes(job.id)}
|
||||
selectJob={(detail) => selectedJobs = [...selectedJobs, detail]}
|
||||
unselectJob={(detail) => selectedJobs = selectedJobs.filter(item => item !== detail)}
|
||||
/>
|
||||
|
||||
@@ -30,11 +30,10 @@
|
||||
setFilter
|
||||
} = $props();
|
||||
|
||||
/* Const Init */
|
||||
const clusters = getContext("clusters");
|
||||
const initialized = getContext("initialized");
|
||||
|
||||
/* Derived */
|
||||
const initialized = $derived(getContext("initialized") || false);
|
||||
const clusterInfos = $derived($initialized ? getContext("clusters") : null);
|
||||
let pendingCluster = $derived(presetCluster);
|
||||
let pendingPartition = $derived(presetPartition);
|
||||
</script>
|
||||
@@ -56,7 +55,7 @@
|
||||
>
|
||||
Any Cluster
|
||||
</ListGroupItem>
|
||||
{#each clusters as cluster}
|
||||
{#each clusterInfos as cluster}
|
||||
<ListGroupItem
|
||||
disabled={disableClusterSelection}
|
||||
active={pendingCluster == cluster.name}
|
||||
@@ -80,7 +79,7 @@
|
||||
>
|
||||
Any Partition
|
||||
</ListGroupItem>
|
||||
{#each clusters?.find((c) => c.name == pendingCluster)?.partitions as partition}
|
||||
{#each clusterInfos?.find((c) => c.name == pendingCluster)?.partitions as partition}
|
||||
<ListGroupItem
|
||||
active={pendingPartition == partition}
|
||||
onclick={() => (pendingPartition = partition)}
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
contains: "Contains",
|
||||
}
|
||||
|
||||
const findMaxNumAccels = (clusters) =>
|
||||
clusters.reduce(
|
||||
const findMaxNumAccels = (infos) =>
|
||||
infos.reduce(
|
||||
(max, cluster) =>
|
||||
Math.max(
|
||||
max,
|
||||
@@ -56,8 +56,8 @@
|
||||
);
|
||||
|
||||
// Limited to Single-Node Thread Count
|
||||
const findMaxNumHWThreadsPerNode = (clusters) =>
|
||||
clusters.reduce(
|
||||
const findMaxNumHWThreadsPerNode = (infos) =>
|
||||
infos.reduce(
|
||||
(max, cluster) =>
|
||||
Math.max(
|
||||
max,
|
||||
@@ -92,8 +92,8 @@
|
||||
let threadState = $derived(presetNumHWThreads);
|
||||
let accState = $derived(presetNumAccelerators);
|
||||
|
||||
const clusters = $derived(getContext("clusters"));
|
||||
const initialized = $derived(getContext("initialized"));
|
||||
const initialized = $derived(getContext("initialized") || false);
|
||||
const clusterInfos = $derived($initialized ? getContext("clusters") : null);
|
||||
// Is Selection Active
|
||||
const nodesActive = $derived(!(JSON.stringify(nodesState) === JSON.stringify({ from: 1, to: maxNumNodes })));
|
||||
const threadActive = $derived(!(JSON.stringify(threadState) === JSON.stringify({ from: 1, to: maxNumHWThreads })));
|
||||
@@ -109,12 +109,12 @@
|
||||
$effect(() => {
|
||||
if ($initialized) {
|
||||
if (activeCluster != null) {
|
||||
const { subClusters } = clusters.find((c) => c.name == activeCluster);
|
||||
const { subClusters } = clusterInfos.find((c) => c.name == activeCluster);
|
||||
maxNumAccelerators = findMaxNumAccels([{ subClusters }]);
|
||||
maxNumHWThreads = findMaxNumHWThreadsPerNode([{ subClusters }]);
|
||||
} else if (clusters.length > 0) {
|
||||
maxNumAccelerators = findMaxNumAccels(clusters);
|
||||
maxNumHWThreads = findMaxNumHWThreadsPerNode(clusters);
|
||||
} else if (clusterInfos.length > 0) {
|
||||
maxNumAccelerators = findMaxNumAccels(clusterInfos);
|
||||
maxNumHWThreads = findMaxNumHWThreadsPerNode(clusterInfos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
} = $props();
|
||||
|
||||
/* Derived */
|
||||
const allTags = $derived(getContext("tags"))
|
||||
const initialized = $derived(getContext("initialized"))
|
||||
const initialized = $derived(getContext("initialized") || false)
|
||||
const allTags = $derived($initialized ? getContext("tags") : [])
|
||||
|
||||
/* State Init */
|
||||
let searchTerm = $state("");
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
} = $props();
|
||||
|
||||
/* Derived */
|
||||
const allTags = $derived(getContext('tags'));
|
||||
const initialized = $derived(getContext('initialized'));
|
||||
const initialized = $derived(getContext('initialized') || false);
|
||||
const allTags = $derived($initialized ? getContext('tags') : []);
|
||||
|
||||
/* Effects */
|
||||
$effect(() => {
|
||||
|
||||
@@ -48,8 +48,6 @@
|
||||
const client = getContextClient();
|
||||
|
||||
/* State Init */
|
||||
let initialized = getContext("initialized")
|
||||
let allTags = getContext("tags")
|
||||
let newTagType = $state("");
|
||||
let newTagName = $state("");
|
||||
let filterTerm = $state("");
|
||||
@@ -57,10 +55,13 @@
|
||||
let isOpen = $state(false);
|
||||
|
||||
/* Derived */
|
||||
const initialized = $derived(getContext("initialized") || false );
|
||||
let allTags = $derived(initialized ? getContext("tags") : [])
|
||||
let newTagScope = $derived(username);
|
||||
|
||||
const isAdmin = $derived((roles && authlevel == roles.admin));
|
||||
const isSupport = $derived((roles && authlevel == roles.support));
|
||||
const allTagsFiltered = $derived(($initialized, jobTags, fuzzySearchTags(filterTerm, allTags))); // $init und JobTags only for triggering react
|
||||
const allTagsFiltered = $derived((initialized, jobTags, fuzzySearchTags(filterTerm, allTags))); // $init und JobTags only for triggering react
|
||||
const usedTagsFiltered = $derived(matchJobTags(jobTags, allTagsFiltered, 'used', isAdmin, isSupport));
|
||||
const unusedTagsFiltered = $derived(matchJobTags(jobTags, allTagsFiltered, 'unused', isAdmin, isSupport));
|
||||
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
- `triggerMetricRefresh Bool?`: If changed to true from upstream, will trigger metric query [Default: false]
|
||||
- `selectJob Func`: The callback function to select a job for comparison
|
||||
- `unselectJob Func`: The callback function to unselect a job from comparison
|
||||
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||
- `clusterInfos [Obj]`: Includes the backend supplied cluster topology
|
||||
- `resampleConfig [Obj]`: Includes the backend supplied resampling info
|
||||
-->
|
||||
|
||||
<script>
|
||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||
import { getContext } from "svelte";
|
||||
import { Card, Spinner } from "@sveltestrap/sveltestrap";
|
||||
import { maxScope, checkMetricDisabled } from "../utils.js";
|
||||
import JobInfo from "./JobInfo.svelte";
|
||||
@@ -33,13 +35,13 @@
|
||||
triggerMetricRefresh = false,
|
||||
selectJob,
|
||||
unselectJob,
|
||||
globalMetrics,
|
||||
clusterInfos,
|
||||
resampleConfig
|
||||
} = $props();
|
||||
|
||||
/* Const Init */
|
||||
const client = getContextClient();
|
||||
const cluster = getContext("clusters");
|
||||
const resampleConfig = getContext("resampling") || null;
|
||||
const resampleDefault = resampleConfig ? Math.max(...resampleConfig.resolutions) : 0;
|
||||
const query = gql`
|
||||
query ($id: ID!, $metrics: [String!]!, $scopes: [MetricScope!]!, $selectedResolution: Int) {
|
||||
jobMetrics(id: $id, metrics: $metrics, scopes: $scopes, resolution: $selectedResolution) {
|
||||
@@ -73,11 +75,11 @@
|
||||
`;
|
||||
|
||||
/* State Init */
|
||||
let selectedResolution = $state(resampleDefault);
|
||||
let zoomStates = $state({});
|
||||
let thresholdStates = $state({});
|
||||
|
||||
/* Derived */
|
||||
const resampleDefault = $derived(resampleConfig ? Math.max(...resampleConfig.resolutions) : 0);
|
||||
const jobId = $derived(job?.id);
|
||||
const scopes = $derived.by(() => {
|
||||
if (job.numNodes == 1) {
|
||||
@@ -87,6 +89,8 @@
|
||||
return ["node"];
|
||||
};
|
||||
});
|
||||
|
||||
let selectedResolution = $derived(resampleDefault);
|
||||
let isSelected = $derived(previousSelect);
|
||||
let metricsQuery = $derived(queryStore({
|
||||
client: client,
|
||||
@@ -94,6 +98,7 @@
|
||||
variables: { id: jobId, metrics, scopes, selectedResolution },
|
||||
})
|
||||
);
|
||||
|
||||
const refinedData = $derived($metricsQuery?.data?.jobMetrics ? sortAndSelectScope($metricsQuery.data.jobMetrics) : []);
|
||||
|
||||
/* Effects */
|
||||
@@ -160,6 +165,7 @@
|
||||
return {
|
||||
name: jobMetric.data.name,
|
||||
disabled: checkMetricDisabled(
|
||||
globalMetrics,
|
||||
jobMetric.data.name,
|
||||
job.cluster,
|
||||
job.subCluster,
|
||||
@@ -220,7 +226,7 @@
|
||||
series={metric.data.metric.series}
|
||||
statisticsSeries={metric.data.metric.statisticsSeries}
|
||||
metric={metric.data.name}
|
||||
cluster={cluster.find((c) => c.name == job.cluster)}
|
||||
cluster={clusterInfos.find((c) => c.name == job.cluster)}
|
||||
subCluster={job.subCluster}
|
||||
isShared={job.shared != "none"}
|
||||
numhwthreads={job.numHWThreads}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
- `ìsOpen Bool`: Is selection opened [Bindable]
|
||||
- `configName String`: The config id string to be updated in database on selection change
|
||||
- `presetSelectedHistograms [String]`: The currently selected metrics to display as histogram
|
||||
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||
- `applyChange Func`: The callback function to apply current selection
|
||||
-->
|
||||
|
||||
@@ -24,10 +25,11 @@
|
||||
|
||||
/* Svelte 5 Props */
|
||||
let {
|
||||
cluster,
|
||||
cluster = "",
|
||||
isOpen = $bindable(),
|
||||
configName,
|
||||
presetSelectedHistograms,
|
||||
globalMetrics,
|
||||
applyChange
|
||||
} = $props();
|
||||
|
||||
@@ -42,11 +44,11 @@
|
||||
function loadHistoMetrics(thisCluster) {
|
||||
// isInit Check Removed: Parent Component has finished Init-Query: Globalmetrics available here.
|
||||
if (!thisCluster) {
|
||||
return getContext("globalMetrics")
|
||||
return globalMetrics
|
||||
.filter((gm) => gm?.footprint)
|
||||
.map((fgm) => { return fgm.name })
|
||||
} else {
|
||||
return getContext("globalMetrics")
|
||||
return globalMetrics
|
||||
.filter((gm) => gm?.availability.find((av) => av.cluster == thisCluster))
|
||||
.filter((agm) => agm?.footprint)
|
||||
.map((afgm) => { return afgm.name })
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
- `cluster String?`: The currently selected cluster [Default: null]
|
||||
- `subCluster String?`: The currently selected subCluster [Default: null]
|
||||
- `footprintSelect Bool?`: Render checkbox for footprint display in upstream component [Default: false]
|
||||
- `preInitialized Bool?`: If the parent component has a dedicated call to init() [Default: false]
|
||||
- `configName String`: The config key for the last saved selection (constant)
|
||||
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||
- `applyMetrics Func`: The callback function to apply current selection
|
||||
-->
|
||||
|
||||
<script>
|
||||
import { getContext } from "svelte";
|
||||
import {
|
||||
Modal,
|
||||
ModalBody,
|
||||
@@ -35,14 +34,12 @@
|
||||
cluster = null,
|
||||
subCluster = null,
|
||||
footprintSelect = false,
|
||||
preInitialized = false, // Job View is Pre-Init'd: $initialized "alone" store returns false
|
||||
configName,
|
||||
globalMetrics,
|
||||
applyMetrics
|
||||
} = $props();
|
||||
|
||||
/* Const Init */
|
||||
const globalMetrics = getContext("globalMetrics");
|
||||
const initialized = getContext("initialized");
|
||||
const client = getContextClient();
|
||||
const updateConfigurationMutation = ({ name, value }) => {
|
||||
return mutationStore({
|
||||
@@ -58,27 +55,23 @@
|
||||
|
||||
/* State Init */
|
||||
let pendingShowFootprint = $state(!!showFootprint);
|
||||
let listedMetrics = $state([]);
|
||||
let columnHovering = $state(null);
|
||||
|
||||
/* Derives States */
|
||||
let pendingMetrics = $derived(presetMetrics);
|
||||
const allMetrics = $derived(loadAvailable(preInitialized || $initialized));
|
||||
const allMetrics = $derived(loadAvailable(globalMetrics));
|
||||
let pendingMetrics = $derived(presetMetrics || []);
|
||||
let listedMetrics = $derived([...presetMetrics, ...allMetrics.difference(new Set(presetMetrics))]); // List (preset) active metrics first, then list inactives
|
||||
|
||||
/* Reactive Effects */
|
||||
$effect(() => {
|
||||
totalMetrics = allMetrics?.size || 0;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
listedMetrics = [...presetMetrics, ...allMetrics.difference(new Set(presetMetrics))]; // List (preset) active metrics first, then list inactives
|
||||
});
|
||||
|
||||
/* Functions */
|
||||
function loadAvailable(init) {
|
||||
function loadAvailable(gms) {
|
||||
const availableMetrics = new Set();
|
||||
if (init) {
|
||||
for (let gm of globalMetrics) {
|
||||
if (gms) {
|
||||
for (let gm of gms) {
|
||||
if (!cluster) {
|
||||
availableMetrics.add(gm.name)
|
||||
} else {
|
||||
@@ -90,7 +83,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return availableMetrics
|
||||
return availableMetrics;
|
||||
}
|
||||
|
||||
function printAvailability(metric, cluster) {
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
- `presetSorting Object?`: The latest sort selection state
|
||||
- Default { field: "startTime", type: "col", order: "DESC" }
|
||||
- `isOpen Bool?`: Is modal opened [Bindable, Default: false]
|
||||
- `globalMetrics [Obj]`: Includes the backend supplied availabilities for cluster and subCluster
|
||||
- `applySorting Func`: The callback function to apply current selection
|
||||
-->
|
||||
|
||||
<script>
|
||||
import { getContext, onMount } from "svelte";
|
||||
import {
|
||||
Icon,
|
||||
Button,
|
||||
@@ -25,12 +25,11 @@
|
||||
let {
|
||||
isOpen = $bindable(false),
|
||||
presetSorting = { field: "startTime", type: "col", order: "DESC" },
|
||||
globalMetrics,
|
||||
applySorting
|
||||
} = $props();
|
||||
|
||||
/* Const Init */
|
||||
const initialized = getContext("initialized");
|
||||
const globalMetrics = getContext("globalMetrics");
|
||||
const fixedSortables = $state([
|
||||
{ field: "startTime", type: "col", text: "Start Time (Default)", order: "DESC" },
|
||||
{ field: "duration", type: "col", text: "Duration", order: "DESC" },
|
||||
@@ -42,22 +41,11 @@
|
||||
|
||||
/* State Init */
|
||||
let activeColumnIdx = $state(0);
|
||||
let metricSortables = $state([]);
|
||||
|
||||
/* Derived */
|
||||
let sorting = $derived({...presetSorting})
|
||||
let sortableColumns = $derived([...fixedSortables, ...metricSortables]);
|
||||
|
||||
/* Effect */
|
||||
$effect(() => {
|
||||
if ($initialized) {
|
||||
loadMetricSortables();
|
||||
};
|
||||
});
|
||||
|
||||
/* Functions */
|
||||
function loadMetricSortables() {
|
||||
metricSortables = globalMetrics.map((gm) => {
|
||||
let metricSortables = $derived.by(() => {
|
||||
return globalMetrics.map((gm) => {
|
||||
if (gm?.footprint) {
|
||||
return {
|
||||
field: gm.name + '_' + gm.footprint,
|
||||
@@ -68,8 +56,10 @@
|
||||
}
|
||||
return null
|
||||
}).filter((r) => r != null)
|
||||
};
|
||||
});
|
||||
let sortableColumns = $derived([...fixedSortables, ...metricSortables]);
|
||||
|
||||
/* Functions */
|
||||
function loadActiveIndex() {
|
||||
activeColumnIdx = sortableColumns.findIndex(
|
||||
(col) => col.field == sorting.field,
|
||||
|
||||
@@ -302,19 +302,17 @@ export function stickyHeader(datatableHeaderSelector, updatePading) {
|
||||
onDestroy(() => document.removeEventListener("scroll", onscroll));
|
||||
}
|
||||
|
||||
export function checkMetricDisabled(m, c, s) { // [m]etric, [c]luster, [s]ubcluster
|
||||
const metrics = getContext("globalMetrics");
|
||||
const available = metrics?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
|
||||
export function checkMetricDisabled(gm, m, c, s) { // [g]lobal[m]etrics, [m]etric, [c]luster, [s]ubcluster
|
||||
const available = gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s)
|
||||
// Return inverse logic
|
||||
return !available
|
||||
}
|
||||
|
||||
export function checkMetricsDisabled(ma, c, s) { // [m]etric[a]rray, [c]luster, [s]ubcluster
|
||||
export function checkMetricsDisabled(gm, ma, c, s) { // [g]lobal[m]etrics, [m]etric[a]rray, [c]luster, [s]ubcluster
|
||||
let result = {};
|
||||
const metrics = getContext("globalMetrics");
|
||||
ma.forEach((m) => {
|
||||
// Return named inverse logic: !available
|
||||
result[m] = !(metrics?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
|
||||
result[m] = !(gm?.find((gm) => gm.name === m)?.availability?.find((av) => av.cluster === c)?.subClusters?.includes(s))
|
||||
});
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user