From 3686fa24e00dacb8aecf9fff0857a49d910759ab Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 9 Mar 2023 15:03:18 -0500 Subject: [PATCH] Convert status injectables to be for a Map instead of object - Allows us to use the data during further computations instead of doing it all again - Large speed wins Signed-off-by: Sebastian Malton --- .../statuses.injectable.ts | 6 +-- .../statuses.injectable.ts | 41 +++---------------- .../+workloads-jobs/statuses.injectable.ts | 6 +-- .../overview-workload-status.tsx | 38 +++++++++++------ .../workloads/workload-injection-token.ts | 13 +++++- .../+workloads-pods/statuses.injectable.ts | 5 ++- .../statuses-by-owner-id.injectable.ts | 32 +++++++++++++++ .../statuses.injectable.ts | 6 +-- .../statuses.injectable.ts | 6 +-- .../utils/compute-status-counts.injectable.ts | 37 +++++------------ 10 files changed, 101 insertions(+), 89 deletions(-) create mode 100644 packages/core/src/renderer/components/+workloads-replicasets/statuses-by-owner-id.injectable.ts diff --git a/packages/core/src/renderer/components/+workloads-daemonsets/statuses.injectable.ts b/packages/core/src/renderer/components/+workloads-daemonsets/statuses.injectable.ts index bb63dbb641..42e7b9935f 100644 --- a/packages/core/src/renderer/components/+workloads-daemonsets/statuses.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-daemonsets/statuses.injectable.ts @@ -4,16 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; +import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable"; import daemonSetStoreInjectable from "./store.injectable"; const totalStatusesForDaemonSetsInSelectedNamespacesInjectable = getInjectable({ id: "total-statuses-for-daemon-sets-in-selected-namespaces", instantiate: (di) => { const daemonSetStore = di.inject(daemonSetStoreInjectable); - const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); + const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable); - return computed(() => computeStatusCountsForOwners(daemonSetStore.contextItems)); + return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(daemonSetStore.contextItems)); }, }); diff --git a/packages/core/src/renderer/components/+workloads-deployments/statuses.injectable.ts b/packages/core/src/renderer/components/+workloads-deployments/statuses.injectable.ts index ebdcb883e5..6fc387b823 100644 --- a/packages/core/src/renderer/components/+workloads-deployments/statuses.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-deployments/statuses.injectable.ts @@ -4,48 +4,19 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import podStoreInjectable from "../+workloads-pods/store.injectable"; -import type { Deployment, PodStatusPhase } from "../../../common/k8s-api/endpoints"; -import { byLabels } from "../../../common/k8s-api/kube-object.store"; -import { getOrInsert } from "../../utils"; -import { foldPodStatusPhase } from "../../utils/fold-pod-status-phase"; +import statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable from "../+workloads-replicasets/statuses-by-owner-id.injectable"; import deploymentStoreInjectable from "./store.injectable"; const statusCountsForAllDeploymentsInSelectedNamespacesInjectable = getInjectable({ id: "status-counts-for-all-deployments-in-selected-namespaces", instantiate: (di) => { const deploymentStore = di.inject(deploymentStoreInjectable); - const podStore = di.inject(podStoreInjectable); + const replicaSetStatuses = di.inject(statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable); - return computed(() => { - const statuses = { running: 0, failed: 0, pending: 0 }; - const podsByNamespace = new Map> }; status: PodStatusPhase }[]>(); - - for (const pod of podStore.items) { - getOrInsert(podsByNamespace, pod.getNs(), []).push({ - metadata: { - labels: JSON.parse(JSON.stringify(pod.metadata.labels ?? {})), - }, - status: pod.getStatus(), - }); - } - - const getChildPods = (deployment: Deployment) => { - const pods = podsByNamespace.get(deployment.getNs()) ?? []; - - return pods.filter(byLabels(deployment.spec.template.metadata.labels)); - }; - - for (const deployment of deploymentStore.contextItems) { - const status = getChildPods(deployment) - .map(pod => pod.status) - .reduce(foldPodStatusPhase, "running"); - - statuses[status]++; - } - - return statuses; - }); + return computed(() => new Map(deploymentStore.contextItems.map(deployment => [ + deployment.getId(), + replicaSetStatuses.get().get(deployment.getId()) ?? [], + ]))); }, }); diff --git a/packages/core/src/renderer/components/+workloads-jobs/statuses.injectable.ts b/packages/core/src/renderer/components/+workloads-jobs/statuses.injectable.ts index 40ec25a3ed..4f718c7f90 100644 --- a/packages/core/src/renderer/components/+workloads-jobs/statuses.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-jobs/statuses.injectable.ts @@ -4,16 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; +import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable"; import jobStoreInjectable from "./store.injectable"; const statusCountsForAllJobsInSelectedNamespacesInjectable = getInjectable({ id: "status-counts-for-all-jobs-in-selected-namespaces", instantiate: (di) => { const jobStore = di.inject(jobStoreInjectable); - const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); + const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable); - return computed(() => computeStatusCountsForOwners(jobStore.contextItems)); + return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(jobStore.contextItems)); }, }); diff --git a/packages/core/src/renderer/components/+workloads-overview/overview-workload-status.tsx b/packages/core/src/renderer/components/+workloads-overview/overview-workload-status.tsx index 41d947b326..d65244ed83 100644 --- a/packages/core/src/renderer/components/+workloads-overview/overview-workload-status.tsx +++ b/packages/core/src/renderer/components/+workloads-overview/overview-workload-status.tsx @@ -10,17 +10,20 @@ import capitalize from "lodash/capitalize"; import { observer } from "mobx-react"; import type { PieChartData } from "../chart"; import { PieChart } from "../chart"; -import { object } from "../../utils"; +import { iter, object } from "../../utils"; import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { PascalCase } from "type-fest"; import type { IComputedValue } from "mobx"; import activeThemeInjectable from "../../themes/active.injectable"; import type { Workload } from "./workloads/workload-injection-token"; +import { foldWorkloadStatusPhase } from "../../utils/fold-workload-status-phase"; export type LowercaseOrPascalCase = Lowercase | PascalCase; -export type WorkloadStatus = Partial, number>>; +export type WorkloadStatusCounts = Partial, number>>; + +export type WorkloadStatus = keyof typeof backgroundColourMapping; function toLowercase(src: T): Lowercase { return src.toLowerCase() as Lowercase; @@ -34,18 +37,24 @@ interface Dependencies { activeTheme: IComputedValue; } -const statusBackgroundColorMapping = { - "running": "colorOk", - "scheduled": "colorOk", - "pending": "colorWarning", +const cronJobStatusBackgroundColourMapping = { "suspended": "colorWarning", - "evicted": "colorError", - "succeeded": "colorSuccess", + "scheduled": "colorOk", +} as const; + +const podStatusBackgroundColourMapping = { "failed": "colorError", + "evicted": "colorError", + "pending": "colorWarning", + "running": "colorOk", + "succeeded": "colorSuccess", "terminated": "colorTerminated", - "terminating": "colorTerminated", "unknown": "colorVague", - "complete": "colorSuccess", +} as const; + +const backgroundColourMapping = { + ...podStatusBackgroundColourMapping, + ...cronJobStatusBackgroundColourMapping, } as const; const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatusProps & Dependencies) => { @@ -54,7 +63,12 @@ const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatu activeTheme, } = props; - const statusesToBeShown = object.entries(workload.status.get()).filter(([, val]) => val > 0); + const statuses = workload.status.get(); + const statusCounts = iter.chain(statuses.values()) + .map(phases => phases.reduce(foldWorkloadStatusPhase, "unknown")) + .count(); + + const statusesToBeShown = object.entries(statusCounts).filter(([, val]) => val > 0); const theme = activeTheme.get(); const emptyDataSet = { @@ -66,7 +80,7 @@ const NonInjectedOverviewWorkloadStatus = observer((props: OverviewWorkloadStatu label: "Status", data: statusesToBeShown.map(([, value]) => value), backgroundColor: statusesToBeShown.map(([status]) => ( - theme.colors[statusBackgroundColorMapping[toLowercase(status)]] + theme.colors[backgroundColourMapping[toLowercase(status)]] )), tooltipLabels: statusesToBeShown.map(([status]) => ( (percent: string) => `${capitalize(status)}: ${percent}` diff --git a/packages/core/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts b/packages/core/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts index fca19148a7..3f543dd399 100644 --- a/packages/core/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts +++ b/packages/core/src/renderer/components/+workloads-overview/workloads/workload-injection-token.ts @@ -5,13 +5,22 @@ import { getInjectionToken } from "@ogre-tools/injectable"; import type { IComputedValue } from "mobx"; import type { KubeApiResourceDescriptor } from "../../../../common/rbac"; -import type { WorkloadStatus } from "../overview-workload-status"; + +export type WorkloadStatusPhase = + "Terminated" + | "Failed" + | "Pending" + | "Running" + | "Succeeded" + | "Evicted" + | "Suspended" + | "Scheduled"; export interface Workload { resource: KubeApiResourceDescriptor; open: () => void; amountOfItems: IComputedValue; - status: IComputedValue; + status: IComputedValue>; title: string; orderNumber: number; } diff --git a/packages/core/src/renderer/components/+workloads-pods/statuses.injectable.ts b/packages/core/src/renderer/components/+workloads-pods/statuses.injectable.ts index 6dc88c1a7a..2ed2845511 100644 --- a/packages/core/src/renderer/components/+workloads-pods/statuses.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-pods/statuses.injectable.ts @@ -11,7 +11,10 @@ const totalStatusesForPodsInSelectedNamespacesInjectable = getInjectable({ instantiate: (di) => { const podStore = di.inject(podStoreInjectable); - return computed(() => podStore.getStatuses(podStore.contextItems)); + return computed(() => new Map(podStore.contextItems.map(pod => [ + pod.getId(), + [pod.getStatus()], + ]))); }, }); diff --git a/packages/core/src/renderer/components/+workloads-replicasets/statuses-by-owner-id.injectable.ts b/packages/core/src/renderer/components/+workloads-replicasets/statuses-by-owner-id.injectable.ts new file mode 100644 index 0000000000..3a96e407b7 --- /dev/null +++ b/packages/core/src/renderer/components/+workloads-replicasets/statuses-by-owner-id.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import type { PodStatusPhase } from "../../../common/k8s-api/endpoints"; +import { getOrInsert } from "../../utils"; +import statusCountsForAllReplicaSetsInSelectedNamespacesInjectable from "./statuses.injectable"; +import replicaSetStoreInjectable from "./store.injectable"; + +const statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable = getInjectable({ + id: "statuses-of-replica-sets-by-owner-ids-in-selected-namespaces", + instantiate: (di) => { + const replicaSetStore = di.inject(replicaSetStoreInjectable); + const statuses = di.inject(statusCountsForAllReplicaSetsInSelectedNamespacesInjectable); + + return computed(() => { + const result = new Map(); + + for (const replicaSet of replicaSetStore.contextItems) { + for (const ownerRef of replicaSet.getOwnerRefs()) { + getOrInsert(result, ownerRef.uid, []).push(...statuses.get().get(replicaSet.getId()) ?? []); + } + } + + return result; + }); + }, +}); + +export default statusesOfReplicaSetsByOwnerIdsInSelectedNamespacesInjectable; diff --git a/packages/core/src/renderer/components/+workloads-replicasets/statuses.injectable.ts b/packages/core/src/renderer/components/+workloads-replicasets/statuses.injectable.ts index 6bc0f7f995..4bb26101d2 100644 --- a/packages/core/src/renderer/components/+workloads-replicasets/statuses.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-replicasets/statuses.injectable.ts @@ -5,16 +5,16 @@ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; +import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable"; import replicaSetStoreInjectable from "./store.injectable"; const statusCountsForAllReplicaSetsInSelectedNamespacesInjectable = getInjectable({ id: "status-counts-for-all-replica-sets-in-selected-namespaces", instantiate: (di) => { const replicaSetStore = di.inject(replicaSetStoreInjectable); - const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); + const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable); - return computed(() => computeStatusCountsForOwners(replicaSetStore.contextItems)); + return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(replicaSetStore.contextItems)); }, }); diff --git a/packages/core/src/renderer/components/+workloads-statefulsets/statuses.injectable.ts b/packages/core/src/renderer/components/+workloads-statefulsets/statuses.injectable.ts index 6f4ace1f2b..06178e367b 100644 --- a/packages/core/src/renderer/components/+workloads-statefulsets/statuses.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-statefulsets/statuses.injectable.ts @@ -4,16 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import computeStatusCountsForOwnersInjectable from "../../utils/compute-status-counts.injectable"; +import computeStatusesOfObjectsBasedOnOwnedPodsInjectable from "../../utils/compute-status-counts.injectable"; import statefulSetStoreInjectable from "./store.injectable"; const totalStatusesForStatefulSetsInSelectedNamespacesInjectable = getInjectable({ id: "total-statuses-for-stateful-sets-in-selected-namespaces", instantiate: (di) => { const statefulSetStore = di.inject(statefulSetStoreInjectable); - const computeStatusCountsForOwners = di.inject(computeStatusCountsForOwnersInjectable); + const computeStatusesOfObjectsBasedOnOwnedPods = di.inject(computeStatusesOfObjectsBasedOnOwnedPodsInjectable); - return computed(() => computeStatusCountsForOwners(statefulSetStore.contextItems)); + return computed(() => computeStatusesOfObjectsBasedOnOwnedPods(statefulSetStore.contextItems)); }, }); diff --git a/packages/core/src/renderer/utils/compute-status-counts.injectable.ts b/packages/core/src/renderer/utils/compute-status-counts.injectable.ts index 514a8d7703..545af8bfad 100644 --- a/packages/core/src/renderer/utils/compute-status-counts.injectable.ts +++ b/packages/core/src/renderer/utils/compute-status-counts.injectable.ts @@ -5,52 +5,35 @@ import type { PodStatusPhase } from "../../common/k8s-api/endpoints"; import { getOrInsert } from "../../common/utils"; -import { foldPodStatusPhase } from "./fold-pod-status-phase"; import { getInjectable } from "@ogre-tools/injectable"; import podStoreInjectable from "../components/+workloads-pods/store.injectable"; import { computed } from "mobx"; import type { KubeObject } from "../../common/k8s-api/kube-object"; -export interface StatusCounts { - running: number; - failed: number; - pending: number; -} - -const computeStatusCountsForOwnersInjectable = getInjectable({ - id: "compute-status-counts-for-owners", +const computeStatusesOfObjectsBasedOnOwnedPodsInjectable = getInjectable({ + id: "compute-statuses-of-objects-based-on-owned-pods", instantiate: (di) => { const podStore = di.inject(podStoreInjectable); - const podsByOwnerId = computed(() => { - const podsByOwnerId = new Map(); + const podStatusesByOwnerId = computed(() => { + const podsByOwnerId = new Map(); for (const pod of podStore.contextItems) { for (const ownerRef of pod.getOwnerRefs()) { - getOrInsert(podsByOwnerId, ownerRef.uid, []).push({ - status: pod.getStatus(), - }); + getOrInsert(podsByOwnerId, ownerRef.uid, []).push(pod.getStatus()); } } return podsByOwnerId; }); - return (possibleOwners: KubeObject[]): StatusCounts => { - const statuses = { running: 0, failed: 0, pending: 0 }; + return (owners: KubeObject[]) => new Map(owners.map(owner => { + const statuses = podStatusesByOwnerId.get().get(owner.getId()) ?? []; - for (const possibleOwner of possibleOwners) { - const status = (podsByOwnerId.get().get(possibleOwner.getId()) ?? []) - .map(pod => pod.status) - .reduce(foldPodStatusPhase, "running"); - - statuses[status]++; - } - - return statuses; - }; + return [owner.getId(), statuses]; + })); }, }); -export default computeStatusCountsForOwnersInjectable; +export default computeStatusesOfObjectsBasedOnOwnedPodsInjectable;