mirror of
https://github.com/Unleash/unleash.git
synced 2025-11-10 01:19:53 +01:00
chore: add edge instances to instance stats
This commit is contained in:
parent
14b4809c8e
commit
8e52e24313
@ -65,6 +65,7 @@ export const InstanceStats: FC = () => {
|
|||||||
},
|
},
|
||||||
{ title: 'Release templates', value: stats?.releaseTemplates },
|
{ title: 'Release templates', value: stats?.releaseTemplates },
|
||||||
{ title: 'Release plans', value: stats?.releasePlans },
|
{ title: 'Release plans', value: stats?.releasePlans },
|
||||||
|
{ title: 'Edge instances', value: stats?.edgeInstances },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (stats?.versionEnterprise) {
|
if (stats?.versionEnterprise) {
|
||||||
|
|||||||
@ -50,6 +50,10 @@ import {
|
|||||||
} from './getLicensedUsers.js';
|
} from './getLicensedUsers.js';
|
||||||
import { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
import { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
||||||
import { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
import { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
||||||
|
import {
|
||||||
|
createFakeGetEdgeInstances,
|
||||||
|
createGetEdgeInstances,
|
||||||
|
} from './getEdgeInstances.js';
|
||||||
|
|
||||||
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
||||||
const { eventBus, getLogger, flagResolver } = config;
|
const { eventBus, getLogger, flagResolver } = config;
|
||||||
@ -134,6 +138,7 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
const getActiveUsers = createGetActiveUsers(db);
|
const getActiveUsers = createGetActiveUsers(db);
|
||||||
const getProductionChanges = createGetProductionChanges(db);
|
const getProductionChanges = createGetProductionChanges(db);
|
||||||
const getLicencedUsers = createGetLicensedUsers(db);
|
const getLicencedUsers = createGetLicensedUsers(db);
|
||||||
|
const getEdgeInstances = createGetEdgeInstances(db);
|
||||||
const versionService = new VersionService(versionServiceStores, config);
|
const versionService = new VersionService(versionServiceStores, config);
|
||||||
|
|
||||||
const instanceStatsService = new InstanceStatsService(
|
const instanceStatsService = new InstanceStatsService(
|
||||||
@ -143,6 +148,7 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
getActiveUsers,
|
getActiveUsers,
|
||||||
getProductionChanges,
|
getProductionChanges,
|
||||||
getLicencedUsers,
|
getLicencedUsers,
|
||||||
|
getEdgeInstances,
|
||||||
);
|
);
|
||||||
|
|
||||||
return instanceStatsService;
|
return instanceStatsService;
|
||||||
@ -199,6 +205,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
const getActiveUsers = createFakeGetActiveUsers();
|
const getActiveUsers = createFakeGetActiveUsers();
|
||||||
const getLicensedUsers = createFakeGetLicensedUsers();
|
const getLicensedUsers = createFakeGetLicensedUsers();
|
||||||
const getProductionChanges = createFakeGetProductionChanges();
|
const getProductionChanges = createFakeGetProductionChanges();
|
||||||
|
const getEdgeInstances = createFakeGetEdgeInstances();
|
||||||
const versionService = new VersionService(versionServiceStores, config);
|
const versionService = new VersionService(versionServiceStores, config);
|
||||||
|
|
||||||
const instanceStatsService = new InstanceStatsService(
|
const instanceStatsService = new InstanceStatsService(
|
||||||
@ -208,6 +215,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
getActiveUsers,
|
getActiveUsers,
|
||||||
getProductionChanges,
|
getProductionChanges,
|
||||||
getLicensedUsers,
|
getLicensedUsers,
|
||||||
|
getEdgeInstances,
|
||||||
);
|
);
|
||||||
|
|
||||||
return instanceStatsService;
|
return instanceStatsService;
|
||||||
|
|||||||
59
src/lib/features/instance-stats/getEdgeInstances.ts
Normal file
59
src/lib/features/instance-stats/getEdgeInstances.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import type { Db } from '../../types/index.js';
|
||||||
|
|
||||||
|
const TABLE = 'edge_node_presence';
|
||||||
|
const GRACE_PERCENTAGE = 0.05;
|
||||||
|
|
||||||
|
export type GetEdgeInstances = () => Promise<{
|
||||||
|
last30: number;
|
||||||
|
last60: number;
|
||||||
|
last90: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export const createGetEdgeInstances =
|
||||||
|
(db: Db): GetEdgeInstances =>
|
||||||
|
async () => {
|
||||||
|
const result = await db
|
||||||
|
.with('buckets', (qb) =>
|
||||||
|
qb
|
||||||
|
.from(TABLE)
|
||||||
|
.whereRaw("bucket_ts >= NOW() - INTERVAL '90 days'")
|
||||||
|
.groupBy('bucket_ts')
|
||||||
|
.select(
|
||||||
|
db.raw('bucket_ts'),
|
||||||
|
db.raw('COUNT(*)::int AS active_nodes'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.from('buckets')
|
||||||
|
.select({
|
||||||
|
last30: db.raw(
|
||||||
|
`COALESCE(CEIL(AVG(active_nodes) FILTER (WHERE bucket_ts >= NOW() - INTERVAL '30 days') * ?)::int, 0)`,
|
||||||
|
[1 + GRACE_PERCENTAGE],
|
||||||
|
),
|
||||||
|
last60: db.raw(
|
||||||
|
`COALESCE(CEIL(AVG(active_nodes) FILTER (WHERE bucket_ts >= NOW() - INTERVAL '60 days') * ?)::int, 0)`,
|
||||||
|
[1 + GRACE_PERCENTAGE],
|
||||||
|
),
|
||||||
|
last90: db.raw(
|
||||||
|
`COALESCE(CEIL(AVG(active_nodes) FILTER (WHERE bucket_ts >= NOW() - INTERVAL '90 days') * ?)::int, 0)`,
|
||||||
|
[1 + GRACE_PERCENTAGE],
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return {
|
||||||
|
last30: Number(result?.last30 ?? 0),
|
||||||
|
last60: Number(result?.last60 ?? 0),
|
||||||
|
last90: Number(result?.last90 ?? 0),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFakeGetEdgeInstances =
|
||||||
|
(
|
||||||
|
edgeInstances: Awaited<ReturnType<GetEdgeInstances>> = {
|
||||||
|
last30: 0,
|
||||||
|
last60: 0,
|
||||||
|
last90: 0,
|
||||||
|
},
|
||||||
|
): GetEdgeInstances =>
|
||||||
|
() =>
|
||||||
|
Promise.resolve(edgeInstances);
|
||||||
@ -33,6 +33,7 @@ import type { GetLicensedUsers } from './getLicensedUsers.js';
|
|||||||
import type { IFeatureUsageInfo } from '../../services/version-service.js';
|
import type { IFeatureUsageInfo } from '../../services/version-service.js';
|
||||||
import type { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
import type { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
||||||
import type { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
import type { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
||||||
|
import type { GetEdgeInstances } from './getEdgeInstances.js';
|
||||||
|
|
||||||
export type TimeRange = 'allTime' | '30d' | '7d';
|
export type TimeRange = 'allTime' | '30d' | '7d';
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ export interface InstanceStats {
|
|||||||
maxConstraintValues: number;
|
maxConstraintValues: number;
|
||||||
releaseTemplates?: number;
|
releaseTemplates?: number;
|
||||||
releasePlans?: number;
|
releasePlans?: number;
|
||||||
|
edgeInstances?: Awaited<ReturnType<GetEdgeInstances>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
||||||
@ -124,6 +126,8 @@ export class InstanceStatsService {
|
|||||||
|
|
||||||
getProductionChanges: GetProductionChanges;
|
getProductionChanges: GetProductionChanges;
|
||||||
|
|
||||||
|
getEdgeInstances: GetEdgeInstances;
|
||||||
|
|
||||||
private featureStrategiesReadModel: IFeatureStrategiesReadModel;
|
private featureStrategiesReadModel: IFeatureStrategiesReadModel;
|
||||||
|
|
||||||
private featureStrategiesStore: IFeatureStrategiesStore;
|
private featureStrategiesStore: IFeatureStrategiesStore;
|
||||||
@ -185,6 +189,7 @@ export class InstanceStatsService {
|
|||||||
getActiveUsers: GetActiveUsers,
|
getActiveUsers: GetActiveUsers,
|
||||||
getProductionChanges: GetProductionChanges,
|
getProductionChanges: GetProductionChanges,
|
||||||
getLicencedUsers: GetLicensedUsers,
|
getLicencedUsers: GetLicensedUsers,
|
||||||
|
getEdgeInstances: GetEdgeInstances,
|
||||||
) {
|
) {
|
||||||
this.strategyStore = strategyStore;
|
this.strategyStore = strategyStore;
|
||||||
this.userStore = userStore;
|
this.userStore = userStore;
|
||||||
@ -209,6 +214,8 @@ export class InstanceStatsService {
|
|||||||
'getProductionChanges',
|
'getProductionChanges',
|
||||||
getProductionChanges.bind(this),
|
getProductionChanges.bind(this),
|
||||||
);
|
);
|
||||||
|
this.getEdgeInstances = () =>
|
||||||
|
this.memorize('getEdgeInstances', getEdgeInstances.bind(this));
|
||||||
this.apiTokenStore = apiTokenStore;
|
this.apiTokenStore = apiTokenStore;
|
||||||
this.clientMetricsStore = clientMetricsStoreV2;
|
this.clientMetricsStore = clientMetricsStoreV2;
|
||||||
this.flagResolver = flagResolver;
|
this.flagResolver = flagResolver;
|
||||||
@ -356,6 +363,7 @@ export class InstanceStatsService {
|
|||||||
maxConstraints,
|
maxConstraints,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getArchivedToggleCount(),
|
this.getArchivedToggleCount(),
|
||||||
@ -402,6 +410,7 @@ export class InstanceStatsService {
|
|||||||
),
|
),
|
||||||
this.getReleaseTemplates(),
|
this.getReleaseTemplates(),
|
||||||
this.getReleasePlans(),
|
this.getReleasePlans(),
|
||||||
|
this.getEdgeInstances(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -442,6 +451,7 @@ export class InstanceStatsService {
|
|||||||
maxConstraints: maxConstraints?.count ?? 0,
|
maxConstraints: maxConstraints?.count ?? 0,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,6 +481,7 @@ export class InstanceStatsService {
|
|||||||
hostedBy,
|
hostedBy,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getRegisteredUsers(),
|
this.getRegisteredUsers(),
|
||||||
@ -496,6 +507,7 @@ export class InstanceStatsService {
|
|||||||
this.getHostedBy(),
|
this.getHostedBy(),
|
||||||
this.getReleaseTemplates(),
|
this.getReleaseTemplates(),
|
||||||
this.getReleasePlans(),
|
this.getReleasePlans(),
|
||||||
|
this.getEdgeInstances(),
|
||||||
]);
|
]);
|
||||||
const versionInfo = await this.versionService.getVersionInfo();
|
const versionInfo = await this.versionService.getVersionInfo();
|
||||||
|
|
||||||
@ -533,6 +545,9 @@ export class InstanceStatsService {
|
|||||||
hostedBy,
|
hostedBy,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances30: edgeInstances.last30,
|
||||||
|
edgeInstances60: edgeInstances.last60,
|
||||||
|
edgeInstances90: edgeInstances.last90,
|
||||||
};
|
};
|
||||||
return featureInfo;
|
return featureInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -284,6 +284,34 @@ export const instanceAdminStatsSchema = {
|
|||||||
example: 1,
|
example: 1,
|
||||||
description: 'The number of release plans in this instance',
|
description: 'The number of release plans in this instance',
|
||||||
},
|
},
|
||||||
|
edgeInstances: {
|
||||||
|
type: 'object',
|
||||||
|
description:
|
||||||
|
'The billable number of edge instances in the last 30, 60 and 90 days',
|
||||||
|
properties: {
|
||||||
|
last30: {
|
||||||
|
type: 'integer',
|
||||||
|
description:
|
||||||
|
'The billable number of edge instances in the last 30 days',
|
||||||
|
example: 10,
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
last60: {
|
||||||
|
type: 'integer',
|
||||||
|
description:
|
||||||
|
'The billable number of edge instances in the last 60 days',
|
||||||
|
example: 12,
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
last90: {
|
||||||
|
type: 'integer',
|
||||||
|
description:
|
||||||
|
'The billable number of edge instances in the last 90 days',
|
||||||
|
example: 15,
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
sum: {
|
sum: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -133,6 +133,11 @@ class InstanceAdminController extends Controller {
|
|||||||
maxConstraintValues: 123,
|
maxConstraintValues: 123,
|
||||||
releaseTemplates: 3,
|
releaseTemplates: 3,
|
||||||
releasePlans: 5,
|
releasePlans: 5,
|
||||||
|
edgeInstances: {
|
||||||
|
last30: 10,
|
||||||
|
last60: 15,
|
||||||
|
last90: 20,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,9 @@ const fakeTelemetryData = {
|
|||||||
hostedBy: 'self-hosted',
|
hostedBy: 'self-hosted',
|
||||||
releaseTemplates: 2,
|
releaseTemplates: 2,
|
||||||
releasePlans: 4,
|
releasePlans: 4,
|
||||||
|
edgeInstances30: 0,
|
||||||
|
edgeInstances60: 0,
|
||||||
|
edgeInstances90: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
test('yields current versions', async () => {
|
test('yields current versions', async () => {
|
||||||
|
|||||||
@ -54,6 +54,9 @@ export interface IFeatureUsageInfo {
|
|||||||
hostedBy: string;
|
hostedBy: string;
|
||||||
releaseTemplates: number;
|
releaseTemplates: number;
|
||||||
releasePlans: number;
|
releasePlans: number;
|
||||||
|
edgeInstances30?: number;
|
||||||
|
edgeInstances60?: number;
|
||||||
|
edgeInstances90?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VersionService {
|
export default class VersionService {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user