@sidequest/mysql-backend
Version:
@sidequest/mysql-backend is a MySQL backend for Sidequest, a distributed background job queue system.
162 lines (157 loc) • 7.25 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
var backend = require('@sidequest/backend');
var core = require('@sidequest/core');
var createKnex = require('knex');
var path = require('path');
var util = require('util');
const defaultKnexConfig = {
client: "mysql2",
migrations: {
directory: path.join(__dirname, "..", "migrations"),
tableName: "sidequest_migrations",
extension: "cjs",
},
};
class MysqlBackend extends backend.SQLBackend {
constructor(dbConfig) {
const knexConfig = {
...defaultKnexConfig,
...(typeof dbConfig === "string" ? { connection: dbConfig } : dbConfig),
};
const knex = createKnex(knexConfig);
super(knex);
}
async createNewQueue(queueConfig) {
if (queueConfig.concurrency !== undefined && queueConfig.concurrency < 1) {
throw new Error("Concurrency must be at least 1");
}
const data = {
...backend.QUEUE_FALLBACK,
...queueConfig,
};
core.logger("Backend").debug(`Inserting new queue config: ${util.inspect(data)}`);
const result = await this.knex.transaction(async (trx) => {
const [insertedId] = await trx("sidequest_queues").insert(data);
const inserted = await trx("sidequest_queues").where({ id: insertedId }).first();
if (!inserted)
throw new Error("Failed to insert queue config.");
return inserted;
});
core.logger("Backend").debug(`Queue inserted successfully: ${util.inspect(result)}`);
return result;
}
async updateQueue(queueData) {
if (queueData.concurrency !== undefined && queueData.concurrency < 1) {
throw new Error("Concurrency must be at least 1");
}
const { id, ...updates } = queueData;
core.logger("Backend").debug(`Updating queue: ${util.inspect(queueData)}`);
if (!id)
throw new Error("Queue id is required for update.");
const result = await this.knex.transaction(async (trx) => {
await trx("sidequest_queues").where({ id }).update(updates);
const updated = await trx("sidequest_queues").where({ id }).first();
if (!updated)
throw new Error("Cannot update queue, not found.");
return updated;
});
core.logger("Backend").debug(`Queue updated successfully: ${util.inspect(result)}`);
return result;
}
async createNewJob(job) {
const data = {
queue: job.queue,
script: job.script,
class: job.class,
args: JSON.stringify(job.args ?? backend.JOB_FALLBACK.args),
constructor_args: JSON.stringify(job.constructor_args ?? backend.JOB_FALLBACK.constructor_args),
state: job.state,
attempt: job.attempt,
max_attempts: job.max_attempts ?? backend.JOB_FALLBACK.max_attempts,
available_at: job.available_at ?? backend.JOB_FALLBACK.available_at,
timeout: job.timeout ?? backend.JOB_FALLBACK.timeout,
unique_digest: job.unique_digest ?? backend.JOB_FALLBACK.unique_digest,
uniqueness_config: job.uniqueness_config ? JSON.stringify(job.uniqueness_config) : backend.JOB_FALLBACK.uniqueness_config,
inserted_at: new Date(),
backoff_strategy: job.backoff_strategy ?? backend.JOB_FALLBACK.backoff_strategy,
retry_delay: job.retry_delay ?? backend.JOB_FALLBACK.retry_delay,
};
core.logger("Backend").debug(`Creating new job: ${util.inspect(data)}`);
try {
const insertedJob = await this.knex.transaction(async (trx) => {
const [insertedId] = await trx("sidequest_jobs").insert(data);
const inserted = await trx("sidequest_jobs").where({ id: insertedId }).first();
if (!inserted)
throw new Error("Failed to create job.");
core.logger("Backend").debug(`Job created successfully: ${util.inspect(inserted)}`);
return backend.safeParseJobData(inserted);
});
return insertedJob;
}
catch (error) {
if (error instanceof Error &&
(error.message?.includes("sidequest_jobs.unique_digest") ||
("constraint" in error && error.constraint === "sidequest_jobs_unique_digest_active_idx"))) {
throw new core.DuplicatedJobError(job);
}
throw error;
}
}
async updateJob(job) {
const data = {
...job,
args: job.args ? JSON.stringify(job.args) : job.args,
constructor_args: job.constructor_args ? JSON.stringify(job.constructor_args) : job.constructor_args,
result: job.result ? JSON.stringify(job.result) : job.result,
errors: job.errors ? JSON.stringify(job.errors) : job.errors,
uniqueness_config: job.uniqueness_config ? JSON.stringify(job.uniqueness_config) : job.uniqueness_config,
};
core.logger("Backend").debug(`Updating job: ${util.inspect(data)}`);
const updatedJob = await this.knex.transaction(async (trx) => {
await trx("sidequest_jobs").where({ id: job.id }).update(data);
const updated = await trx("sidequest_jobs").where({ id: job.id }).first();
if (!updated)
throw new Error("Cannot update job, not found.");
return backend.safeParseJobData(updated);
});
core.logger("Backend").debug(`Job updated successfully: ${util.inspect(updatedJob)}`);
return updatedJob;
}
async listJobs(params) {
const limit = params?.limit ?? 50;
const offset = params?.offset ?? 0;
const query = this.knex("sidequest_jobs").select("*").orderBy("id", "desc").limit(limit).offset(offset);
if (params) {
const { queue, jobClass, state, timeRange, args } = params;
backend.whereOrWhereIn(query, "queue", queue);
backend.whereOrWhereIn(query, "class", jobClass);
backend.whereOrWhereIn(query, "state", state);
if (args)
query.whereRaw("JSON_CONTAINS(args, ?)", [JSON.stringify(args)]);
if (timeRange?.from)
query.andWhere("attempted_at", ">=", timeRange.from);
if (timeRange?.to)
query.andWhere("attempted_at", "<=", timeRange.to);
}
const rawJobs = (await query);
return rawJobs.map(backend.safeParseJobData);
}
truncDate(date, unit) {
let format;
switch (unit) {
case "m":
format = "%Y-%m-%dT%H:%i:00.000"; // Truncate to minute
break;
case "h":
format = "%Y-%m-%dT%H:00:00.000"; // Truncate to hour
break;
case "d":
format = "%Y-%m-%dT00:00:00.000"; // Truncate to day
break;
}
return this.knex.raw(`DATE_FORMAT(${date}, ?)`, [format]).toQuery();
}
}
exports.default = MysqlBackend;
//# sourceMappingURL=mysql-backend.cjs.map
;