Skip to content

Commit d4b800b

Browse files
committed
feat(scorecard): implement cleanup expired metrics mechanism
Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
1 parent f7fc377 commit d4b800b

File tree

6 files changed

+94
-4
lines changed

6 files changed

+94
-4
lines changed

workspaces/scorecard/plugins/scorecard-backend/config.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { SchedulerServiceTaskScheduleDefinitionConfig } from '@backstage/backend
1919
export interface Config {
2020
/** Configuration for scorecard plugin */
2121
scorecard?: {
22+
/** Number of days to retain metric data in the database. Older data will be automatically cleaned up. Default: 365 days */
23+
dataRetentionDays?: number;
2224
/** Configuration for scorecard metric providers */
2325
plugins?: {
2426
/** Configuration for datasource */
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export const DEFAULT_DATA_RETENTION_DAYS = 365;

workspaces/scorecard/plugins/scorecard-backend/src/database/DatabaseMetricValuesStore.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,13 @@ export class DatabaseMetricValuesStore implements MetricValuesStore {
4949
.groupBy('metric_id'),
5050
);
5151
}
52+
53+
/**
54+
* Delete metric values that are older than the given date
55+
*/
56+
async cleanupExpiredMetrics(olderThan: Date): Promise<number> {
57+
return await this.knex(this.tableName)
58+
.where('timestamp', '<', olderThan)
59+
.del();
60+
}
5261
}

workspaces/scorecard/plugins/scorecard-backend/src/database/MetricValuesStore.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ export interface MetricValuesStore {
4141
catalog_entity_ref: string,
4242
metric_ids: string[],
4343
): Promise<DbMetricValue[]>;
44+
45+
/**
46+
* Delete metric values that are older than the given date
47+
*/
48+
cleanupExpiredMetrics(olderThan: Date): Promise<number>;
4449
}

workspaces/scorecard/plugins/scorecard-backend/src/providers/MetricProvidersScheduler.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import {
3131
MetricValuesStore,
3232
} from '../database/MetricValuesStore';
3333
import { MetricProvidersRegistry } from './MetricProvidersRegistry';
34+
import { DEFAULT_DATA_RETENTION_DAYS } from '../constants/metricSchedules';
35+
import { daysToMilliseconds } from '../utils/daysToMilliseconds';
3436

3537
export class MetricProvidersScheduler {
3638
private static readonly CATALOG_BATCH_SIZE = 50;
@@ -46,14 +48,19 @@ export class MetricProvidersScheduler {
4648
) {}
4749

4850
async schedule() {
51+
this.scheduleCleanupExpiredMetrics();
52+
this.scheduleLoadProviderMetrics();
53+
}
54+
55+
private async scheduleLoadProviderMetrics() {
56+
let countScheduledProviders = 0;
4957
const providers = this.metricProvidersRegistry.listProviders();
50-
const scheduledProviders: string[] = [];
5158

5259
await Promise.all(
5360
providers.map(async provider => {
5461
try {
5562
await this.scheduleProvider(provider);
56-
scheduledProviders.push(provider.getProviderId());
63+
countScheduledProviders++;
5764
} catch (e) {
5865
this.logger.warn(
5966
`Failed to schedule provider ${provider.getProviderId()}, ${e}`,
@@ -63,7 +70,7 @@ export class MetricProvidersScheduler {
6370
);
6471

6572
this.logger.info(
66-
`Scheduled ${scheduledProviders.length}/${providers.length} metric providers`,
73+
`Scheduled ${countScheduledProviders}/${providers.length} metric providers`,
6774
);
6875
}
6976

@@ -173,9 +180,40 @@ export class MetricProvidersScheduler {
173180
this.config.getConfig(schedulePath),
174181
)
175182
: ({
176-
frequency: { hours: 2 },
183+
frequency: { hours: 1 },
177184
timeout: { minutes: 15 },
178185
initialDelay: { seconds: 3 },
179186
} as SchedulerServiceTaskScheduleDefinition);
180187
}
188+
189+
private async cleanupExpiredMetric(logger: LoggerService): Promise<void> {
190+
const dataRetentionDays =
191+
this.config.getOptionalNumber('scorecard.dataRetentionDays') ??
192+
DEFAULT_DATA_RETENTION_DAYS;
193+
const olderThan = new Date(
194+
Date.now() - daysToMilliseconds(dataRetentionDays),
195+
);
196+
197+
const deletedMetrics = await this.metricValuesStore.cleanupExpiredMetrics(
198+
olderThan,
199+
);
200+
logger.info(
201+
`Deleted ${deletedMetrics} abandoned metrics older than ${dataRetentionDays} days`,
202+
);
203+
}
204+
205+
/**
206+
* Schedule a task to delete abandoned metrics
207+
*/
208+
private async scheduleCleanupExpiredMetrics() {
209+
await this.scheduler.scheduleTask({
210+
id: 'cleanup-expired-metrics',
211+
frequency: { days: 1 },
212+
timeout: { minutes: 2 },
213+
initialDelay: { seconds: 3 },
214+
fn: async () => {
215+
await this.cleanupExpiredMetric(this.logger);
216+
},
217+
});
218+
}
181219
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export const daysToMilliseconds = (days: number) => {
18+
return days * 24 * 60 * 60 * 1000;
19+
};

0 commit comments

Comments
 (0)