@cadence-mq/driver-libsql
Version:
LibSQL driver for Cadence MQ
121 lines (117 loc) • 3.42 kB
JavaScript
//#region src/driver.ts
async function getAndMarkJobAsProcessing({ client, processingTimeoutMs, now = /* @__PURE__ */ new Date() }) {
const transaction = await client.transaction("write");
try {
const expiredAt = new Date(now.getTime() - processingTimeoutMs);
const { rows } = await transaction.execute({
sql: "SELECT * FROM jobs WHERE status = 'pending' OR (status = 'processing' AND started_at < ?) ORDER BY schedule_at ASC LIMIT 1",
args: [expiredAt]
});
const [jobRow] = rows;
if (!jobRow) {
await transaction.rollback();
return { job: null };
}
await transaction.execute({
sql: "UPDATE jobs SET status = 'processing', started_at = ? WHERE id = ? AND status = 'pending'",
args: [now, jobRow.id]
});
await transaction.commit();
return { job: toJob(jobRow) };
} catch (error) {
await transaction.rollback();
throw error;
}
}
function toJob(row) {
return {
id: String(row.id),
taskName: String(row.task_name),
status: row.status,
startedAt: row.started_at ? new Date(String(row.started_at)) : void 0,
completedAt: row.completed_at ? new Date(String(row.completed_at)) : void 0,
maxRetries: Number(row.max_retries),
data: row.data ? JSON.parse(String(row.data)) : void 0,
result: row.result ? JSON.parse(String(row.result)) : void 0,
scheduleAt: new Date(String(row.schedule_at))
};
}
function createLibSqlDriver({ client, pollIntervalMs = 1e3 }) {
return {
fetchNextJob: async ({ processingTimeoutMs }) => {
while (true) {
const { job } = await getAndMarkJobAsProcessing({
client,
processingTimeoutMs
});
if (!job) {
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
continue;
}
return { job };
}
},
saveJob: async ({ job, now = /* @__PURE__ */ new Date() }) => {
await client.batch([{
sql: "INSERT INTO jobs (id, task_name, status, created_at, max_retries, data, schedule_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
args: [
job.id,
job.taskName,
job.status,
now,
job.maxRetries ?? null,
JSON.stringify(job.data),
job.scheduleAt
]
}], "write");
},
markJobAsCompleted: async ({ jobId, now = /* @__PURE__ */ new Date(), result }) => {
await client.batch([{
sql: "UPDATE jobs SET status = 'completed', completed_at = ?, result = ? WHERE id = ?",
args: [
now,
result ? JSON.stringify(result) : null,
jobId
]
}], "write");
},
markJobAsFailed: async ({ jobId, error }) => {
await client.batch([{
sql: "UPDATE jobs SET status = ?, error = ? WHERE id = ?",
args: [
"failed",
error,
jobId
]
}], "write");
}
};
}
//#endregion
//#region src/migrations.ts
function getSchema() {
return `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = 1;
PRAGMA busy_timeout = 5000;
CREATE TABLE IF NOT EXISTS jobs (
id TEXT PRIMARY KEY,
task_name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
created_at DATETIME NOT NULL,
started_at DATETIME,
completed_at DATETIME,
max_retries INTEGER,
data TEXT,
result TEXT,
schedule_at DATETIME NOT NULL
);
CREATE INDEX IF NOT EXISTS jobs_status_schedule_at_started_at_idx ON jobs (status, schedule_at, started_at);
`.trim();
}
async function setupSchema({ client }) {
await client.executeMultiple(getSchema());
}
//#endregion
export { createLibSqlDriver, getSchema, setupSchema };
//# sourceMappingURL=index.js.map