@grouparoo/core
Version:
The Grouparoo Core
119 lines (118 loc) • 4.64 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLS = void 0;
const actionhero_1 = require("actionhero");
const cls_hooked_1 = __importDefault(require("cls-hooked"));
/**
* This module is so you can delay the execution of side-effects within a transaction.
* Say your new Model() creates a task... you don't want to enqueue it until after the transaction settles.
* Use: `CLS.enqueueTaskIn(1000, taskName, args)`
* If you are in a CLS transaction, it will be run afterwards by CLSTask or CLSAction
* If you aren't in a CLS transaction, it will be run now.
*
* The more generic usage is `CLS.afterCommit(() => {})` where you can supply any async function
*
* Learn more @ https://sequelize.org/master/manual/transactions.html and https://github.com/Jeff-Lewis/cls-hooked
*/
var CLS;
(function (CLS) {
// export const namespace = cls.getNamespace("grouparoo-cls");
function getNamespace() {
return cls_hooked_1.default.getNamespace("grouparoo-cls");
}
CLS.getNamespace = getNamespace;
function get(key) {
return getNamespace().get(key);
}
CLS.get = get;
function set(key, data) {
return getNamespace().set(key, data);
}
CLS.set = set;
CLS.wrap = async (f, options = {}) => {
const { catchError } = options;
try {
const runResponse = await wrapInternal(f, options);
return runResponse;
}
catch (error) {
if (catchError)
return error;
throw error;
}
};
const wrapInternal = async (f, options = {}) => {
const { write, priority } = options;
let runResponse;
let afterCommitJobs = [];
const transOptions = {};
const dialect = actionhero_1.api.sequelize.options.dialect;
if (dialect === "sqlite") {
if (write) {
// if we take out the write lock immediately, then this BEGIN IMMEDIATE TRANSACTION
// call will be the one that fails if someone else is writing with SQLITE_BUSY
transOptions.type = "IMMEDIATE";
}
const retryLength = 60 * 1000; // give the UI 60 seconds total before error
const betweenTries = 1; // 1 millisecond
if (priority) {
// this will retry based on the default in the config, but for a priority case,
// we can say to sleep less each time and retry much more often
const retry = Object.assign({}, actionhero_1.config.sequelize.retry);
retry.backoffBase = betweenTries;
retry.backoffExponent = 1; // don't backoff
retry.max = Math.round(retryLength / betweenTries);
retry.timeout = retryLength;
transOptions.retry = retry;
}
else {
// give a gap for the UI thread to get access
await actionhero_1.utils.sleep(betweenTries + 5);
}
}
await actionhero_1.api.sequelize.transaction(transOptions, async (t) => {
runResponse = await f(t);
afterCommitJobs = getNamespace().get("afterCommitJobs");
});
for (const i in afterCommitJobs) {
await afterCommitJobs[i]();
}
return runResponse;
};
function active() {
const transaction = get("transaction");
if (transaction)
return true;
return false;
}
CLS.active = active;
async function afterCommit(f) {
const isActive = active();
// If we aren't in a transaction, run it now
if (!isActive)
return f();
// otherwise, save the job for later
const key = "afterCommitJobs";
const jobs = getNamespace().get(key) || [];
jobs.push(f);
getNamespace().set(key, jobs);
}
CLS.afterCommit = afterCommit;
/**
* A CLS wrapper around task.enqueue
*/
async function enqueueTask(taskName, args, queue) {
await afterCommit(async () => actionhero_1.task.enqueue(taskName, args, queue));
}
CLS.enqueueTask = enqueueTask;
/**
* A CLS wrapper around task.enqueueIn
*/
async function enqueueTaskIn(delay, taskName, args, queue) {
await afterCommit(async () => actionhero_1.task.enqueueIn(delay, taskName, args, queue));
}
CLS.enqueueTaskIn = enqueueTaskIn;
})(CLS = exports.CLS || (exports.CLS = {}));