UNPKG

@cumulus/ingest

Version:
142 lines (126 loc) 4.03 kB
import { s3PutObject, deleteS3Object, listS3ObjectsV2, } from '@cumulus/aws-client/S3'; import { sleep } from '@cumulus/common'; import * as log from '@cumulus/common/log'; const lockPrefix = 'lock'; export interface Lock { Key?: string, LastModified?: Date } /** * Checks all locks and removes those older than five minutes. Returns a count * of locks that are not older than configured retention period or 5 minutes. * * @param {object} bucket - The AWS S3 bucket with the locks to check * @param {Array} locks - The list of locks in the bucket * @param {number} retentionTimeInSeconds - lock retention time in seconds, default is 300 * @returns {integer} - Number of locks remaining in bucket */ export async function checkOldLocks( bucket: string, locks: Lock[] = [], retentionTimeInSeconds: number = 300 ): Promise<number> { const expirationTimestamp = Date.now() - (retentionTimeInSeconds * 1000); const expiredLocks = locks.filter( (lock) => { if (!lock.LastModified) { throw new TypeError(`Could not find LastModified on ${JSON.stringify(lock)}`); } return lock.LastModified.getTime() < expirationTimestamp; } ); await Promise.all(expiredLocks.map((lock) => { if (!lock.Key) { throw new TypeError(`Could not find Key on ${JSON.stringify(lock)}`); } log.debug(`Removing expired lock ${JSON.stringify(lock)}`); return deleteS3Object(bucket, lock.Key); })); return locks.length - expiredLocks.length; } /** * Counts the number of locks in a bucket. * * @param {object} bucket - The AWS S3 bucket to check * @param {string} providerName - The provider name * @param {number} retentionTimeInSeconds - lock retention time in seconds, default is 300 * @returns {integer} - Number of current locks in the bucket */ export async function countLock( bucket: string, providerName: string, retentionTimeInSeconds?: number ): Promise<number> { const locks = await listS3ObjectsV2({ Bucket: bucket, Prefix: `${lockPrefix}/${providerName}`, }); return checkOldLocks(bucket, locks, retentionTimeInSeconds); } async function addLock( bucket: string, providerName: string, granuleId: string ): Promise<void> { const key = `${lockPrefix}/${providerName}/${granuleId}`; await s3PutObject({ Bucket: bucket, Key: key, Body: '', }); } export async function removeLock( bucket: string, providerName: string, granuleId: string ): Promise<void> { await deleteS3Object( bucket, `${lockPrefix}/${providerName}/${granuleId}` ); } /** * * @param {string} bucket - system bucket to place the lock files * @param {object} provider - provider object * @param {string} provider.id - provider id * @param {number} provider.globalConnectionLimit - provider globalConnectionLimit * @param {number} provider.maxDownloadTime - provider maxDownloadTime for a granule * @param {string} granuleId - id of downloading granule * @param {number} counter - retry counter * @returns {Promise<boolean>} */ export async function proceed( bucket: string, provider: { id: string, globalConnectionLimit?: number, maxDownloadTime?: number, }, granuleId: string, counter = 0 ): Promise<boolean> { const { globalConnectionLimit, maxDownloadTime } = provider; if (globalConnectionLimit === undefined) { return true; } // Fail if lock is not removed after 270 tries. if (counter > 270) { log.debug(`The "${provider.id}" provider has no lock available after ${counter} retries`); return false; } const count = await countLock(bucket, provider.id, maxDownloadTime); if (count >= globalConnectionLimit) { log.debug(`The "${provider.id}" provider's globalConnectionLimit of "${provider.globalConnectionLimit}" has been reached.`); // wait for 5 second and try again await sleep(5000); return proceed(bucket, provider, granuleId, counter + 1); } // add the lock await addLock(bucket, provider.id, granuleId); return true; }