mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
chore: add metrics/gauges for "max constraint values" and "max constraints" (#7398)
This PR adds metrics tracking for: - "maxConstraintValues": the highest number of constraint values that are in use - "maxConstraintsPerStrategy": the highest number of constraints used on a strategy It updates the existing feature strategy read model that returns max metrics for other strategy-related things. It also moves one test into a more fitting describe block.
This commit is contained in:
parent
4eaa1525a0
commit
c4e2159401
@ -17,4 +17,20 @@ export class FakeFeatureStrategiesReadModel
|
||||
} | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async getMaxConstraintValues(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
count: number;
|
||||
} | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async getMaxConstraintsPerStrategy(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
count: number;
|
||||
} | null> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel {
|
||||
constructor(db: Db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async getMaxFeatureEnvironmentStrategies(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
@ -47,4 +46,60 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel {
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
async getMaxConstraintValues(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
count: number;
|
||||
} | null> {
|
||||
const rows = await this.db('feature_strategies')
|
||||
.select(
|
||||
'feature_name',
|
||||
'environment',
|
||||
this.db.raw(
|
||||
"MAX(coalesce(jsonb_array_length(constraint_value->'values'), 0)) as max_values_count",
|
||||
),
|
||||
)
|
||||
.from(
|
||||
this.db.raw(
|
||||
'feature_strategies, jsonb_array_elements(constraints) AS constraint_value',
|
||||
),
|
||||
)
|
||||
.groupBy('feature_name', 'environment')
|
||||
.orderBy('max_values_count', 'desc')
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0
|
||||
? {
|
||||
feature: String(rows[0].feature_name),
|
||||
environment: String(rows[0].environment),
|
||||
count: Number(rows[0].max_values_count),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
async getMaxConstraintsPerStrategy(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
count: number;
|
||||
} | null> {
|
||||
const rows = await this.db('feature_strategies')
|
||||
.select(
|
||||
'feature_name',
|
||||
'environment',
|
||||
this.db.raw(
|
||||
'jsonb_array_length(constraints) as constraint_count',
|
||||
),
|
||||
)
|
||||
|
||||
.orderBy('constraint_count', 'desc')
|
||||
.limit(1);
|
||||
|
||||
return rows.length > 0
|
||||
? {
|
||||
feature: String(rows[0].feature_name),
|
||||
environment: String(rows[0].environment),
|
||||
count: Number(rows[0].constraint_count),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import type {
|
||||
IProjectStore,
|
||||
IUnleashStores,
|
||||
} from '../../../types';
|
||||
import { randomId } from '../../../util';
|
||||
|
||||
let stores: IUnleashStores;
|
||||
let db: ITestDb;
|
||||
@ -246,6 +247,9 @@ describe('strategy parameters default to sane defaults', () => {
|
||||
});
|
||||
expect(strategy.parameters.stickiness).toBe(defaultStickiness);
|
||||
});
|
||||
});
|
||||
|
||||
describe('max metrics collection', () => {
|
||||
test('Read feature with max number of strategies', async () => {
|
||||
const toggle = await featureToggleStore.create('default', {
|
||||
name: 'featureA',
|
||||
@ -289,4 +293,122 @@ describe('strategy parameters default to sane defaults', () => {
|
||||
count: 2,
|
||||
});
|
||||
});
|
||||
|
||||
test('Read feature with max number of constraint values', async () => {
|
||||
const flagA = await featureToggleStore.create('default', {
|
||||
name: randomId(),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const flagB = await featureToggleStore.create('default', {
|
||||
name: randomId(),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const maxConstraintValuesBefore =
|
||||
await featureStrategiesReadModel.getMaxConstraintValues();
|
||||
expect(maxConstraintValuesBefore).toBe(null);
|
||||
|
||||
const maxValueCount = 100;
|
||||
await featureStrategiesStore.createStrategyFeatureEnv({
|
||||
strategyName: 'gradualRollout',
|
||||
projectId: 'default',
|
||||
environment: 'default',
|
||||
featureName: flagA.name,
|
||||
constraints: [
|
||||
{
|
||||
values: ['only one'],
|
||||
operator: 'IN',
|
||||
contextName: 'appName',
|
||||
},
|
||||
{
|
||||
values: Array.from({ length: maxValueCount }, (_, i) =>
|
||||
i.toString(),
|
||||
),
|
||||
operator: 'IN',
|
||||
contextName: 'appName',
|
||||
},
|
||||
],
|
||||
|
||||
sortOrder: 0,
|
||||
parameters: {},
|
||||
});
|
||||
await featureStrategiesStore.createStrategyFeatureEnv({
|
||||
strategyName: 'gradualRollout',
|
||||
projectId: 'default',
|
||||
environment: 'default',
|
||||
featureName: flagB.name,
|
||||
constraints: [
|
||||
{
|
||||
operator: 'IN',
|
||||
contextName: 'appName',
|
||||
},
|
||||
],
|
||||
sortOrder: 0,
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const maxConstraintValues =
|
||||
await featureStrategiesReadModel.getMaxConstraintValues();
|
||||
expect(maxConstraintValues).toEqual({
|
||||
feature: flagA.name,
|
||||
environment: 'default',
|
||||
count: maxValueCount,
|
||||
});
|
||||
});
|
||||
|
||||
test('Read feature strategy with max number of constraints', async () => {
|
||||
const flagA = await featureToggleStore.create('default', {
|
||||
name: randomId(),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const flagB = await featureToggleStore.create('default', {
|
||||
name: randomId(),
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
const maxConstraintValuesBefore =
|
||||
await featureStrategiesReadModel.getMaxConstraintsPerStrategy();
|
||||
expect(maxConstraintValuesBefore).toBe(null);
|
||||
|
||||
await featureStrategiesStore.createStrategyFeatureEnv({
|
||||
strategyName: 'gradualRollout',
|
||||
projectId: 'default',
|
||||
environment: 'default',
|
||||
featureName: flagA.name,
|
||||
constraints: [
|
||||
{
|
||||
values: ['blah'],
|
||||
operator: 'IN',
|
||||
contextName: 'appName',
|
||||
},
|
||||
{
|
||||
values: ['blah'],
|
||||
operator: 'IN',
|
||||
contextName: 'appName',
|
||||
},
|
||||
],
|
||||
|
||||
sortOrder: 0,
|
||||
parameters: {},
|
||||
});
|
||||
await featureStrategiesStore.createStrategyFeatureEnv({
|
||||
strategyName: 'gradualRollout',
|
||||
projectId: 'default',
|
||||
environment: 'default',
|
||||
featureName: flagB.name,
|
||||
constraints: [],
|
||||
sortOrder: 0,
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const maxConstraintValues =
|
||||
await featureStrategiesReadModel.getMaxConstraintsPerStrategy();
|
||||
expect(maxConstraintValues).toEqual({
|
||||
feature: flagA.name,
|
||||
environment: 'default',
|
||||
count: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -8,4 +8,14 @@ export interface IFeatureStrategiesReadModel {
|
||||
feature: string;
|
||||
count: number;
|
||||
} | null>;
|
||||
getMaxConstraintValues(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
count: number;
|
||||
} | null>;
|
||||
getMaxConstraintsPerStrategy(): Promise<{
|
||||
feature: string;
|
||||
environment: string;
|
||||
count: number;
|
||||
} | null>;
|
||||
}
|
||||
|
@ -119,6 +119,16 @@ export default class MetricsMonitor {
|
||||
help: 'Maximum number of strategies in one feature',
|
||||
labelNames: ['feature'],
|
||||
});
|
||||
const maxConstraintValues = createGauge({
|
||||
name: 'max_constraint_values',
|
||||
help: 'Maximum number of constraint values used in a single constraint',
|
||||
labelNames: ['feature', 'environment'],
|
||||
});
|
||||
const maxConstraintsPerStrategy = createGauge({
|
||||
name: 'max_strategy_constraints',
|
||||
help: 'Maximum number of constraints used on a single strategy',
|
||||
labelNames: ['feature', 'environment'],
|
||||
});
|
||||
|
||||
const featureTogglesArchivedTotal = createGauge({
|
||||
name: 'feature_toggles_archived_total',
|
||||
@ -284,11 +294,17 @@ export default class MetricsMonitor {
|
||||
async function collectStaticCounters() {
|
||||
try {
|
||||
const stats = await instanceStatsService.getStats();
|
||||
const [maxStrategies, maxEnvironmentStrategies] =
|
||||
await Promise.all([
|
||||
stores.featureStrategiesReadModel.getMaxFeatureStrategies(),
|
||||
stores.featureStrategiesReadModel.getMaxFeatureEnvironmentStrategies(),
|
||||
]);
|
||||
const [
|
||||
maxStrategies,
|
||||
maxEnvironmentStrategies,
|
||||
maxConstraintValuesResult,
|
||||
maxConstraintsPerStrategyResult,
|
||||
] = await Promise.all([
|
||||
stores.featureStrategiesReadModel.getMaxFeatureStrategies(),
|
||||
stores.featureStrategiesReadModel.getMaxFeatureEnvironmentStrategies(),
|
||||
stores.featureStrategiesReadModel.getMaxConstraintValues(),
|
||||
stores.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
|
||||
]);
|
||||
|
||||
featureFlagsTotal.reset();
|
||||
featureFlagsTotal.labels({ version }).set(stats.featureToggles);
|
||||
@ -332,6 +348,25 @@ export default class MetricsMonitor {
|
||||
.labels({ feature: maxStrategies.feature })
|
||||
.set(maxStrategies.count);
|
||||
}
|
||||
if (maxConstraintValuesResult) {
|
||||
maxConstraintValues.reset();
|
||||
maxConstraintValues
|
||||
.labels({
|
||||
environment: maxConstraintValuesResult.environment,
|
||||
feature: maxConstraintValuesResult.feature,
|
||||
})
|
||||
.set(maxConstraintValuesResult.count);
|
||||
}
|
||||
if (maxConstraintsPerStrategyResult) {
|
||||
maxConstraintsPerStrategy.reset();
|
||||
maxConstraintsPerStrategy
|
||||
.labels({
|
||||
environment:
|
||||
maxConstraintsPerStrategyResult.environment,
|
||||
feature: maxConstraintsPerStrategyResult.feature,
|
||||
})
|
||||
.set(maxConstraintsPerStrategyResult.count);
|
||||
}
|
||||
|
||||
enabledMetricsBucketsPreviousDay.reset();
|
||||
enabledMetricsBucketsPreviousDay.set(
|
||||
|
Loading…
Reference in New Issue
Block a user