Skip to content

Commit 7512198

Browse files
authored
feat(stats): shows ephemeral and persistent storage separately on pie chart (#647)
refs #645, #665
1 parent 71e7eec commit 7512198

File tree

9 files changed

+267
-132
lines changed

9 files changed

+267
-132
lines changed

apps/api/src/routes/v1/networkCapacity.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ const route = createRoute({
2828
totalCPU: z.number(),
2929
totalGPU: z.number(),
3030
totalMemory: z.number(),
31-
totalStorage: z.number()
31+
totalStorage: z.number(),
32+
activeEphemeralStorage: z.number(),
33+
pendingEphemeralStorage: z.number(),
34+
availableEphemeralStorage: z.number(),
35+
activePersistentStorage: z.number(),
36+
pendingPersistentStorage: z.number(),
37+
availablePersistentStorage: z.number(),
3238
})
3339
}
3440
}

apps/api/src/routes/v1/providers/byAddress.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
33
import { getProviderDetail } from "@src/services/db/providerStatusService";
44
import { openApiExampleProviderAddress } from "@src/utils/constants";
55

6+
const statsItemSchema = () => z.object({
7+
active: z.number(),
8+
available: z.number(),
9+
pending: z.number(),
10+
});
611
const route = createRoute({
712
method: "get",
813
path: "/providers/{address}",
@@ -46,6 +51,15 @@ const route = createRoute({
4651
isOnline: z.boolean(),
4752
lastOnlineDate: z.string().nullable(),
4853
isAudited: z.boolean(),
54+
stats: z.object({
55+
cpu: statsItemSchema(),
56+
gpu: statsItemSchema(),
57+
memory: statsItemSchema(),
58+
storage: z.object({
59+
ephemeral: statsItemSchema(),
60+
persistent: statsItemSchema(),
61+
})
62+
}),
4963
activeStats: z.object({
5064
cpu: z.number(),
5165
gpu: z.number(),

apps/api/src/services/db/providerStatusService.ts

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Provider, ProviderAttribute, ProviderAttributeSignature, ProviderSnapshotNode, ProviderSnapshotNodeGPU } from "@akashnetwork/database/dbSchemas/akash";
22
import { ProviderSnapshot } from "@akashnetwork/database/dbSchemas/akash/providerSnapshot";
33
import { add, sub } from "date-fns";
4+
import uniqBy from "lodash/uniqBy";
45
import { Op } from "sequelize";
56

67
import { ProviderDetail } from "@src/types/provider";
@@ -24,31 +25,58 @@ export async function getNetworkCapacity() {
2425
]
2526
});
2627

27-
const filteredProviders = providers.filter((value, index, self) => self.map(x => x.hostUri).indexOf(value.hostUri) === index);
28+
const filteredProviders = uniqBy(providers, provider => provider.hostUri);
29+
const stats = filteredProviders.reduce(
30+
(all, provider) => {
31+
stats.activeCPU += provider.lastSuccessfulSnapshot.activeCPU;
32+
stats.pendingCPU += provider.lastSuccessfulSnapshot.pendingCPU;
33+
stats.availableCPU += provider.lastSuccessfulSnapshot.availableCPU;
2834

29-
const stats = {
30-
activeProviderCount: filteredProviders.length,
31-
activeCPU: filteredProviders.map(x => x.lastSuccessfulSnapshot.activeCPU).reduce((a, b) => a + b, 0),
32-
activeGPU: filteredProviders.map(x => x.lastSuccessfulSnapshot.activeGPU).reduce((a, b) => a + b, 0),
33-
activeMemory: filteredProviders.map(x => x.lastSuccessfulSnapshot.activeMemory).reduce((a, b) => a + b, 0),
34-
activeStorage: filteredProviders
35-
.map(x => x.lastSuccessfulSnapshot.activeEphemeralStorage + x.lastSuccessfulSnapshot.activePersistentStorage)
36-
.reduce((a, b) => a + b, 0),
37-
pendingCPU: filteredProviders.map(x => x.lastSuccessfulSnapshot.pendingCPU).reduce((a, b) => a + b, 0),
38-
pendingGPU: filteredProviders.map(x => x.lastSuccessfulSnapshot.pendingGPU).reduce((a, b) => a + b, 0),
39-
pendingMemory: filteredProviders.map(x => x.lastSuccessfulSnapshot.pendingMemory).reduce((a, b) => a + b, 0),
40-
pendingStorage: filteredProviders
41-
.map(x => x.lastSuccessfulSnapshot.pendingEphemeralStorage + x.lastSuccessfulSnapshot.pendingPersistentStorage)
42-
.reduce((a, b) => a + b, 0),
43-
availableCPU: filteredProviders.map(x => x.lastSuccessfulSnapshot.availableCPU).reduce((a, b) => a + b, 0),
44-
availableGPU: filteredProviders.map(x => x.lastSuccessfulSnapshot.availableGPU).reduce((a, b) => a + b, 0),
45-
availableMemory: filteredProviders.map(x => x.lastSuccessfulSnapshot.availableMemory).reduce((a, b) => a + b, 0),
46-
availableStorage: filteredProviders
47-
.map(x => x.lastSuccessfulSnapshot.availableEphemeralStorage + x.lastSuccessfulSnapshot.availablePersistentStorage)
48-
.reduce((a, b) => a + b, 0)
49-
};
35+
stats.activeGPU += provider.lastSuccessfulSnapshot.activeGPU;
36+
stats.pendingGPU += provider.lastSuccessfulSnapshot.pendingGPU;
37+
stats.availableGPU += provider.lastSuccessfulSnapshot.availableGPU;
38+
39+
stats.activeMemory += provider.lastSuccessfulSnapshot.activeMemory;
40+
stats.pendingMemory += provider.lastSuccessfulSnapshot.pendingMemory;
41+
stats.availableMemory += provider.lastSuccessfulSnapshot.availableMemory;
42+
43+
stats.activeEphemeralStorage += provider.lastSuccessfulSnapshot.activeEphemeralStorage;
44+
stats.pendingEphemeralStorage += provider.lastSuccessfulSnapshot.pendingEphemeralStorage;
45+
stats.availableEphemeralStorage += provider.lastSuccessfulSnapshot.availableEphemeralStorage;
46+
47+
stats.activePersistentStorage += provider.lastSuccessfulSnapshot.activePersistentStorage;
48+
stats.pendingPersistentStorage += provider.lastSuccessfulSnapshot.pendingPersistentStorage;
49+
stats.availablePersistentStorage += provider.lastSuccessfulSnapshot.availablePersistentStorage;
50+
51+
return all;
52+
},
53+
{
54+
activeCPU: 0,
55+
pendingCPU: 0,
56+
availableCPU: 0,
57+
activeGPU: 0,
58+
pendingGPU: 0,
59+
availableGPU: 0,
60+
activeMemory: 0,
61+
pendingMemory: 0,
62+
availableMemory: 0,
63+
activeStorage: 0,
64+
pendingStorage: 0,
65+
availableStorage: 0,
66+
activeEphemeralStorage: 0,
67+
pendingEphemeralStorage: 0,
68+
availableEphemeralStorage: 0,
69+
activePersistentStorage: 0,
70+
pendingPersistentStorage: 0,
71+
availablePersistentStorage: 0
72+
}
73+
);
5074

5175
return {
76+
activeProviderCount: filteredProviders.length,
77+
activeStorage: stats.activeEphemeralStorage + stats.activePersistentStorage,
78+
pendingStorage: stats.pendingEphemeralStorage + stats.pendingPersistentStorage,
79+
availableStorage: stats.availableEphemeralStorage + stats.availablePersistentStorage,
5280
...stats,
5381
totalCPU: stats.activeCPU + stats.pendingCPU + stats.availableCPU,
5482
totalGPU: stats.activeGPU + stats.pendingGPU + stats.availableGPU,
@@ -96,10 +124,8 @@ export const getProviderList = async () => {
96124
});
97125

98126
const distinctProviders = providersWithAttributesAndAuditors.filter((value, index, self) => self.map(x => x.hostUri).lastIndexOf(value.hostUri) === index);
99-
const providerAttributeSchemaQuery = getProviderAttributesSchema();
100-
const auditorsQuery = getAuditors();
101127

102-
const [auditors, providerAttributeSchema] = await Promise.all([auditorsQuery, providerAttributeSchemaQuery]);
128+
const [auditors, providerAttributeSchema] = await Promise.all([getAuditors(), getProviderAttributesSchema()]);
103129

104130
return distinctProviders.map(x => {
105131
const lastSuccessfulSnapshot = providerWithNodes.find(p => p.owner === x.owner)?.lastSuccessfulSnapshot;
@@ -151,10 +177,7 @@ export const getProviderDetail = async (address: string): Promise<ProviderDetail
151177
})
152178
: null;
153179

154-
const providerAttributeSchemaQuery = getProviderAttributesSchema();
155-
const auditorsQuery = getAuditors();
156-
157-
const [auditors, providerAttributeSchema] = await Promise.all([auditorsQuery, providerAttributeSchemaQuery]);
180+
const [auditors, providerAttributeSchema] = await Promise.all([getAuditors(), getProviderAttributesSchema()]);
158181

159182
return {
160183
...mapProviderToList(provider, providerAttributeSchema, auditors, lastSuccessfulSnapshot),

apps/api/src/types/provider.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,30 @@ export interface ProviderList {
2424
lastOnlineDate: Date;
2525
isAudited: boolean;
2626
gpuModels: { vendor: string; model: string; ram: string; interface: string }[];
27+
stats: {
28+
cpu: StatsItem;
29+
gpu: StatsItem;
30+
memory: StatsItem;
31+
storage: {
32+
ephemeral: StatsItem;
33+
persistent: StatsItem;
34+
},
35+
};
36+
/** @deprecated use `stats` instead */
2737
activeStats: {
2838
cpu: number;
2939
gpu: number;
3040
memory: number;
3141
storage: number;
3242
};
43+
/** @deprecated use `stats` instead */
3344
pendingStats: {
3445
cpu: number;
3546
gpu: number;
3647
memory: number;
3748
storage: number;
3849
};
50+
/** @deprecated use `stats` instead */
3951
availableStats: {
4052
cpu: number;
4153
gpu: number;
@@ -130,3 +142,9 @@ export type Auditor = {
130142
address: string;
131143
website: string;
132144
};
145+
146+
export interface StatsItem {
147+
active: number;
148+
available: number;
149+
pending: number;
150+
}

apps/api/src/utils/map/provider.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Provider, ProviderSnapshot, ProviderSnapshotNode } from "@akashnetwork/database/dbSchemas/akash";
22
import semver from "semver";
33

4-
import { Auditor, ProviderAttributesSchema, ProviderList } from "@src/types/provider";
4+
import { Auditor, ProviderAttributesSchema, ProviderList, StatsItem } from "@src/types/provider";
55
import { createFilterUnique } from "../array/array";
66

77
export const mapProviderToList = (
@@ -10,9 +10,18 @@ export const mapProviderToList = (
1010
auditors: Array<Auditor>,
1111
lastSuccessfulSnapshot?: ProviderSnapshot
1212
): ProviderList => {
13-
const isValidVersion = provider.cosmosSdkVersion ? semver.gte(provider.cosmosSdkVersion, "v0.45.9") : false;
13+
const isValidSdkVersion = provider.cosmosSdkVersion ? semver.gte(provider.cosmosSdkVersion, "v0.45.9") : false;
1414
const name = provider.isOnline ? new URL(provider.hostUri).hostname : null;
1515
const gpuModels = getDistinctGpuModelsFromNodes(lastSuccessfulSnapshot?.nodes || []);
16+
const stats: ProviderList['stats'] = {
17+
cpu: buildStatsItem('CPU', lastSuccessfulSnapshot, isValidSdkVersion),
18+
gpu: buildStatsItem('GPU', lastSuccessfulSnapshot, isValidSdkVersion),
19+
memory: buildStatsItem('Memory', lastSuccessfulSnapshot, isValidSdkVersion),
20+
storage: {
21+
ephemeral: buildStatsItem('EphemeralStorage', lastSuccessfulSnapshot, isValidSdkVersion),
22+
persistent: buildStatsItem('PersistentStorage', lastSuccessfulSnapshot, isValidSdkVersion),
23+
},
24+
};
1625

1726
return {
1827
owner: provider.owner,
@@ -32,29 +41,15 @@ export const mapProviderToList = (
3241
ipCountryCode: provider.ipCountryCode,
3342
ipLat: provider.ipLat,
3443
ipLon: provider.ipLon,
35-
activeStats: {
36-
cpu: lastSuccessfulSnapshot?.activeCPU,
37-
gpu: lastSuccessfulSnapshot?.activeGPU,
38-
memory: lastSuccessfulSnapshot?.activeMemory,
39-
storage: lastSuccessfulSnapshot?.activeEphemeralStorage + lastSuccessfulSnapshot?.activePersistentStorage
40-
},
41-
pendingStats: {
42-
cpu: isValidVersion ? lastSuccessfulSnapshot?.pendingCPU : 0,
43-
gpu: isValidVersion ? lastSuccessfulSnapshot?.pendingGPU : 0,
44-
memory: isValidVersion ? lastSuccessfulSnapshot?.pendingMemory : 0,
45-
storage: isValidVersion ? lastSuccessfulSnapshot?.pendingEphemeralStorage + lastSuccessfulSnapshot?.pendingPersistentStorage : 0
46-
},
47-
availableStats: {
48-
cpu: isValidVersion ? lastSuccessfulSnapshot?.availableCPU : 0,
49-
gpu: isValidVersion ? lastSuccessfulSnapshot?.availableGPU : 0,
50-
memory: isValidVersion ? lastSuccessfulSnapshot?.availableMemory : 0,
51-
storage: isValidVersion ? lastSuccessfulSnapshot?.availableEphemeralStorage + lastSuccessfulSnapshot?.availablePersistentStorage : 0
52-
},
44+
stats,
45+
activeStats: buildLegacyStatsItem(stats, 'active'),
46+
pendingStats: buildLegacyStatsItem(stats, 'pending'),
47+
availableStats: buildLegacyStatsItem(stats, 'pending'),
5348
gpuModels: gpuModels,
5449
uptime1d: provider.uptime1d,
5550
uptime7d: provider.uptime7d,
5651
uptime30d: provider.uptime30d,
57-
isValidVersion,
52+
isValidVersion: isValidSdkVersion,
5853
isOnline: provider.isOnline,
5954
lastOnlineDate: lastSuccessfulSnapshot?.checkDate,
6055
isAudited: provider.providerAttributeSignatures.some(a => auditors.some(y => y.address === a.auditor)),
@@ -93,6 +88,32 @@ export const mapProviderToList = (
9388
} as ProviderList;
9489
};
9590

91+
type StatsEntry = 'CPU' | 'GPU' | 'Memory' | 'PersistentStorage' | 'EphemeralStorage';
92+
function buildStatsItem<T extends StatsEntry>(suffix: T, snapshot: ProviderSnapshot | undefined | null, isValidSdkVersion: boolean): StatsItem {
93+
if (!isValidSdkVersion) {
94+
return {
95+
active: snapshot?.[`active${suffix}`] || 0,
96+
available: 0,
97+
pending: 0,
98+
};
99+
}
100+
101+
return {
102+
active: snapshot?.[`active${suffix}`] || 0,
103+
available: snapshot?.[`available${suffix}`] || 0,
104+
pending: snapshot?.[`pending${suffix}`] || 0,
105+
};
106+
}
107+
108+
function buildLegacyStatsItem(stats: ProviderList['stats'], type: keyof StatsItem) {
109+
return {
110+
cpu: stats.cpu[type],
111+
gpu: stats.gpu[type],
112+
memory: stats.memory[type],
113+
storage: stats.storage.ephemeral[type] + stats.storage.persistent[type],
114+
};
115+
}
116+
96117
function getDistinctGpuModelsFromNodes(nodes: ProviderSnapshotNode[]) {
97118
const gpuModels = nodes.flatMap(x => x.gpus).map(x => ({ vendor: x.vendor, model: x.name, ram: x.memorySize, interface: x.interface }));
98119
const distinctGpuModels = gpuModels.filter(
@@ -102,8 +123,8 @@ function getDistinctGpuModelsFromNodes(nodes: ProviderSnapshotNode[]) {
102123
return distinctGpuModels;
103124
}
104125

105-
export const getProviderAttributeValue = (
106-
key: keyof ProviderAttributesSchema,
126+
export const getProviderAttributeValue = <TKey extends keyof ProviderAttributesSchema>(
127+
key: TKey,
107128
provider: Provider,
108129
providerAttributeSchema: ProviderAttributesSchema
109130
): string | string[] | boolean | number | null => {

0 commit comments

Comments
 (0)