diff --git a/src/lib/services/project-health-service.ts b/src/lib/services/project-health-service.ts index 729da8d1e2..85a12b3e05 100644 --- a/src/lib/services/project-health-service.ts +++ b/src/lib/services/project-health-service.ts @@ -10,6 +10,9 @@ import { calculateProjectHealth, calculateProjectHealthRating, } from '../domain/project-health/project-health'; +import { batchExecute } from '../util'; +import metricsHelper from '../util/metrics-helper'; +import { FUNCTION_TIME } from '../metric-events'; export default class ProjectHealthService { private logger: Logger; @@ -22,7 +25,9 @@ export default class ProjectHealthService { private projectService: ProjectService; - calculateHealthRating: (project: IProject) => Promise; + calculateHealthRating: (project: Pick) => Promise; + + private timer: Function; constructor( { @@ -33,7 +38,7 @@ export default class ProjectHealthService { IUnleashStores, 'projectStore' | 'featureTypeStore' | 'featureToggleStore' >, - { getLogger }: Pick, + { getLogger, eventBus }: Pick, projectService: ProjectService, ) { this.logger = getLogger('services/project-health-service.ts'); @@ -46,6 +51,11 @@ export default class ProjectHealthService { this.featureTypeStore, this.featureToggleStore, ); + this.timer = (functionName: string) => + metricsHelper.wrapTimer(eventBus, FUNCTION_TIME, { + className: 'ProjectHealthService', + functionName, + }); } async getProjectHealthReport( @@ -70,17 +80,21 @@ export default class ProjectHealthService { }; } - async setHealthRating(): Promise { + async setHealthRating(batchSize = 1): Promise { const projects = await this.projectStore.getAll(); - await Promise.all( - projects.map(async (project) => { - const newHealth = await this.calculateHealthRating(project); - await this.projectStore.updateHealth({ - id: project.id, - health: newHealth, - }); - }), + void batchExecute(projects, batchSize, 5000, (project) => + this.setProjectHealthRating(project.id), ); } + + async setProjectHealthRating(projectId: string): Promise { + const stopTimer = this.timer('setProjectHealthRating'); + const newHealth = await this.calculateHealthRating({ id: projectId }); + await this.projectStore.updateHealth({ + id: projectId, + health: newHealth, + }); + stopTimer(); + } } diff --git a/src/test/e2e/api/admin/project/project.health.e2e.test.ts b/src/test/e2e/api/admin/project/project.health.e2e.test.ts index 52136f373d..b2b02b489b 100644 --- a/src/test/e2e/api/admin/project/project.health.e2e.test.ts +++ b/src/test/e2e/api/admin/project/project.health.e2e.test.ts @@ -112,7 +112,7 @@ test('Health rating endpoint yields stale, potentially stale and active count on stale: true, }) .expect(201); - await app.services.projectHealthService.setHealthRating(); + await app.services.projectHealthService.setProjectHealthRating(project.id); await app.request .get(`/api/admin/projects/${project.id}/health-report`) .expect(200) @@ -177,7 +177,7 @@ test('Health rating endpoint does not include archived toggles when calculating }) .expect(201); - await app.services.projectHealthService.setHealthRating(); + await app.services.projectHealthService.setProjectHealthRating(project.id); await app.request .get(`/api/admin/projects/${project.id}/health-report`) .expect(200) @@ -232,7 +232,7 @@ test('Health rating endpoint correctly handles potentially stale toggles', async createdAt: new Date(2019, 5, 1), }) .expect(201); - await app.services.projectHealthService.setHealthRating(); + await app.services.projectHealthService.setProjectHealthRating(project.id); await app.request .get(`/api/admin/projects/${project.id}/health-report`) .expect(200) @@ -339,7 +339,7 @@ test('Sorts environments correctly if sort order is equal', async () => { }); test('Update update_at when setHealth runs', async () => { - await app.services.projectHealthService.setHealthRating(); + await app.services.projectHealthService.setProjectHealthRating('default'); await app.request .get('/api/admin/projects/default/health-report') .expect(200)