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:
parent
fbdae0df0a
commit
8b8b2eb9aa
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ async function createApp(
|
||||
metricsMonitor.stopMonitoring();
|
||||
stores.clientInstanceStore.destroy();
|
||||
stores.clientMetricsStore.destroy();
|
||||
services.clientMetricsServiceV2.destroy();
|
||||
await db.destroy();
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,5 @@ export interface IClientMetricsStoreV2
|
||||
featureName: string,
|
||||
hoursBack?: number,
|
||||
): Promise<string[]>;
|
||||
clearMetrics(hoursAgo: number): Promise<void>;
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user