@cadence-mq/driver-memory
Version:
Memory driver for CadenceMQ
105 lines (102 loc) • 3.6 kB
JavaScript
import { createErrorFactory, createJobNotFoundError } from "@cadence-mq/core";
//#region src/errors.ts
const createJobWithSameIdExistsError = createErrorFactory({
code: "jobs.unique-id-constraint-violation",
message: "A job with the same id already exists"
});
//#endregion
//#region src/driver.ts
function getNextJob({ jobsRegistry, processingTimeoutMs, now = /* @__PURE__ */ new Date() }) {
let nextJob = null;
const isJobSelectable = (job) => job.status === "pending" || job.status === "processing" && job.startedAt && job.startedAt.getTime() + processingTimeoutMs < now.getTime();
const isJobScheduledEarlier = (job) => nextJob === null || job.scheduledAt < nextJob.scheduledAt;
for (const job of jobsRegistry.values()) if (isJobSelectable(job) && isJobScheduledEarlier(job)) nextJob = job;
return nextJob;
}
function createMemoryDriver() {
const jobsRegistry = /* @__PURE__ */ new Map();
const pendingResolvers = [];
let nextJobTimeout = null;
const consumeNextJob = async ({ job }) => {
const resolver = pendingResolvers.shift();
if (!resolver) return;
resolver({ job });
};
const refreshConsumption = ({ processingTimeoutMs, getNow = () => /* @__PURE__ */ new Date() }) => {
const now = getNow();
const nextJob = getNextJob({
jobsRegistry,
processingTimeoutMs,
now
});
const nextJobProcessingExpiresAt = nextJob?.startedAt ? new Date(nextJob.startedAt.getTime() + processingTimeoutMs) : void 0;
if (nextJobTimeout) clearTimeout(nextJobTimeout);
if (!nextJob) return;
const availableAt = nextJob.status === "processing" ? nextJobProcessingExpiresAt : nextJob.scheduledAt;
const deltaMs = availableAt.getTime() - now.getTime();
if (deltaMs <= 0) {
consumeNextJob({ job: nextJob });
return;
}
nextJobTimeout = setTimeout(() => {
refreshConsumption({
processingTimeoutMs,
getNow
});
}, deltaMs);
};
return {
getNextJobAndMarkAsProcessing: async ({ now = /* @__PURE__ */ new Date() }) => {
const { promise, resolve } = Promise.withResolvers();
pendingResolvers.push(resolve);
refreshConsumption({ processingTimeoutMs: 10 * 60 * 1e3 });
const { job } = await promise;
jobsRegistry.set(job.id, {
...job,
status: "processing",
startedAt: now
});
return { job };
},
saveJob: async ({ job }) => {
if (jobsRegistry.has(job.id)) throw createJobWithSameIdExistsError();
jobsRegistry.set(job.id, job);
refreshConsumption({ processingTimeoutMs: 10 * 60 * 1e3 });
},
getJob: async ({ jobId }) => {
const job = jobsRegistry.get(jobId);
if (!job) return { job: null };
return { job: {
result: void 0,
error: void 0,
startedAt: void 0,
completedAt: void 0,
cron: void 0,
...job
} };
},
getJobCount: async ({ filter } = {}) => {
if (!filter) return { count: jobsRegistry.size };
const filterEntries = Object.entries(filter);
const isJobMatchingFilter = (job) => filterEntries.every(([key, value]) => job[key] === value);
const filteredJobs = Array.from(jobsRegistry.values()).filter(isJobMatchingFilter);
return { count: filteredJobs.length };
},
updateJob: async ({ jobId, values }) => {
const existingJob = jobsRegistry.get(jobId);
if (!existingJob) throw createJobNotFoundError();
jobsRegistry.set(jobId, {
...existingJob,
...values
});
},
deleteJob: async ({ jobId }) => {
const existingJob = jobsRegistry.get(jobId);
if (!existingJob) throw createJobNotFoundError();
jobsRegistry.delete(jobId);
}
};
}
//#endregion
export { createMemoryDriver };
//# sourceMappingURL=index.js.map