UNPKG

@tmlmobilidade/utils

Version:

A collection of utility functions and helpers for the TML Mobilidade Go monorepo, providing common functionality for batching operations, caching, HTTP requests, object manipulation, permissions, and more.

80 lines (79 loc) 5.32 kB
/* * */ import { Timer } from '@tmlmobilidade/timer'; /** * Copy documents from a source database to a destination database in multiple steps. * The goal of this function is to ensure that the destination database has the same documents * as the source database. The replication process is designed to be efficient and to minimize * the amount of data transferred between the two databases by only syncing the missing documents. * * 1. First count the total number of documents in both databases to check if they match. This is a * crucial optimization step, as it allows us to skip the replication process if both databases already * have the same number of documents, which would indicate that they are already in sync. * Though, it's important to note that having the same document count does not guarantee * that the documents are identical, but it is a quick check to potentially avoid unnecessary replication. * * 2. If the counts do not match, get the distinct document IDs from both databases and compare them * to find out which ones are missing in the destination database. This step is essential to identify * the specific documents that need to be replicated, rather than syncing all documents again. * Sync only the missing documents from the source database to the destination database, * * 3. Delete any extra documents in the destination database that are not present in the source database. * * 4. Run the onComplete callback function if provided. This allows for any additional actions to be performed after * the replication process is complete, such as logging, flushing writers or any other necessary cleanup tasks. */ export async function replicate({ countDestinationDbFn, countSourceDbFn, deleteDestinationDbFn, distinctDestinationDbFn, distinctSourceDbFn, missingDocumentsSourceDbAsyncIterator, onCompleteCallbackFn, writeSourceDocumentToDestinationDbFn }) { // const globalTimer = new Timer(); // // Run the count functions for both databases, if enabled, to get the total number // of documents that match a given query. This is done to check if the document count // is the same for both databases, which would indicate that all documents are already synced. const countStepTimer = new Timer(); const sourceDbCount = await countSourceDbFn(); const destinationDbCount = await countDestinationDbFn(); if (sourceDbCount === destinationDbCount) { console.info(`MATCH: Found the same number of documents in both databases: ${sourceDbCount} Source = ${destinationDbCount} Destination (${countStepTimer.get()})`); return; } console.info(`MISMATCH: Document count was different for both databases: ${sourceDbCount} Source != ${destinationDbCount} Destination (${countStepTimer.get()})`); // // If the document count was different, then check which documents are missing. // Instead of syncing all documents again, only the missing IDs are synced. // This is done to get the distinct values from each database and comparing // them to find the missing ones. const distinctStepTimer = new Timer(); const sourceDbDocIds = await distinctSourceDbFn(); const sourceDbDocIdsUnique = new Set(sourceDbDocIds); const destinationDbDocIds = await distinctDestinationDbFn(); const destinationDbDocIdsUnique = new Set(destinationDbDocIds); const missingDocumentIds = sourceDbDocIds.filter((documentId) => !destinationDbDocIdsUnique.has(documentId)); const extraDocumentIds = destinationDbDocIds.filter(doc => !sourceDbDocIdsUnique.has(doc)); console.info(`Source Total: ${sourceDbCount} | Source Unique: ${sourceDbDocIdsUnique.size} | Source ▲: ${sourceDbCount - sourceDbDocIdsUnique.size} | Destination Total: ${destinationDbCount} | Destination Unique: ${destinationDbDocIdsUnique.size} | Destination ▲: ${destinationDbCount - destinationDbDocIdsUnique.size} | Destination Missing: ${missingDocumentIds.length} | Destination Extra: ${extraDocumentIds.length} (${distinctStepTimer.get()})`); // // If there are missing documents, then they are synced. // We query the Source database for the missing documents // and write them to the Destination database. const missingStepTimer = new Timer(); if (missingDocumentIds.length > 0) { for await (const sourceDbDocument of missingDocumentsSourceDbAsyncIterator(missingDocumentIds)) { await writeSourceDocumentToDestinationDbFn(sourceDbDocument); } console.info(`Synced ${missingDocumentIds.length} missing documents to the Destination database. (${missingStepTimer.get()})`); } // // Extra documents in the destination database should be removed, // as they are not present in the source database. const deleteStepTimer = new Timer(); if (extraDocumentIds.length > 0 && deleteDestinationDbFn) { await deleteDestinationDbFn(extraDocumentIds); console.info(`Deleted ${extraDocumentIds.length} extra documents in the Destination database. (${deleteStepTimer.get()})`); } // // After syncing the missing documents, // run the onComplete callback function if provided. if (onCompleteCallbackFn) await onCompleteCallbackFn(); console.info(`Replication complete (${globalTimer.get()})`); // }