UNPKG

@dossierhq/core

Version:

The core Dossier library used by clients and server alike, used to interact with schema and entities directly, as well as remotely through a client.

63 lines 2.54 kB
/// <reference types="./AdvisoryLockUtils.d.ts" /> import { createErrorResult, ErrorType, notOk, } from '../ErrorResult.js'; import { NoOpLogger } from '../Logger.js'; export async function withAdvisoryLock(client, name, options, callback) { // Acquire lock const { acquireInterval, renewInterval, ...acquireOptions } = options; const acquireResult = await acquireLockWithRetry(client, name, acquireOptions, acquireInterval); if (acquireResult.isError()) return notOk.Generic(acquireResult.message); // BadRequest -> Generic const { handle } = acquireResult.value; const status = { active: true, renewError: null }; // Keep lock alive const intervalHandle = setInterval(() => { void (async () => { try { const renewResult = await client.renewAdvisoryLock(name, handle); if (renewResult.isError()) { setStatusError(status, notOk.Generic(renewResult.message)); // NotFound -> Generic } } catch (error) { setStatusError(status, notOk.GenericUnexpectedException({ logger: NoOpLogger }, error)); } if (!status.active) { clearInterval(intervalHandle); } })(); }, renewInterval); let result; try { result = await callback(status); } catch (error) { result = notOk.GenericUnexpectedException({ logger: NoOpLogger }, error); } const realRenewError = status.renewError; clearInterval(intervalHandle); if (status.active) { setStatusError(status, notOk.Generic('Somehow accessed status after returning from callback')); await client.releaseAdvisoryLock(name, handle); // ignore potential error of releasing } return realRenewError ?? result; } function setStatusError(status, renewError) { status.active = false; status.renewError = renewError; } async function acquireLockWithRetry(client, name, options, acquireInterval) { while (true) { const result = await client.acquireAdvisoryLock(name, options); if (result.isOk()) return result.map((it) => it); const errorType = result.error; if (errorType === ErrorType.Conflict) { await new Promise((resolve) => setTimeout(resolve, acquireInterval)); continue; // retry } else { return createErrorResult(errorType, result.message); } } } //# sourceMappingURL=AdvisoryLockUtils.js.map