UNPKG

@grouparoo/core

Version:
119 lines (118 loc) 4.64 kB
"use strict"; 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 = {}));