mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: count events instead of loading them in memory (#3382)
Refactor project events to use count instead of loading the events in memory
This commit is contained in:
		
							parent
							
								
									30c1ebc3c3
								
							
						
					
					
						commit
						354e54a356
					
				| @ -207,6 +207,35 @@ class EventStore implements IEventStore { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async queryCount(operations: IQueryOperations[]): Promise<number> { | ||||
|         try { | ||||
|             let query: Knex.QueryBuilder = this.db.count().from(TABLE); | ||||
| 
 | ||||
|             operations.forEach((operation) => { | ||||
|                 if (operation.op === 'where') { | ||||
|                     query = this.where(query, operation.parameters); | ||||
|                 } | ||||
| 
 | ||||
|                 if (operation.op === 'forFeatures') { | ||||
|                     query = this.forFeatures(query, operation.parameters); | ||||
|                 } | ||||
| 
 | ||||
|                 if (operation.op === 'beforeDate') { | ||||
|                     query = this.beforeDate(query, operation.parameters); | ||||
|                 } | ||||
| 
 | ||||
|                 if (operation.op === 'betweenDate') { | ||||
|                     query = this.betweenDate(query, operation.parameters); | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             const queryResult = await query.first(); | ||||
|             return parseInt(queryResult.count || 0); | ||||
|         } catch (e) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     where( | ||||
|         query: Knex.QueryBuilder, | ||||
|         parameters: { [key: string]: string }, | ||||
|  | ||||
| @ -43,7 +43,7 @@ async function createApp( | ||||
|     const db = createDb(config); | ||||
|     const stores = createStores(config, db); | ||||
|     const services = createServices(stores, config, db); | ||||
|     scheduleServices(services, config); | ||||
|     scheduleServices(services); | ||||
| 
 | ||||
|     const metricsMonitor = createMetricsMonitor(); | ||||
|     const unleashSession = sessionDb(config, db); | ||||
|  | ||||
| @ -58,10 +58,7 @@ import { | ||||
| } from '../features/change-request-access-service/createChangeRequestAccessReadModel'; | ||||
| 
 | ||||
| // TODO: will be moved to scheduler feature directory
 | ||||
| export const scheduleServices = ( | ||||
|     services: IUnleashServices, | ||||
|     config: IUnleashConfig, | ||||
| ): void => { | ||||
| export const scheduleServices = (services: IUnleashServices): void => { | ||||
|     const { | ||||
|         schedulerService, | ||||
|         apiTokenService, | ||||
| @ -94,12 +91,10 @@ export const scheduleServices = ( | ||||
|         hoursToMilliseconds(24), | ||||
|     ); | ||||
| 
 | ||||
|     if (config.flagResolver.isEnabled('projectStatusApi')) { | ||||
|         schedulerService.schedule( | ||||
|             projectService.statusJob.bind(projectService), | ||||
|             hoursToMilliseconds(24), | ||||
|         ); | ||||
|     } | ||||
|     schedulerService.schedule( | ||||
|         projectService.statusJob.bind(projectService), | ||||
|         hoursToMilliseconds(24), | ||||
|     ); | ||||
| 
 | ||||
|     schedulerService.schedule( | ||||
|         projectHealthService.setHealthRating.bind(projectHealthService), | ||||
|  | ||||
| @ -35,6 +35,7 @@ import { | ||||
|     ProjectUserRemovedEvent, | ||||
|     ProjectUserUpdateRoleEvent, | ||||
|     RoleName, | ||||
|     IFlagResolver, | ||||
| } from '../types'; | ||||
| import { | ||||
|     IProjectQuery, | ||||
| @ -114,6 +115,8 @@ export default class ProjectService { | ||||
| 
 | ||||
|     private projectStatsStore: IProjectStatsStore; | ||||
| 
 | ||||
|     private flagResolver: IFlagResolver; | ||||
| 
 | ||||
|     constructor( | ||||
|         { | ||||
|             projectStore, | ||||
| @ -157,6 +160,7 @@ export default class ProjectService { | ||||
|         this.groupService = groupService; | ||||
|         this.projectStatsStore = projectStatsStore; | ||||
|         this.logger = config.getLogger('services/project-service.js'); | ||||
|         this.flagResolver = config.flagResolver; | ||||
|     } | ||||
| 
 | ||||
|     async getProjects( | ||||
| @ -664,20 +668,24 @@ export default class ProjectService { | ||||
|     } | ||||
| 
 | ||||
|     async statusJob(): Promise<void> { | ||||
|         const projects = await this.store.getAll(); | ||||
|         if (this.flagResolver.isEnabled('projectStatusApi')) { | ||||
|             const projects = await this.store.getAll(); | ||||
| 
 | ||||
|         const statusUpdates = await Promise.all( | ||||
|             projects.map((project) => this.getStatusUpdates(project.id)), | ||||
|         ); | ||||
|             const statusUpdates = await Promise.all( | ||||
|                 projects.map((project) => this.getStatusUpdates(project.id)), | ||||
|             ); | ||||
| 
 | ||||
|         await Promise.all( | ||||
|             statusUpdates.map((statusUpdate) => { | ||||
|                 return this.projectStatsStore.updateProjectStats( | ||||
|                     statusUpdate.projectId, | ||||
|                     statusUpdate.updates, | ||||
|                 ); | ||||
|             }), | ||||
|         ); | ||||
|             await Promise.all( | ||||
|                 statusUpdates.map((statusUpdate) => { | ||||
|                     return this.projectStatsStore.updateProjectStats( | ||||
|                         statusUpdate.projectId, | ||||
|                         statusUpdate.updates, | ||||
|                     ); | ||||
|                 }), | ||||
|             ); | ||||
|         } else { | ||||
|             this.logger.info('Project status API is disabled'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async getStatusUpdates(projectId: string): Promise<ICalculateStatus> { | ||||
| @ -726,7 +734,7 @@ export default class ProjectService { | ||||
| 
 | ||||
|         const [projectActivityCurrentWindow, projectActivityPastWindow] = | ||||
|             await Promise.all([ | ||||
|                 this.eventStore.query([ | ||||
|                 this.eventStore.queryCount([ | ||||
|                     { op: 'where', parameters: { project: projectId } }, | ||||
|                     { | ||||
|                         op: 'beforeDate', | ||||
| @ -736,7 +744,7 @@ export default class ProjectService { | ||||
|                         }, | ||||
|                     }, | ||||
|                 ]), | ||||
|                 this.eventStore.query([ | ||||
|                 this.eventStore.queryCount([ | ||||
|                     { op: 'where', parameters: { project: projectId } }, | ||||
|                     { | ||||
|                         op: 'betweenDate', | ||||
| @ -792,9 +800,8 @@ export default class ProjectService { | ||||
|                 createdPastWindow: createdPastWindow.length, | ||||
|                 archivedCurrentWindow: archivedCurrentWindow.length, | ||||
|                 archivedPastWindow: archivedPastWindow.length, | ||||
|                 projectActivityCurrentWindow: | ||||
|                     projectActivityCurrentWindow.length, | ||||
|                 projectActivityPastWindow: projectActivityPastWindow.length, | ||||
|                 projectActivityCurrentWindow: projectActivityCurrentWindow, | ||||
|                 projectActivityPastWindow: projectActivityPastWindow, | ||||
|                 projectMembersAddedCurrentWindow: | ||||
|                     projectMembersAddedCurrentWindow, | ||||
|             }, | ||||
|  | ||||
| @ -15,4 +15,5 @@ export interface IEventStore | ||||
|     searchEvents(search: SearchEventsSchema): Promise<IEvent[]>; | ||||
|     getMaxRevisionId(currentMax?: number): Promise<number>; | ||||
|     query(operations: IQueryOperations[]): Promise<IEvent[]>; | ||||
|     queryCount(operations: IQueryOperations[]): Promise<number>; | ||||
| } | ||||
|  | ||||
| @ -1390,4 +1390,6 @@ test('should get correct amount of project members for current and past window', | ||||
| 
 | ||||
|     const result = await projectService.getStatusUpdates(project.id); | ||||
|     expect(result.updates.projectMembersAddedCurrentWindow).toBe(5); | ||||
|     expect(result.updates.projectActivityCurrentWindow).toBe(6); | ||||
|     expect(result.updates.projectActivityPastWindow).toBe(0); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										5
									
								
								src/test/fixtures/fake-event-store.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								src/test/fixtures/fake-event-store.ts
									
									
									
									
										vendored
									
									
								
							| @ -95,6 +95,11 @@ class FakeEventStore implements IEventStore { | ||||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     async queryCount(operations: IQueryOperations[]): Promise<number> { | ||||
|         if (operations) return 0; | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     setMaxListeners(number: number): EventEmitter { | ||||
|         return this.eventEmitter.setMaxListeners(number); | ||||
|     } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user