@azure/core-amqp
Version:
Common library for amqp based azure sdks like @azure/event-hubs.
159 lines (158 loc) • 5.55 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var lock_exports = {};
__export(lock_exports, {
CancellableAsyncLockImpl: () => CancellableAsyncLockImpl
});
module.exports = __toCommonJS(lock_exports);
var import_abort_controller = require("@azure/abort-controller");
var import_rhea_promise = require("rhea-promise");
var import_constants = require("./constants.js");
var import_log = require("../log.js");
class CancellableAsyncLockImpl {
_keyMap = /* @__PURE__ */ new Map();
_executionRunningSet = /* @__PURE__ */ new Set();
/**
* Returns a promise that resolves to the value returned by the provided task function.
* Only 1 task can be invoked at a time for a given `key` value.
*
* An acquire call can be cancelled via an `abortSignal`.
* If cancelled, the promise will be rejected with an `AbortError`.
*
* `acquireTimeoutInMs` can also be provided to properties.
* If the timeout is reached before the provided `task` is invoked,
* then the promise will be rejected with an Error stating the task
* timed out waiting to acquire a lock.
*
* @param key - All `acquire` calls are grouped by the provided `key`.
* @param task - The function to invoke once the lock has been acquired.
* @param properties - Additional properties to control the behavior of `acquire`.
*/
acquire(key, task, properties) {
const { abortSignal, timeoutInMs } = properties;
if (abortSignal?.aborted) {
return Promise.reject(new import_abort_controller.AbortError(import_constants.StandardAbortMessage));
}
const taskQueue = this._keyMap.get(key) ?? [];
this._keyMap.set(key, taskQueue);
const { promise, rejecter, resolver } = getPromiseParts();
const taskDetails = {
reject: rejecter,
resolve: resolver,
task
};
if (typeof timeoutInMs === "number") {
const tid = setTimeout(
() => {
this._removeTaskDetails(key, taskDetails);
rejecter(
new import_rhea_promise.OperationTimeoutError(`The task timed out waiting to acquire a lock for ${key}`)
);
},
Math.max(timeoutInMs, 0)
);
taskDetails.tid = tid;
}
if (abortSignal) {
const abortListener = () => {
this._removeTaskDetails(key, taskDetails);
rejecter(new import_abort_controller.AbortError(import_constants.StandardAbortMessage));
};
abortSignal.addEventListener("abort", abortListener);
taskDetails.abortSignal = abortSignal;
taskDetails.abortListener = abortListener;
}
taskQueue.push(taskDetails);
import_log.logger.verbose(
`Called acquire() for lock "${key}". Lock "${key}" has ${taskQueue.length} pending tasks.`
);
this._execute(key);
return promise;
}
/**
* Iterates over all the pending tasks for a given `key` serially.
*
* Note: If the pending tasks are already being iterated by an early
* _execute invocation, this returns immediately.
* @returns
*/
async _execute(key) {
if (this._executionRunningSet.has(key)) {
return;
}
const taskQueue = this._keyMap.get(key);
if (!taskQueue || !taskQueue.length) {
return;
}
this._executionRunningSet.add(key);
while (taskQueue.length) {
const taskDetails = taskQueue.shift();
if (!taskDetails) {
continue;
}
try {
import_log.logger.verbose(`Acquired lock for "${key}", invoking task.`);
cleanupTaskDetails(taskDetails);
const value = await taskDetails.task();
taskDetails.resolve(value);
} catch (err) {
taskDetails.reject(err);
}
import_log.logger.verbose(
`Task completed for lock "${key}". Lock "${key}" has ${taskQueue.length} pending tasks.`
);
}
this._executionRunningSet.delete(key);
this._keyMap.delete(key);
}
_removeTaskDetails(key, taskDetails) {
const taskQueue = this._keyMap.get(key);
if (!taskQueue || !taskQueue.length) {
return;
}
const index = taskQueue.indexOf(taskDetails);
if (index !== -1) {
const [details] = taskQueue.splice(index, 1);
cleanupTaskDetails(details);
}
}
}
function getPromiseParts() {
let resolver;
let rejecter;
const promise = new Promise((resolve, reject) => {
resolver = resolve;
rejecter = reject;
});
return {
promise,
resolver,
rejecter
};
}
function cleanupTaskDetails(taskDetails) {
if (taskDetails.tid) clearTimeout(taskDetails.tid);
if (taskDetails.abortSignal && taskDetails.abortListener) {
taskDetails.abortSignal.removeEventListener("abort", taskDetails.abortListener);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CancellableAsyncLockImpl
});
//# sourceMappingURL=lock.js.map