redis-smq-common
Version:
Provides essential components and utilities shared across RedisSMQ packages.
64 lines • 2.77 kB
JavaScript
import { WatchedKeysChangedError, WatchTransactionMaxRetriesExceeded, } from './errors/index.js';
import { CallbackEmptyReplyError } from '../errors/index.js';
export function withWatchTransaction(client, attemptFn, callback, options) {
const maxAttempts = options?.maxAttempts ?? 3;
const doAttempt = (attemptNo) => {
if (attemptNo > maxAttempts) {
const err = options?.makeRetryExceededError?.() ??
new WatchTransactionMaxRetriesExceeded();
return callback(err);
}
const watch = (keys, cb) => {
const arr = Array.isArray(keys) ? keys : [keys];
if (arr.length === 0)
return cb();
client.watch(arr, (err) => {
if (err && options?.onWatchError)
options.onWatchError(err);
cb(err || null);
});
};
attemptFn(client, watch, (attemptErr, result) => {
if (attemptErr) {
return client.unwatch(() => callback(attemptErr));
}
if (!result || !result.multi) {
return client.unwatch(() => callback(new CallbackEmptyReplyError({
message: 'Invalid attempt result. Expected a result object with a "multi" key.',
})));
}
result.multi.exec((execErr, execRes) => {
if (execErr) {
if (execErr instanceof WatchedKeysChangedError) {
options?.onRetry?.(attemptNo, maxAttempts);
return client.unwatch(() => {
const backoff = options?.backoff ?? ((n) => n * 1000);
const delay = backoff(attemptNo);
if (delay && delay > 0) {
setTimeout(() => doAttempt(attemptNo + 1), delay);
}
else {
doAttempt(attemptNo + 1);
}
});
}
if (options?.onExecError)
options.onExecError(execErr);
return client.unwatch(() => callback(execErr));
}
if (result.afterExec) {
try {
result.afterExec(execRes ?? []);
}
catch (e) {
const err = e instanceof Error ? e : new Error(String(e));
return callback(err);
}
}
callback(null, execRes);
});
});
};
doAttempt(1);
}
//# sourceMappingURL=with-watch-transaction.js.map