UNPKG

lidex

Version:

lightweight durable execution library

179 lines (178 loc) 7.07 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.forInternalTesting = void 0; exports.makeClient = makeClient; const DEFAULT_MAX_FAILURES = 3; const DEFAULT_TIMEOUT_MS = 60000; // 1m const DEFAULT_POLL_MS = 1000; // 1s const DEFAULT_RETRY_MS = 60000; // 1s function goSleep(ms) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, ms); }); } function makeClaim(persistence, timeoutIntervalMs) { return function () { return __awaiter(this, void 0, void 0, function* () { const now = new Date(); const timeoutAt = new Date(now.getTime() + timeoutIntervalMs); return yield persistence.claim(now, timeoutAt); }); }; } function makeMakeStep(persistence, timeoutIntervalMs) { return function (workflowId) { return function (stepId, fn) { return __awaiter(this, void 0, void 0, function* () { let output = yield persistence.findOutput(workflowId, stepId); if (!(output === undefined)) { return output; } output = yield fn(); const now = new Date(); const timeoutAt = new Date(now.getTime() + timeoutIntervalMs); yield persistence.updateOutput(workflowId, stepId, output, timeoutAt); return output; }); }; }; } function makeMakeSleep(persistence, timeoutIntervalMs) { return function (workflowId) { return function (napId, ms) { return __awaiter(this, void 0, void 0, function* () { let wakeUpAt = yield persistence.findWakeUpAt(workflowId, napId); const now = new Date(); if (wakeUpAt) { const remainingMs = wakeUpAt.getTime() - now.getTime(); if (remainingMs > 0) { yield goSleep(remainingMs); } return; } wakeUpAt = new Date(now.getTime() + ms); const timeoutAt = new Date(wakeUpAt.getTime() + timeoutIntervalMs); yield persistence.updateWakeUpAt(workflowId, napId, wakeUpAt, timeoutAt); yield goSleep(ms); }); }; }; } function makeRun(persistence, handlers, makeStep, makeSleep, start, maxFailures, retryIntervalMs) { return function (workflowId) { return __awaiter(this, void 0, void 0, function* () { const runData = yield persistence.findRunData(workflowId); if (!runData) { throw new Error(`workflow not found: ${workflowId}`); } const fn = handlers.get(runData.handler); if (!fn) { throw new Error(`handler not found: ${runData.handler}`); } const ctx = { step: makeStep(workflowId), sleep: makeSleep(workflowId), start, }; try { yield fn(ctx, runData.input); } catch (error) { let lastError = ""; if (error instanceof Error) { lastError = error.message; } else { lastError = JSON.stringify(error); } const failures = (runData.failures || 0) + 1; const status = failures < maxFailures ? "failed" : "aborted"; const now = new Date(); const timeoutAt = new Date(now.getTime() + retryIntervalMs); yield persistence.updateStatus(workflowId, status, timeoutAt, failures, lastError); return; } yield persistence.setAsFinished(workflowId); }); }; } function makeStart(persistence) { return function (workflowId, handler, input) { return __awaiter(this, void 0, void 0, function* () { return persistence.insert(workflowId, handler, input); }); }; } function makeWait(persistence) { return function (workflowId, status, times, ms) { return __awaiter(this, void 0, void 0, function* () { for (let i = 0; i < times; i++) { const found = yield persistence.findStatus(workflowId); if (found && status.includes(found)) { return found; } yield goSleep(ms); } return undefined; }); }; } function makePoll(claim, run, pollIntervalMs) { return function (shouldStop) { return __awaiter(this, void 0, void 0, function* () { while (!shouldStop()) { const workflowId = yield claim(); if (workflowId) { run(workflowId); // Intentionally not awaiting } else { yield goSleep(pollIntervalMs); } } }); }; } /** * Creates a client based on the given configuration. If no configuration is * provided, then the library defaults are used. * @param config The configutarion object. * @returns The client instance. */ function makeClient(config) { return __awaiter(this, void 0, void 0, function* () { const { handlers, persistence } = config; yield persistence.init(); const maxFailures = config.maxFailures || DEFAULT_MAX_FAILURES; const timeoutIntervalMs = config.timeoutIntervalMs || DEFAULT_TIMEOUT_MS; const pollIntervalMs = config.pollIntervalMs || DEFAULT_POLL_MS; const retryIntervalMs = config.retryIntervalMs || DEFAULT_RETRY_MS; const start = makeStart(persistence); const wait = makeWait(persistence); const claim = makeClaim(persistence, timeoutIntervalMs); const makeStep = makeMakeStep(persistence, timeoutIntervalMs); const makeSleep = makeMakeSleep(persistence, timeoutIntervalMs); const run = makeRun(persistence, handlers, makeStep, makeSleep, start, maxFailures, retryIntervalMs); const poll = makePoll(claim, run, pollIntervalMs); return { start, wait, poll }; }); } exports.forInternalTesting = { makeClaim, makeMakeStep, makeMakeSleep, makeRun, makeStart, makeWait, makePoll, };