diff --git a/src/lib/db/index.ts b/src/lib/db/index.ts index 95143bec9e..01f5e47502 100644 --- a/src/lib/db/index.ts +++ b/src/lib/db/index.ts @@ -180,7 +180,7 @@ export const createStores = ( segmentReadModel: new SegmentReadModel(db), projectOwnersReadModel: new ProjectOwnersReadModel(db), projectFlagCreatorsReadModel: new ProjectFlagCreatorsReadModel(db), - featureLifecycleStore: new FeatureLifecycleStore(db), + featureLifecycleStore: new FeatureLifecycleStore(db, eventBus), featureStrategiesReadModel: new FeatureStrategiesReadModel(db), onboardingReadModel: createOnboardingReadModel(db), onboardingStore: new OnboardingStore(db), diff --git a/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts b/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts index 97b3713f03..c2bece9604 100644 --- a/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts +++ b/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts @@ -18,7 +18,7 @@ export const createFeatureLifecycleService = (config: IUnleashConfig) => (db: Db) => { const { eventBus, getLogger } = config; const eventStore = new EventStore(db, getLogger); - const featureLifecycleStore = new FeatureLifecycleStore(db); + const featureLifecycleStore = new FeatureLifecycleStore(db, eventBus); const environmentStore = new EnvironmentStore(db, eventBus, config); const featureEnvironmentStore = new FeatureEnvironmentStore( db, diff --git a/src/lib/features/feature-lifecycle/feature-lifecycle-store.ts b/src/lib/features/feature-lifecycle/feature-lifecycle-store.ts index d77daca4c5..b5e0b2382c 100644 --- a/src/lib/features/feature-lifecycle/feature-lifecycle-store.ts +++ b/src/lib/features/feature-lifecycle/feature-lifecycle-store.ts @@ -7,6 +7,9 @@ import type { } from './feature-lifecycle-store-type.js'; import type { Db } from '../../db/db.js'; import type { StageName } from '../../types/index.js'; +import type EventEmitter from 'events'; +import metricsHelper from '../../util/metrics-helper.js'; +import { DB_TIME } from '../../metric-events.js'; type DBType = { stage: StageName; @@ -23,11 +26,19 @@ type DBProjectType = DBType & { export class FeatureLifecycleStore implements IFeatureLifecycleStore { private db: Db; - constructor(db: Db) { + private timer: (string) => any; + + constructor(db: Db, eventBus: EventEmitter) { this.db = db; + this.timer = (action) => + metricsHelper.wrapTimer(eventBus, DB_TIME, { + store: 'feature-lifecycle', + action, + }); } async backfill(): Promise { + const stopTimer = this.timer('backfill'); await this.db.raw(` INSERT INTO feature_lifecycles (feature, stage, created_at) SELECT features.name, 'initial', features.created_at @@ -43,11 +54,13 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { WHERE features.archived_at IS NOT NULL AND feature_lifecycles.feature IS NULL `); + stopTimer(); } async insert( featureLifecycleStages: FeatureLifecycleStage[], ): Promise { + const stopTimer = this.timer('insert'); const existingFeatures = await this.db('features') .select('name') .whereIn( @@ -62,6 +75,7 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { ); if (validStages.length === 0) { + stopTimer(); return []; } const result = await this.db('feature_lifecycles') @@ -78,6 +92,7 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { .onConflict(['feature', 'stage']) .ignore(); + stopTimer(); return result.map((row) => ({ stage: row.stage, feature: row.feature, @@ -85,9 +100,11 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { } async get(feature: string): Promise { + const stopTimer = this.timer('get'); const results = await this.db('feature_lifecycles') .where({ feature }) .orderBy('created_at', 'asc'); + stopTimer(); return results.map(({ stage, status, created_at }: DBType) => ({ stage, @@ -97,10 +114,12 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { } async getAll(): Promise { + const stopTimer = this.timer('getAll'); const results = await this.db('feature_lifecycles as flc') .select('flc.feature', 'flc.stage', 'flc.created_at', 'f.project') .leftJoin('features as f', 'f.name', 'flc.feature') .orderBy('created_at', 'asc'); + stopTimer(); return results.map( ({ feature, stage, created_at, project }: DBProjectType) => ({ @@ -113,27 +132,35 @@ export class FeatureLifecycleStore implements IFeatureLifecycleStore { } async delete(feature: string): Promise { + const stopTimer = this.timer('delete'); await this.db('feature_lifecycles').where({ feature }).del(); + stopTimer(); } async deleteAll(): Promise { + const stopTimer = this.timer('deleteAll'); await this.db('feature_lifecycles').del(); + stopTimer(); } async deleteStage(stage: FeatureLifecycleStage): Promise { + const stopTimer = this.timer('deleteStage'); await this.db('feature_lifecycles') .where({ stage: stage.stage, feature: stage.feature, }) .del(); + stopTimer(); } async stageExists(stage: FeatureLifecycleStage): Promise { + const stopTimer = this.timer('stageExists'); const result = await this.db.raw( `SELECT EXISTS(SELECT 1 FROM feature_lifecycles WHERE stage = ? and feature = ?) AS present`, [stage.stage, stage.feature], ); + stopTimer(); const { present } = result.rows[0]; return present; } diff --git a/src/lib/metrics.test.ts b/src/lib/metrics.test.ts index 531ec6355c..8a255365e8 100644 --- a/src/lib/metrics.test.ts +++ b/src/lib/metrics.test.ts @@ -69,7 +69,7 @@ beforeAll(async () => { featureLifeCycleReadModel = new FeatureLifecycleReadModel(db.rawDatabase); stores.featureLifecycleReadModel = featureLifeCycleReadModel; - featureLifeCycleStore = new FeatureLifecycleStore(db.rawDatabase); + featureLifeCycleStore = new FeatureLifecycleStore(db.rawDatabase, eventBus); stores.featureLifecycleStore = featureLifeCycleStore; statsService = new InstanceStatsService(