mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-10 17:53:36 +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,
|
IClientMetricsEnvKey,
|
||||||
IClientMetricsStoreV2,
|
IClientMetricsStoreV2,
|
||||||
} from '../types/stores/client-metrics-store-v2';
|
} from '../types/stores/client-metrics-store-v2';
|
||||||
|
import NotFoundError from '../error/notfound-error';
|
||||||
|
|
||||||
interface ClientMetricsEnvTable {
|
interface ClientMetricsEnvTable {
|
||||||
feature_name: string;
|
feature_name: string;
|
||||||
@ -53,8 +54,19 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
|
|||||||
this.logger = getLogger('client-metrics-store-v2.js');
|
this.logger = getLogger('client-metrics-store-v2.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
get(key: IClientMetricsEnvKey): Promise<IClientMetricsEnv> {
|
async get(key: IClientMetricsEnvKey): Promise<IClientMetricsEnv> {
|
||||||
throw new Error('Method not implemented.');
|
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[]> {
|
async getAll(query: Object = {}): Promise<IClientMetricsEnv[]> {
|
||||||
@ -64,12 +76,24 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
|
|||||||
return rows.map(fromRow);
|
return rows.map(fromRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
exists(key: IClientMetricsEnvKey): Promise<boolean> {
|
async exists(key: IClientMetricsEnvKey): Promise<boolean> {
|
||||||
throw new Error('Method not implemented.');
|
try {
|
||||||
|
await this.get(key);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(key: IClientMetricsEnvKey): Promise<void> {
|
async delete(key: IClientMetricsEnvKey): Promise<void> {
|
||||||
throw new Error('Method not implemented.');
|
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> {
|
deleteAll(): Promise<void> {
|
||||||
@ -130,4 +154,10 @@ export class ClientMetricsStoreV2 implements IClientMetricsStoreV2 {
|
|||||||
.pluck('app_name')
|
.pluck('app_name')
|
||||||
.orderBy('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();
|
metricsMonitor.stopMonitoring();
|
||||||
stores.clientInstanceStore.destroy();
|
stores.clientInstanceStore.destroy();
|
||||||
stores.clientMetricsStore.destroy();
|
stores.clientMetricsStore.destroy();
|
||||||
|
services.clientMetricsServiceV2.destroy();
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,9 +10,10 @@ import {
|
|||||||
import { clientMetricsSchema } from './client-metrics-schema';
|
import { clientMetricsSchema } from './client-metrics-schema';
|
||||||
|
|
||||||
const FIVE_MINUTES = 5 * 60 * 1000;
|
const FIVE_MINUTES = 5 * 60 * 1000;
|
||||||
|
const ONE_DAY = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
export default class ClientMetricsServiceV2 {
|
export default class ClientMetricsServiceV2 {
|
||||||
private timers: NodeJS.Timeout[] = [];
|
private timer: NodeJS.Timeout;
|
||||||
|
|
||||||
private clientMetricsStoreV2: IClientMetricsStoreV2;
|
private clientMetricsStoreV2: IClientMetricsStoreV2;
|
||||||
|
|
||||||
@ -30,6 +31,11 @@ export default class ClientMetricsServiceV2 {
|
|||||||
this.logger = getLogger('/services/client-metrics/index.ts');
|
this.logger = getLogger('/services/client-metrics/index.ts');
|
||||||
|
|
||||||
this.bulkInterval = bulkInterval;
|
this.bulkInterval = bulkInterval;
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
console.log('Clear metrics');
|
||||||
|
this.clientMetricsStoreV2.clearMetrics(48);
|
||||||
|
}, ONE_DAY);
|
||||||
|
this.timer.unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerClientMetrics(
|
async registerClientMetrics(
|
||||||
@ -97,4 +103,9 @@ export default class ClientMetricsServiceV2 {
|
|||||||
): Promise<IClientMetricsEnv[]> {
|
): Promise<IClientMetricsEnv[]> {
|
||||||
return this.clientMetricsStoreV2.getMetricsForFeatureToggle(toggleName);
|
return this.clientMetricsStoreV2.getMetricsForFeatureToggle(toggleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,5 @@ export interface IClientMetricsStoreV2
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
hoursBack?: number,
|
hoursBack?: number,
|
||||||
): Promise<string[]>;
|
): 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);
|
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();
|
super();
|
||||||
this.setMaxListeners(0);
|
this.setMaxListeners(0);
|
||||||
}
|
}
|
||||||
|
clearMetrics(hoursBack: number): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
getSeenAppsForFeatureToggle(
|
getSeenAppsForFeatureToggle(
|
||||||
featureName: string,
|
featureName: string,
|
||||||
hoursBack?: number,
|
hoursBack?: number,
|
||||||
|
Loading…
Reference in New Issue
Block a user