App health check: Optimize pending migration query (#11049)
## Optimization: Efficient Health Check Query This PR optimizes the workspace health check system by replacing the N+1 query pattern with efficient database queries. ### Key Improvements - **Eliminated N+1 Query Problem**: Instead of fetching all workspaces and then querying each one individually for pending migrations (which caused slowness in production), we now use a single optimized query to directly identify workspaces with pending migrations - **Better Performance**: Reduced the number of database queries from potentially hundreds/thousands (previous implementation) to just 2 fixed queries regardless of workspace count - **Full Coverage Instead of Sampling**: Rather than implementing a cap on workspace checks at 100 (which was a workaround for performance issues), this solution addresses the root cause by optimizing the query pattern. We can now efficiently check all workspaces with pending migrations without performance penalties. @FelixMalfait This addresses the "always eager-load when you can" feedback by handling the problem at the database level rather than just applying a limit. The optimized query should solve both the performance issues and provide more accurate health status information.
This commit is contained in:
@ -49,6 +49,42 @@ export class WorkspaceMigrationService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find workspaces with pending migrations
|
||||
*
|
||||
* @returns Promise<{ workspaceId: string; pendingMigrations: number }[]>
|
||||
*/
|
||||
public async getWorkspacesWithPendingMigrations(limit: number) {
|
||||
const results = await this.workspaceMigrationRepository
|
||||
.createQueryBuilder('workspaceMigration')
|
||||
.select('workspaceMigration.workspaceId', 'workspaceId')
|
||||
.addSelect('COUNT(*)', 'pendingCount')
|
||||
.where('workspaceMigration.appliedAt IS NULL')
|
||||
.groupBy('workspaceMigration.workspaceId')
|
||||
.limit(limit)
|
||||
.getRawMany();
|
||||
|
||||
return results.map((result) => ({
|
||||
workspaceId: result.workspaceId,
|
||||
pendingMigrations: Number(result.pendingCount) || 0,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total number of workspaces with pending migrations
|
||||
*
|
||||
* @returns Promise<number>
|
||||
*/
|
||||
public async countWorkspacesWithPendingMigrations(): Promise<number> {
|
||||
const result = await this.workspaceMigrationRepository
|
||||
.createQueryBuilder('workspaceMigration')
|
||||
.select('COUNT(DISTINCT workspaceMigration.workspaceId)', 'count')
|
||||
.where('workspaceMigration.appliedAt IS NULL')
|
||||
.getRawOne();
|
||||
|
||||
return Number(result.count) || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set appliedAt as current date for a given migration.
|
||||
* Should be called once the migration has been applied
|
||||
|
||||
Reference in New Issue
Block a user