import dbHandler from "../db-handler"; import type { BunMariaDBConfig, DBResponseObject } from "../../types"; import { quoteIdentifier } from "../../utils/sql-identifier"; import { QUEUE_NOW_SQL, requireQueueTable } from "./queue-utils"; type Params = { table: Table; id: number | string; /** `queue_lock_token` from the claimed row */ lock_token: string; error?: string; /** Wait this long before the job can be retried. Defaults to 0 */ retry_delay_ms?: number; /** Mark the job `failed` instead of retrying once it reaches this many attempts. Defaults to the table's `max_attempts` */ max_attempts?: number; config?: BunMariaDBConfig; }; /** * Release a claimed job after a failed attempt. It goes back to `pending` * (after `retry_delay_ms`) or to `failed` once `max_attempts` is reached. */ export default async function QueueFail
({ table, id, lock_token, error, retry_delay_ms = 0, max_attempts, config, }: Params
): Promise { try { const options = requireQueueTable(table); const tableSql = quoteIdentifier(table); const retryDelayMs = Math.max(0, Math.floor(Number(retry_delay_ms) || 0)); const maxAttempts = Math.max( 1, Math.floor(Number(max_attempts ?? options.max_attempts) || 1), ); const res = await dbHandler({ query: `UPDATE ${tableSql} SET queue_status = IF(queue_attempts >= ?, 'failed', 'pending'), queue_available_at = ${QUEUE_NOW_SQL} + ?, queue_lease_expires_at = NULL, queue_lock_token = NULL, queue_last_error = ?, updated_at = ${QUEUE_NOW_SQL} WHERE id = ? AND queue_status = 'processing' AND queue_lock_token = ?`, values: [maxAttempts, retryDelayMs, error ?? null, id, lock_token], config, }); if (!res.success) { return { ...res, success: false }; } if (!res.db_res?.affectedRows) { return { ...res, success: false, msg: "Job is not claimed with this lock token", }; } return await dbHandler({ query: `SELECT * FROM ${quoteIdentifier(table)} WHERE id = ?`, values: [id], config, }); } catch (error: any) { return { success: false, error: error.message, }; } }