@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
JavaScript
/// <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