1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: add remaining resource usage to instance stats (#7958)

Updates the instance stats endpoint with 
- maxEnvironmentStrategies
- maxConstraints
- maxConstraintValues

It adds the following rows to the front end table:
- segments (already in the payload, just not used for the table before)
- API tokens (separate rows for type, + one for total) (also existed
before, but wasn't listed)
- Highest number of strategies used for a single flag in a single
environment
- Highest number of constraints used on a single strategy
- Highest number of values used for a single constraint


![image](https://github.com/user-attachments/assets/57798f8e-c466-4590-820b-15afd3729243)
This commit is contained in:
Thomas Heartman 2024-08-22 13:09:26 +02:00 committed by GitHub
parent cb8d689bd8
commit b0541a0af2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 126 additions and 26 deletions

View File

@ -8,13 +8,13 @@ import {
TableRow,
} from '@mui/material';
import { Box } from '@mui/system';
import type { VFC } from 'react';
import type { FC } from 'react';
import { useInstanceStats } from 'hooks/api/getters/useInstanceStats/useInstanceStats';
import { formatApiPath } from '../../../../utils/formatPath';
import { PageContent } from '../../../common/PageContent/PageContent';
import { PageHeader } from '../../../common/PageHeader/PageHeader';
export const InstanceStats: VFC = () => {
export const InstanceStats: FC = () => {
const { stats } = useInstanceStats();
let versionTitle: string;
@ -28,6 +28,11 @@ export const InstanceStats: VFC = () => {
version = stats?.versionOSS;
}
const apiTokensTotal = Object.values(stats?.apiTokens ?? {}).reduce(
(acc, val) => acc + val,
0,
);
const rows = [
{ title: 'Instance Id', value: stats?.instanceId, offset: false },
{ title: versionTitle, value: version },
@ -41,6 +46,23 @@ export const InstanceStats: VFC = () => {
{ title: 'Strategies', value: stats?.strategies },
{ title: 'Feature exports', value: stats?.featureExports },
{ title: 'Feature imports', value: stats?.featureImports },
{ title: 'Admin API tokens', value: stats?.apiTokens?.admin },
{ title: 'Client API tokens', value: stats?.apiTokens?.client },
{ title: 'Frontend API tokens', value: stats?.apiTokens?.frontend },
{ title: 'API tokens total', value: apiTokensTotal },
{ title: 'Segments', value: stats?.segments },
{
title: 'Highest number of strategies used for a single flag in a single environment',
value: stats?.maxEnvironmentStrategies,
},
{
title: 'Highest number of constraints used on a single strategy',
value: stats?.maxConstraints,
},
{
title: 'Highest number of values used for a single constraint',
value: stats?.maxConstraintValues,
},
];
if (stats?.versionEnterprise) {

View File

@ -92,4 +92,20 @@ export interface InstanceAdminStatsSchema {
versionEnterprise?: string;
/** The version of Unleash OSS that is bundled in this instance */
versionOSS?: string;
/** A breakdown of API tokens that exist in this instance */
apiTokens: {
client: number;
admin: number;
frontend: number;
};
// The highest number of strategies used on a single feature flag in a single environment.
maxEnvironmentStrategies: number;
// The highest number of constraints used on a single strategy.
maxConstraints: number;
// The highest number of constraint values used on a single constraint.
maxConstraintValues: number;
}

View File

@ -40,6 +40,8 @@ import FakeSettingStore from '../../../test/fixtures/fake-setting-store';
import FakeSegmentStore from '../../../test/fixtures/fake-segment-store';
import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store';
import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store';
import { FeatureStrategiesReadModel } from '../feature-toggle/feature-strategies-read-model';
import { FakeFeatureStrategiesReadModel } from '../feature-toggle/fake-feature-strategies-read-model';
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
const { eventBus, getLogger, flagResolver } = config;
@ -89,6 +91,8 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
getLogger,
flagResolver,
);
const featureStrategiesReadModel = new FeatureStrategiesReadModel(db);
const instanceStatsServiceStores = {
featureToggleStore,
userStore,
@ -104,6 +108,7 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
eventStore,
apiTokenStore,
clientMetricsStoreV2,
featureStrategiesReadModel,
};
const featureStrategiesStore = new FeatureStrategyStore(
db,
@ -151,6 +156,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
const eventStore = new FakeEventStore();
const apiTokenStore = new FakeApiTokenStore();
const clientMetricsStoreV2 = new FakeClientMetricsStoreV2();
const featureStrategiesReadModel = new FakeFeatureStrategiesReadModel();
const instanceStatsServiceStores = {
featureToggleStore,
@ -167,6 +173,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
eventStore,
apiTokenStore,
clientMetricsStoreV2,
featureStrategiesReadModel,
};
const featureStrategiesStore = new FakeFeatureStrategiesStore();
const versionServiceStores = {

View File

@ -5,6 +5,7 @@ import type {
IClientInstanceStore,
IClientMetricsStoreV2,
IEventStore,
IFeatureStrategiesReadModel,
IUnleashStores,
} from '../../types/stores';
import type { IContextFieldStore } from '../../types/stores/context-field-store';
@ -61,6 +62,9 @@ export interface InstanceStats {
enabledCount: number;
variantCount: number;
};
maxEnvironmentStrategies: number;
maxConstraints: number;
maxConstraintValues: number;
}
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
@ -109,6 +113,8 @@ export class InstanceStatsService {
private getProductionChanges: GetProductionChanges;
private featureStrategiesReadModel: IFeatureStrategiesReadModel;
constructor(
{
featureToggleStore,
@ -125,6 +131,7 @@ export class InstanceStatsService {
eventStore,
apiTokenStore,
clientMetricsStoreV2,
featureStrategiesReadModel,
}: Pick<
IUnleashStores,
| 'featureToggleStore'
@ -141,6 +148,7 @@ export class InstanceStatsService {
| 'eventStore'
| 'apiTokenStore'
| 'clientMetricsStoreV2'
| 'featureStrategiesReadModel'
>,
{
getLogger,
@ -169,6 +177,7 @@ export class InstanceStatsService {
this.apiTokenStore = apiTokenStore;
this.clientMetricsStore = clientMetricsStoreV2;
this.flagResolver = flagResolver;
this.featureStrategiesReadModel = featureStrategiesReadModel;
}
async refreshAppCountSnapshot(): Promise<
@ -250,6 +259,9 @@ export class InstanceStatsService {
featureImports,
productionChanges,
previousDayMetricsBucketsCount,
maxEnvironmentStrategies,
maxConstraintValues,
maxConstraints,
] = await Promise.all([
this.getToggleCount(),
this.getArchivedToggleCount(),
@ -277,6 +289,9 @@ export class InstanceStatsService {
}),
this.getProductionChanges(),
this.clientMetricsStore.countPreviousDayHourlyMetricsBuckets(),
this.featureStrategiesReadModel.getMaxFeatureEnvironmentStrategies(),
this.featureStrategiesReadModel.getMaxConstraintValues(),
this.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
]);
return {
@ -309,6 +324,9 @@ export class InstanceStatsService {
featureImports,
productionChanges,
previousDayMetricsBucketsCount,
maxEnvironmentStrategies: maxEnvironmentStrategies?.count ?? 0,
maxConstraintValues: maxConstraintValues?.count ?? 0,
maxConstraints: maxConstraints?.count ?? 0,
};
}

View File

@ -34,7 +34,7 @@ export const instanceAdminStatsSchema = {
example: '5.1.7',
},
users: {
type: 'number',
type: 'integer',
description: 'The number of users this instance has',
example: 8,
minimum: 0,
@ -45,14 +45,14 @@ export const instanceAdminStatsSchema = {
'The number client metrics buckets records recorded in the previous day. # features * # apps * # envs * # hours with metrics',
properties: {
enabledCount: {
type: 'number',
type: 'integer',
description:
'The number of enabled/disabled metrics buckets recorded in the previous day',
example: 10,
minimum: 0,
},
variantCount: {
type: 'number',
type: 'integer',
description:
'The number of variant metrics buckets recorded in the previous day',
example: 10,
@ -66,28 +66,28 @@ export const instanceAdminStatsSchema = {
'The number of active users in the last 7, 30 and 90 days',
properties: {
last7: {
type: 'number',
type: 'integer',
description:
'The number of active users in the last 7 days',
example: 5,
minimum: 0,
},
last30: {
type: 'number',
type: 'integer',
description:
'The number of active users in the last 30 days',
example: 10,
minimum: 0,
},
last60: {
type: 'number',
type: 'integer',
description:
'The number of active users in the last 60 days',
example: 12,
minimum: 0,
},
last90: {
type: 'number',
type: 'integer',
description:
'The number of active users in the last 90 days',
example: 15,
@ -101,21 +101,21 @@ export const instanceAdminStatsSchema = {
'The number of changes to the production environment in the last 30, 60 and 90 days',
properties: {
last30: {
type: 'number',
type: 'integer',
description:
'The number of changes in production in the last 30 days',
example: 10,
minimum: 0,
},
last60: {
type: 'number',
type: 'integer',
description:
'The number of changes in production in the last 60 days',
example: 12,
minimum: 0,
},
last90: {
type: 'number',
type: 'integer',
description:
'The number of changes in production in the last 90 days',
example: 15,
@ -124,50 +124,50 @@ export const instanceAdminStatsSchema = {
},
},
featureToggles: {
type: 'number',
type: 'integer',
description: 'The number of feature-toggles this instance has',
example: 47,
minimum: 0,
},
projects: {
type: 'number',
type: 'integer',
description: 'The number of projects defined in this instance.',
example: 3,
minimum: 0,
},
contextFields: {
type: 'number',
type: 'integer',
description:
'The number of context fields defined in this instance.',
example: 7,
minimum: 0,
},
roles: {
type: 'number',
type: 'integer',
description: 'The number of roles defined in this instance',
example: 5,
minimum: 0,
},
groups: {
type: 'number',
type: 'integer',
description: 'The number of groups defined in this instance',
example: 12,
minimum: 0,
},
environments: {
type: 'number',
type: 'integer',
description: 'The number of environments defined in this instance',
example: 3,
minimum: 0,
},
segments: {
type: 'number',
type: 'integer',
description: 'The number of segments defined in this instance',
example: 19,
minimum: 0,
},
strategies: {
type: 'number',
type: 'integer',
description: 'The number of strategies defined in this instance',
example: 8,
minimum: 0,
@ -200,7 +200,7 @@ export const instanceAdminStatsSchema = {
example: '30d',
},
count: {
type: 'number',
type: 'integer',
description:
'The number of client applications that have been observed in this period',
example: 1,
@ -209,13 +209,13 @@ export const instanceAdminStatsSchema = {
},
},
featureExports: {
type: 'number',
type: 'integer',
description: 'The number of export operations on this instance',
example: 0,
minimum: 0,
},
featureImports: {
type: 'number',
type: 'integer',
description: 'The number of import operations on this instance',
example: 0,
minimum: 0,
@ -225,25 +225,46 @@ export const instanceAdminStatsSchema = {
description: 'The number of API tokens in Unleash, split by type',
properties: {
admin: {
type: 'number',
type: 'integer',
description: 'The number of admin tokens.',
minimum: 0,
example: 5,
},
client: {
type: 'number',
type: 'integer',
description: 'The number of client tokens.',
minimum: 0,
example: 5,
},
frontend: {
type: 'number',
type: 'integer',
description: 'The number of frontend tokens.',
minimum: 0,
example: 5,
},
},
},
maxEnvironmentStrategies: {
type: 'integer',
minimum: 0,
example: 3,
description:
'The highest number of strategies used on a single feature flag in a single environment.',
},
maxConstraints: {
type: 'integer',
minimum: 0,
example: 4,
description:
'The highest number of constraints used on a single strategy.',
},
maxConstraintValues: {
type: 'integer',
minimum: 0,
example: 17,
description:
'The highest number of constraint values used on a single constraint.',
},
sum: {
type: 'string',
description:

View File

@ -126,6 +126,9 @@ class InstanceAdminController extends Controller {
variantCount: 100,
enabledCount: 200,
},
maxEnvironmentStrategies: 20,
maxConstraints: 17,
maxConstraintValues: 123,
};
}

View File

@ -133,3 +133,16 @@ test('should return instance statistics as CSV', async () => {
expect(res.text).toMatch(/featureToggles/);
expect(res.text).toMatch(/"sum"/);
});
test('contains new max* properties', async () => {
const { body } = await app.request
.get('/api/admin/instance-admin/statistics')
.expect('Content-Type', /json/)
.expect(200);
expect(body).toMatchObject({
maxEnvironmentStrategies: 0,
maxConstraints: 0,
maxConstraintValues: 0,
});
});