mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
feat: start monitoring total time to update cache (#6517)
This commit is contained in:
parent
1d526e707b
commit
2a57acca41
@ -16,7 +16,7 @@ import {
|
|||||||
import { validateOrigins } from '../../util';
|
import { validateOrigins } from '../../util';
|
||||||
import { BadDataError, InvalidTokenError } from '../../error';
|
import { BadDataError, InvalidTokenError } from '../../error';
|
||||||
import {
|
import {
|
||||||
OPERATION_TIME,
|
FUNCTION_TIME,
|
||||||
FRONTEND_API_REPOSITORY_CREATED,
|
FRONTEND_API_REPOSITORY_CREATED,
|
||||||
PROXY_REPOSITORY_CREATED,
|
PROXY_REPOSITORY_CREATED,
|
||||||
} from '../../metric-events';
|
} from '../../metric-events';
|
||||||
@ -77,9 +77,10 @@ export class FrontendApiService {
|
|||||||
this.services = services;
|
this.services = services;
|
||||||
this.globalFrontendApiCache = globalFrontendApiCache;
|
this.globalFrontendApiCache = globalFrontendApiCache;
|
||||||
|
|
||||||
this.timer = (operationId) =>
|
this.timer = (functionName) =>
|
||||||
metricsHelper.wrapTimer(config.eventBus, OPERATION_TIME, {
|
metricsHelper.wrapTimer(config.eventBus, FUNCTION_TIME, {
|
||||||
operationId,
|
className: 'FrontendApiService',
|
||||||
|
functionName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,11 @@ const createCache = (
|
|||||||
segment: ISegment = defaultSegment,
|
segment: ISegment = defaultSegment,
|
||||||
features: Record<string, Record<string, IFeatureToggleClient>> = {},
|
features: Record<string, Record<string, IFeatureToggleClient>> = {},
|
||||||
) => {
|
) => {
|
||||||
const config = { getLogger: noLogger, flagResolver: alwaysOnFlagResolver };
|
const config = {
|
||||||
|
getLogger: noLogger,
|
||||||
|
flagResolver: alwaysOnFlagResolver,
|
||||||
|
eventBus: <any>{ emit: jest.fn() },
|
||||||
|
};
|
||||||
const segmentReadModel = new FakeSegmentReadModel([segment as ISegment]);
|
const segmentReadModel = new FakeSegmentReadModel([segment as ISegment]);
|
||||||
const clientFeatureToggleReadModel = new FakeClientFeatureToggleReadModel(
|
const clientFeatureToggleReadModel = new FakeClientFeatureToggleReadModel(
|
||||||
features,
|
features,
|
||||||
|
@ -15,8 +15,10 @@ import { ALL_ENVS } from '../../util/constants';
|
|||||||
import { Logger } from '../../logger';
|
import { Logger } from '../../logger';
|
||||||
import { UPDATE_REVISION } from '../feature-toggle/configuration-revision-service';
|
import { UPDATE_REVISION } from '../feature-toggle/configuration-revision-service';
|
||||||
import { IClientFeatureToggleReadModel } from './client-feature-toggle-read-model-type';
|
import { IClientFeatureToggleReadModel } from './client-feature-toggle-read-model-type';
|
||||||
|
import metricsHelper from '../../util/metrics-helper';
|
||||||
|
import { FUNCTION_TIME } from '../../metric-events';
|
||||||
|
|
||||||
type Config = Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>;
|
type Config = Pick<IUnleashConfig, 'getLogger' | 'flagResolver' | 'eventBus'>;
|
||||||
|
|
||||||
type FrontendApiFeatureCache = Record<string, Record<string, FeatureInterface>>;
|
type FrontendApiFeatureCache = Record<string, Record<string, FeatureInterface>>;
|
||||||
|
|
||||||
@ -39,6 +41,8 @@ export class GlobalFrontendApiCache extends EventEmitter {
|
|||||||
|
|
||||||
private status: GlobalFrontendApiCacheState = 'starting';
|
private status: GlobalFrontendApiCacheState = 'starting';
|
||||||
|
|
||||||
|
private timer: Function;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: Config,
|
config: Config,
|
||||||
segmentReadModel: ISegmentReadModel,
|
segmentReadModel: ISegmentReadModel,
|
||||||
@ -52,6 +56,12 @@ export class GlobalFrontendApiCache extends EventEmitter {
|
|||||||
this.configurationRevisionService = configurationRevisionService;
|
this.configurationRevisionService = configurationRevisionService;
|
||||||
this.segmentReadModel = segmentReadModel;
|
this.segmentReadModel = segmentReadModel;
|
||||||
this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this);
|
this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this);
|
||||||
|
this.timer = (functionName) =>
|
||||||
|
metricsHelper.wrapTimer(config.eventBus, FUNCTION_TIME, {
|
||||||
|
className: 'GlobalFrontendApiCache',
|
||||||
|
functionName,
|
||||||
|
});
|
||||||
|
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
this.configurationRevisionService.on(
|
this.configurationRevisionService.on(
|
||||||
UPDATE_REVISION,
|
UPDATE_REVISION,
|
||||||
@ -103,6 +113,7 @@ export class GlobalFrontendApiCache extends EventEmitter {
|
|||||||
// TODO: also consider not fetching disabled features, because those are not returned by frontend API
|
// TODO: also consider not fetching disabled features, because those are not returned by frontend API
|
||||||
private async refreshData() {
|
private async refreshData() {
|
||||||
try {
|
try {
|
||||||
|
const stopTimer = this.timer('refreshData');
|
||||||
this.featuresByEnvironment = await this.getAllFeatures();
|
this.featuresByEnvironment = await this.getAllFeatures();
|
||||||
this.segments = await this.getAllSegments();
|
this.segments = await this.getAllSegments();
|
||||||
if (this.status === 'starting') {
|
if (this.status === 'starting') {
|
||||||
@ -112,6 +123,7 @@ export class GlobalFrontendApiCache extends EventEmitter {
|
|||||||
this.status = 'updated';
|
this.status = 'updated';
|
||||||
this.emit('updated');
|
this.emit('updated');
|
||||||
}
|
}
|
||||||
|
stopTimer();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error('Cannot load data for token', e);
|
this.logger.error('Cannot load data for token', e);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,11 @@ import { Logger } from '../../logger';
|
|||||||
import ConfigurationRevisionService, {
|
import ConfigurationRevisionService, {
|
||||||
UPDATE_REVISION,
|
UPDATE_REVISION,
|
||||||
} from '../feature-toggle/configuration-revision-service';
|
} from '../feature-toggle/configuration-revision-service';
|
||||||
import { PROXY_FEATURES_FOR_TOKEN_TIME } from '../../metric-events';
|
import {
|
||||||
|
FUNCTION_TIME,
|
||||||
|
PROXY_FEATURES_FOR_TOKEN_TIME,
|
||||||
|
} from '../../metric-events';
|
||||||
|
import metricsHelper from '../../util/metrics-helper';
|
||||||
|
|
||||||
type Config = Pick<IUnleashConfig, 'getLogger' | 'frontendApi' | 'eventBus'>;
|
type Config = Pick<IUnleashConfig, 'getLogger' | 'frontendApi' | 'eventBus'>;
|
||||||
|
|
||||||
@ -55,6 +59,8 @@ export class ProxyRepository
|
|||||||
|
|
||||||
private running: boolean;
|
private running: boolean;
|
||||||
|
|
||||||
|
private methodTimer: Function;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: Config,
|
config: Config,
|
||||||
stores: Stores,
|
stores: Stores,
|
||||||
@ -71,6 +77,12 @@ export class ProxyRepository
|
|||||||
this.token = token;
|
this.token = token;
|
||||||
this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this);
|
this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this);
|
||||||
this.interval = config.frontendApi.refreshIntervalInMs;
|
this.interval = config.frontendApi.refreshIntervalInMs;
|
||||||
|
|
||||||
|
this.methodTimer = (functionName) =>
|
||||||
|
metricsHelper.wrapTimer(config.eventBus, FUNCTION_TIME, {
|
||||||
|
className: 'ProxyRepository',
|
||||||
|
functionName,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTogglesWithSegmentData(): EnhancedFeatureInterface[] {
|
getTogglesWithSegmentData(): EnhancedFeatureInterface[] {
|
||||||
@ -135,8 +147,10 @@ export class ProxyRepository
|
|||||||
|
|
||||||
private async loadDataForToken() {
|
private async loadDataForToken() {
|
||||||
try {
|
try {
|
||||||
|
const stopTimer = this.methodTimer('loadDataForToken');
|
||||||
this.features = await this.featuresForToken();
|
this.features = await this.featuresForToken();
|
||||||
this.segments = await this.segmentsForToken();
|
this.segments = await this.segmentsForToken();
|
||||||
|
stopTimer();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error('Cannot load data for token', e);
|
this.logger.error('Cannot load data for token', e);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const REQUEST_TIME = 'request_time';
|
const REQUEST_TIME = 'request_time';
|
||||||
const DB_TIME = 'db_time';
|
const DB_TIME = 'db_time';
|
||||||
const OPERATION_TIME = 'operation_time';
|
const FUNCTION_TIME = 'function_time';
|
||||||
const SCHEDULER_JOB_TIME = 'scheduler_job_time';
|
const SCHEDULER_JOB_TIME = 'scheduler_job_time';
|
||||||
const FEATURES_CREATED_BY_PROCESSED = 'features_created_by_processed';
|
const FEATURES_CREATED_BY_PROCESSED = 'features_created_by_processed';
|
||||||
const EVENTS_CREATED_BY_PROCESSED = 'events_created_by_processed';
|
const EVENTS_CREATED_BY_PROCESSED = 'events_created_by_processed';
|
||||||
@ -12,7 +12,7 @@ export {
|
|||||||
REQUEST_TIME,
|
REQUEST_TIME,
|
||||||
DB_TIME,
|
DB_TIME,
|
||||||
SCHEDULER_JOB_TIME,
|
SCHEDULER_JOB_TIME,
|
||||||
OPERATION_TIME,
|
FUNCTION_TIME,
|
||||||
FEATURES_CREATED_BY_PROCESSED,
|
FEATURES_CREATED_BY_PROCESSED,
|
||||||
EVENTS_CREATED_BY_PROCESSED,
|
EVENTS_CREATED_BY_PROCESSED,
|
||||||
FRONTEND_API_REPOSITORY_CREATED,
|
FRONTEND_API_REPOSITORY_CREATED,
|
||||||
|
@ -2,7 +2,7 @@ import { register } from 'prom-client';
|
|||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { IEventStore } from './types/stores/event-store';
|
import { IEventStore } from './types/stores/event-store';
|
||||||
import { createTestConfig } from '../test/config/test-config';
|
import { createTestConfig } from '../test/config/test-config';
|
||||||
import { DB_TIME, OPERATION_TIME, REQUEST_TIME } from './metric-events';
|
import { DB_TIME, FUNCTION_TIME, REQUEST_TIME } from './metric-events';
|
||||||
import {
|
import {
|
||||||
CLIENT_METRICS,
|
CLIENT_METRICS,
|
||||||
CLIENT_REGISTER,
|
CLIENT_REGISTER,
|
||||||
@ -172,15 +172,16 @@ test('should collect metrics for db query timings', async () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should collect metrics for operation timings', async () => {
|
test('should collect metrics for function timings', async () => {
|
||||||
eventBus.emit(OPERATION_TIME, {
|
eventBus.emit(FUNCTION_TIME, {
|
||||||
operationId: 'getToggles',
|
functionName: 'getToggles',
|
||||||
|
className: 'ToggleService',
|
||||||
time: 0.1337,
|
time: 0.1337,
|
||||||
});
|
});
|
||||||
|
|
||||||
const metrics = await prometheusRegister.metrics();
|
const metrics = await prometheusRegister.metrics();
|
||||||
expect(metrics).toMatch(
|
expect(metrics).toMatch(
|
||||||
/operation_duration_seconds\{quantile="0\.99",operationId="getToggles"\} 0.1337/,
|
/function_duration_seconds\{quantile="0\.99",functionName="getToggles",className="ToggleService"\} 0.1337/,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -85,10 +85,10 @@ export default class MetricsMonitor {
|
|||||||
maxAgeSeconds: 600,
|
maxAgeSeconds: 600,
|
||||||
ageBuckets: 5,
|
ageBuckets: 5,
|
||||||
});
|
});
|
||||||
const operationDuration = createSummary({
|
const functionDuration = createSummary({
|
||||||
name: 'operation_duration_seconds',
|
name: 'function_duration_seconds',
|
||||||
help: 'Operation duration time',
|
help: 'Function duration time',
|
||||||
labelNames: ['operationId'],
|
labelNames: ['functionName', 'className'],
|
||||||
percentiles: [0.1, 0.5, 0.9, 0.95, 0.99],
|
percentiles: [0.1, 0.5, 0.9, 0.95, 0.99],
|
||||||
maxAgeSeconds: 600,
|
maxAgeSeconds: 600,
|
||||||
ageBuckets: 5,
|
ageBuckets: 5,
|
||||||
@ -413,9 +413,14 @@ export default class MetricsMonitor {
|
|||||||
schedulerDuration.labels(jobId).observe(time);
|
schedulerDuration.labels(jobId).observe(time);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventBus.on(events.OPERATION_TIME, ({ operationId, time }) => {
|
eventBus.on(
|
||||||
operationDuration.labels(operationId).observe(time);
|
events.FUNCTION_TIME,
|
||||||
});
|
({ functionName, className, time }) => {
|
||||||
|
functionDuration
|
||||||
|
.labels({ functionName, className })
|
||||||
|
.observe(time);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
eventBus.on(events.EVENTS_CREATED_BY_PROCESSED, ({ updated }) => {
|
eventBus.on(events.EVENTS_CREATED_BY_PROCESSED, ({ updated }) => {
|
||||||
eventCreatedByMigration.inc(updated);
|
eventCreatedByMigration.inc(updated);
|
||||||
|
Loading…
Reference in New Issue
Block a user