1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-05 17:53:12 +02:00

fix: delete metrics older than 48 hours automatically

This commit is contained in:
Ivar Conradi Østhus 2021-10-07 21:40:26 +02:00
parent fbdae0df0a
commit 8b8b2eb9aa
No known key found for this signature in database
GPG Key ID: 31AC596886B0BD09
6 changed files with 173 additions and 7 deletions

View File

@ -7,6 +7,7 @@ import {
IClientMetricsEnvKey,
IClientMetricsStoreV2,
} from '../types/stores/client-metrics-store-v2';
import NotFoundError from '../error/notfound-error';
interface ClientMetricsEnvTable {
feature_name: string;
@ -53,8 +54,19 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
this.logger = getLogger('client-metrics-store-v2.js');
}
get(key: IClientMetricsEnvKey): Promise<IClientMetricsEnv> {
throw new Error('Method not implemented.');
async get(key: IClientMetricsEnvKey): Promise<IClientMetricsEnv> {
const row = await this.db<ClientMetricsEnvTable>(TABLE)
.where({
feature_name: key.featureName,
app_name: key.appName,
environment: key.environment,
timestamp: roundDownToHour(key.timestamp),
})
.first();
if (row) {
return fromRow(row);
}
throw new NotFoundError(`Could not find metric`);
}
async getAll(query: Object = {}): Promise<IClientMetricsEnv[]> {
@ -64,12 +76,24 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
return rows.map(fromRow);
}
exists(key: IClientMetricsEnvKey): Promise<boolean> {
throw new Error('Method not implemented.');
async exists(key: IClientMetricsEnvKey): Promise<boolean> {
try {
await this.get(key);
return true;
} catch (e) {
return false;
}
}
delete(key: IClientMetricsEnvKey): Promise<void> {
throw new Error('Method not implemented.');
async delete(key: IClientMetricsEnvKey): Promise<void> {
return this.db<ClientMetricsEnvTable>(TABLE)
.where({
feature_name: key.featureName,
app_name: key.appName,
environment: key.environment,
timestamp: roundDownToHour(key.timestamp),
})
.del();
}
deleteAll(): Promise<void> {
@ -130,4 +154,10 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
.pluck('app_name')
.orderBy('app_name');
}
async clearMetrics(hoursAgo: number): Promise<void> {
return this.db<ClientMetricsEnvTable>(TABLE)
.whereRaw(`timestamp <= NOW() - INTERVAL '${hoursAgo} hours'`)
.del();
}
}

View File

@ -49,6 +49,7 @@ async function createApp(
metricsMonitor.stopMonitoring();
stores.clientInstanceStore.destroy();
stores.clientMetricsStore.destroy();
services.clientMetricsServiceV2.destroy();
await db.destroy();
};

View File

@ -10,9 +10,10 @@ import {
import { clientMetricsSchema } from './client-metrics-schema';
const FIVE_MINUTES = 5 * 60 * 1000;
const ONE_DAY = 24 * 60 * 60 * 1000;
export default class ClientMetricsServiceV2 {
private timers: NodeJS.Timeout[] = [];
private timer: NodeJS.Timeout;
private clientMetricsStoreV2: IClientMetricsStoreV2;
@ -30,6 +31,11 @@ export default class ClientMetricsServiceV2 {
this.logger = getLogger('/services/client-metrics/index.ts');
this.bulkInterval = bulkInterval;
this.timer = setInterval(() => {
console.log('Clear metrics');
this.clientMetricsStoreV2.clearMetrics(48);
}, ONE_DAY);
this.timer.unref();
}
async registerClientMetrics(
@ -97,4 +103,9 @@ export default class ClientMetricsServiceV2 {
): Promise<IClientMetricsEnv[]> {
return this.clientMetricsStoreV2.getMetricsForFeatureToggle(toggleName);
}
destroy(): void {
clearInterval(this.timer);
this.timer = null;
}
}

View File

@ -23,4 +23,5 @@ export interface IClientMetricsStoreV2
featureName: string,
hoursBack?: number,
): Promise<string[]>;
clearMetrics(hoursAgo: number): Promise<void>;
}

View File

@ -262,3 +262,123 @@ test('Should not fail on undefined list of metrics', async () => {
expect(all).toHaveLength(0);
});
test('Should return delete old metric', async () => {
const twoDaysAgo = new Date();
twoDaysAgo.setHours(-48);
const metrics: IClientMetricsEnv[] = [
{
featureName: 'demo1',
appName: 'web',
environment: 'dev',
timestamp: new Date(),
yes: 2,
no: 2,
},
{
featureName: 'demo2',
appName: 'backend-api',
environment: 'dev',
timestamp: new Date(),
yes: 1,
no: 3,
},
{
featureName: 'demo3',
appName: 'backend-api',
environment: 'dev',
timestamp: twoDaysAgo,
yes: 1,
no: 3,
},
{
featureName: 'demo4',
appName: 'backend-api',
environment: 'dev',
timestamp: twoDaysAgo,
yes: 1,
no: 3,
},
];
await clientMetricsStore.batchInsertMetrics(metrics);
await clientMetricsStore.clearMetrics(24);
const all = await clientMetricsStore.getAll();
expect(all).toHaveLength(2);
expect(all[0].featureName).toBe('demo1');
expect(all[1].featureName).toBe('demo2');
});
test('Should get metric', async () => {
const twoDaysAgo = new Date();
twoDaysAgo.setHours(-48);
const metrics: IClientMetricsEnv[] = [
{
featureName: 'demo1',
appName: 'web',
environment: 'dev',
timestamp: new Date(),
yes: 2,
no: 2,
},
{
featureName: 'demo2',
appName: 'backend-api',
environment: 'dev',
timestamp: new Date(),
yes: 1,
no: 3,
},
{
featureName: 'demo3',
appName: 'backend-api',
environment: 'dev',
timestamp: twoDaysAgo,
yes: 1,
no: 3,
},
{
featureName: 'demo4',
appName: 'backend-api',
environment: 'dev',
timestamp: twoDaysAgo,
yes: 41,
no: 42,
},
];
await clientMetricsStore.batchInsertMetrics(metrics);
const metric = await clientMetricsStore.get({
featureName: 'demo4',
timestamp: twoDaysAgo,
appName: 'backend-api',
environment: 'dev',
});
expect(metric.featureName).toBe('demo4');
expect(metric.yes).toBe(41);
expect(metric.no).toBe(42);
});
test('Should not exists after delete', async () => {
const metric = {
featureName: 'demo4',
appName: 'backend-api',
environment: 'dev',
timestamp: new Date(),
yes: 41,
no: 42,
};
const metrics: IClientMetricsEnv[] = [metric];
await clientMetricsStore.batchInsertMetrics(metrics);
const existBefore = await clientMetricsStore.exists(metric);
expect(existBefore).toBe(true);
await clientMetricsStore.delete(metric);
const existAfter = await clientMetricsStore.exists(metric);
expect(existAfter).toBe(false);
});

View File

@ -18,6 +18,9 @@ export default class FakeClientMetricsStoreV2
super();
this.setMaxListeners(0);
}
clearMetrics(hoursBack: number): Promise<void> {
return Promise.resolve();
}
getSeenAppsForFeatureToggle(
featureName: string,
hoursBack?: number,