lidex
Version:
lightweight durable execution library
179 lines (178 loc) • 7.07 kB
JavaScript
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,
};
;