backend: add index migration functionality to MigrationService

This commit is contained in:
juancarmore 2026-02-23 10:14:22 +01:00
parent 803e4b6704
commit 248015fc06

View File

@ -28,6 +28,10 @@ export class MigrationService {
* This method should be called during startup to ensure backwards compatibility.
*
* Uses distributed locking to ensure only one instance runs migrations in HA mode.
*
* Migration flow:
* 1) Schema/data migrations
* 2) Index synchronization (drop obsolete + create missing indexes)
*/
async runMigrations(): Promise<void> {
this.logger.info('Running migrations...');
@ -48,6 +52,9 @@ export class MigrationService {
// Run schema migrations to upgrade document structures
await this.runSchemaMigrations();
// Sync collection indexes to match current schema definitions
await this.runIndexMigrations();
this.logger.info('All migrations completed successfully');
} catch (error) {
this.logger.error('Error running migrations:', error);
@ -61,6 +68,59 @@ export class MigrationService {
}
}
/**
* Synchronizes MongoDB indexes for all registered collections.
* This removes obsolete indexes and creates missing ones according to the current schema definitions.
*/
protected async runIndexMigrations(): Promise<void> {
this.logger.info('Running index migrations...');
try {
for (const registry of runtimeMigrationRegistry) {
await this.syncCollectionIndexes(registry);
}
this.logger.info('Index migrations completed successfully');
} catch (error) {
this.logger.error('Error running index migrations:', error);
throw error;
}
}
/**
* Synchronizes indexes for a single collection.
*
* @param registry - The collection migration registry containing model and collection metadata
*/
protected async syncCollectionIndexes<TDocument extends SchemaMigratableDocument>(
registry: CollectionMigrationRegistry<TDocument>
): Promise<void> {
this.logger.info(`Syncing indexes for collection: ${registry.collectionName}`);
const indexesBeforeSync = await registry.model.collection.indexes();
const indexNamesBeforeSync = new Set(indexesBeforeSync.map((index) => index.name));
const droppedIndexes = await registry.model.syncIndexes();
const indexesAfterSync = await registry.model.collection.indexes();
const indexNamesAfterSync = new Set(indexesAfterSync.map((index) => index.name));
const createdIndexes = Array.from(indexNamesAfterSync).filter(
(indexName) => !indexNamesBeforeSync.has(indexName)
);
if (droppedIndexes.length === 0 && createdIndexes.length === 0) {
this.logger.debug(`No index changes for collection: ${registry.collectionName}`);
return;
}
this.logger.info(
`Index sync for ${registry.collectionName}: ` +
`dropped=${droppedIndexes.length} [${droppedIndexes.join(', ')}], ` +
`created=${createdIndexes.length} [${createdIndexes.join(', ')}]`
);
}
/**
* Runs all schema migrations to upgrade document structures to the latest version.
* Processes each collection in the registry and executes pending migrations.