@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 12.4 kB
Source Map (JSON)
{"version":3,"file":"execute-single-job.mjs","names":["constants"],"sources":["../../../src/libs/queue/execute-single-job.ts"],"sourcesContent":["import constants from \"../../constants/constants.js\";\nimport serviceWrapper from \"../../utils/services/service-wrapper.js\";\nimport type { ServiceContext } from \"../../utils/services/types.js\";\nimport { copy } from \"../i18n/index.js\";\nimport logger from \"../logger/index.js\";\nimport { QueueJobsRepository } from \"../repositories/index.js\";\nimport getJobHandler from \"./job-handlers.js\";\nimport type { QueueEvent, QueueJobHandler } from \"./types.js\";\n\nconst BACKOFF_MULTIPLIER = 2;\n\nconst getHandlerFn = (handler: QueueJobHandler) => {\n\tif (typeof handler === \"function\") return handler;\n\treturn handler.handler;\n};\n\nconst runPermanentFailureHook = async (params: {\n\thandler: QueueJobHandler;\n\tcontext: ServiceContext;\n\tjobId: string;\n\tevent: QueueEvent;\n\tpayload: Record<string, unknown>;\n\terrorMessage: string;\n}) => {\n\tif (typeof params.handler === \"function\") return;\n\tif (!params.handler.onPermanentFailure) return;\n\n\tawait params.handler.onPermanentFailure(params.context, {\n\t\tjobId: params.jobId,\n\t\tevent: params.event,\n\t\tpayload: params.payload,\n\t\terrorMessage: params.errorMessage,\n\t});\n};\n\n/**\n * Handles the retry logic for a failed job\n */\nconst handleRetryLogic = async (params: {\n\tjobId: string;\n\tevent: QueueEvent;\n\terrorMessage: string;\n\tattempts: number;\n\tmaxAttempts: number;\n\tsetNextRetryAt: boolean;\n\tQueueJobs: QueueJobsRepository;\n}): Promise<boolean> => {\n\tconst shouldRetry = params.attempts + 1 < params.maxAttempts;\n\n\tif (shouldRetry) {\n\t\tconst updateData: {\n\t\t\tstatus: \"pending\";\n\t\t\tupdated_at: string;\n\t\t\tnext_retry_at?: string;\n\t\t} = {\n\t\t\tstatus: \"pending\",\n\t\t\tupdated_at: new Date().toISOString(),\n\t\t};\n\n\t\tif (params.setNextRetryAt) {\n\t\t\tconst nextRetryAt = new Date(\n\t\t\t\tDate.now() + BACKOFF_MULTIPLIER ** params.attempts * 1000,\n\t\t\t);\n\t\t\tupdateData.next_retry_at = nextRetryAt.toISOString();\n\n\t\t\tlogger.debug({\n\t\t\t\tmessage: \"Job failed, will retry\",\n\t\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\t\tdata: {\n\t\t\t\t\tjobId: params.jobId,\n\t\t\t\t\teventType: params.event,\n\t\t\t\t\tattempts: params.attempts + 1,\n\t\t\t\t\tmaxAttempts: params.maxAttempts,\n\t\t\t\t\tnextRetryAt: nextRetryAt.toISOString(),\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tlogger.debug({\n\t\t\t\tmessage: \"Job failed, will retry\",\n\t\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\t\tdata: {\n\t\t\t\t\tjobId: params.jobId,\n\t\t\t\t\teventType: params.event,\n\t\t\t\t\tattempts: params.attempts + 1,\n\t\t\t\t\tmaxAttempts: params.maxAttempts,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tawait params.QueueJobs.updateSingle({\n\t\t\tdata: updateData,\n\t\t\twhere: [{ key: \"job_id\", operator: \"=\", value: params.jobId }],\n\t\t});\n\t} else {\n\t\tlogger.error({\n\t\t\tmessage: \"Job failed permanently\",\n\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\tdata: {\n\t\t\t\tjobId: params.jobId,\n\t\t\t\teventType: params.event,\n\t\t\t\terrorMessage: params.errorMessage,\n\t\t\t},\n\t\t});\n\n\t\tawait params.QueueJobs.updateSingle({\n\t\t\tdata: {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror_message: params.errorMessage,\n\t\t\t\tfailed_at: new Date().toISOString(),\n\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t},\n\t\t\twhere: [{ key: \"job_id\", operator: \"=\", value: params.jobId }],\n\t\t});\n\t}\n\n\treturn shouldRetry;\n};\n\n/**\n * Executes a single job and updates its status in the database.\n */\nconst executeSingleJob: (\n\tcontext: ServiceContext,\n\tdata: {\n\t\tjobId: string;\n\t\tevent: QueueEvent;\n\t\tpayload: Record<string, unknown>;\n\t\tattempts: number;\n\t\tmaxAttempts: number;\n\t\tsetNextRetryAt?: boolean;\n\t},\n) => Promise<{\n\tsuccess: boolean;\n\tshouldRetry: boolean;\n\tmessage: string;\n}> = async (context, data) => {\n\tconst handler = getJobHandler(data.event);\n\tconst QueueJobs = new QueueJobsRepository(\n\t\tcontext.db.client,\n\t\tcontext.config.db,\n\t);\n\tconst setNextRetryAt = data.setNextRetryAt ?? true;\n\n\tconst jobRes = await QueueJobs.selectSingle({\n\t\tselect: [\"status\"],\n\t\twhere: [{ key: \"job_id\", operator: \"=\", value: data.jobId }],\n\t\tvalidation: {\n\t\t\tenabled: true,\n\t\t\tdefaultError: {\n\t\t\t\tmessage: copy(\"server:core.queue.jobs.not.found\", {\n\t\t\t\t\tdefaultMessage: `Queue job not found: ${data.jobId}`,\n\t\t\t\t}),\n\t\t\t\tstatus: 404,\n\t\t\t},\n\t\t},\n\t});\n\tif (jobRes.error) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tshouldRetry: false,\n\t\t\tmessage:\n\t\t\t\tcontext.translate.english(jobRes.error.message) ??\n\t\t\t\t\"Queue job not found\",\n\t\t};\n\t}\n\n\tif (jobRes.data.status !== \"pending\") {\n\t\tlogger.debug({\n\t\t\tmessage: \"Skipping non-pending queue job\",\n\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\tdata: {\n\t\t\t\tjobId: data.jobId,\n\t\t\t\teventType: data.event,\n\t\t\t\tstatus: jobRes.data.status,\n\t\t\t},\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tshouldRetry: false,\n\t\t\tmessage: \"Queue job is no longer pending\",\n\t\t};\n\t}\n\n\tif (!handler) {\n\t\tconst errorMessage = `No job handler found for job type: ${data.event}`;\n\n\t\tlogger.warn({\n\t\t\tmessage: \"No job handler found for job type\",\n\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\tdata: { jobId: data.jobId, eventType: data.event },\n\t\t});\n\n\t\tawait QueueJobs.updateSingle({\n\t\t\tdata: {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror_message: errorMessage,\n\t\t\t\tfailed_at: new Date().toISOString(),\n\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t},\n\t\t\twhere: [{ key: \"job_id\", operator: \"=\", value: data.jobId }],\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tshouldRetry: false,\n\t\t\tmessage: errorMessage,\n\t\t};\n\t}\n\n\ttry {\n\t\tlogger.debug({\n\t\t\tmessage: \"Processing job\",\n\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\tdata: { jobId: data.jobId, eventType: data.event },\n\t\t});\n\n\t\tawait QueueJobs.updateSingle({\n\t\t\tdata: {\n\t\t\t\tstatus: \"processing\",\n\t\t\t\tattempts: data.attempts + 1,\n\t\t\t\tstarted_at: new Date().toISOString(),\n\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t},\n\t\t\twhere: [{ key: \"job_id\", operator: \"=\", value: data.jobId }],\n\t\t});\n\n\t\tconst handlerResult = await serviceWrapper(getHandlerFn(handler), {\n\t\t\ttransaction: false, //* jobs should handle cleanup themselves\n\t\t})(context, data.payload);\n\n\t\tif (handlerResult.error) {\n\t\t\tconst errorMessage =\n\t\t\t\tcontext.translate.english(handlerResult.error.message) ??\n\t\t\t\t\"Unknown error\";\n\t\t\tconst shouldRetry = await handleRetryLogic({\n\t\t\t\tjobId: data.jobId,\n\t\t\t\tevent: data.event,\n\t\t\t\terrorMessage,\n\t\t\t\tattempts: data.attempts,\n\t\t\t\tmaxAttempts: data.maxAttempts,\n\t\t\t\tsetNextRetryAt,\n\t\t\t\tQueueJobs,\n\t\t\t});\n\t\t\tif (!shouldRetry) {\n\t\t\t\tawait runPermanentFailureHook({\n\t\t\t\t\thandler,\n\t\t\t\t\tcontext,\n\t\t\t\t\tevent: data.event,\n\t\t\t\t\tjobId: data.jobId,\n\t\t\t\t\tpayload: data.payload,\n\t\t\t\t\terrorMessage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tshouldRetry,\n\t\t\t\tmessage: errorMessage,\n\t\t\t};\n\t\t}\n\n\t\tawait QueueJobs.updateSingle({\n\t\t\tdata: {\n\t\t\t\tstatus: \"completed\",\n\t\t\t\tcompleted_at: new Date().toISOString(),\n\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t},\n\t\t\twhere: [{ key: \"job_id\", operator: \"=\", value: data.jobId }],\n\t\t});\n\n\t\tlogger.debug({\n\t\t\tmessage: \"Job completed successfully\",\n\t\t\tscope: constants.logScopes.queueAdapter,\n\t\t\tdata: { jobId: data.jobId, eventType: data.event },\n\t\t});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tshouldRetry: false,\n\t\t\tmessage: \"Job completed successfully\",\n\t\t};\n\t} catch (error) {\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : \"Unknown error\";\n\n\t\tconst shouldRetry = await handleRetryLogic({\n\t\t\tjobId: data.jobId,\n\t\t\tevent: data.event,\n\t\t\terrorMessage,\n\t\t\tattempts: data.attempts,\n\t\t\tmaxAttempts: data.maxAttempts,\n\t\t\tsetNextRetryAt,\n\t\t\tQueueJobs,\n\t\t});\n\t\tif (!shouldRetry) {\n\t\t\tawait runPermanentFailureHook({\n\t\t\t\thandler,\n\t\t\t\tcontext,\n\t\t\t\tevent: data.event,\n\t\t\t\tjobId: data.jobId,\n\t\t\t\tpayload: data.payload,\n\t\t\t\terrorMessage,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\tshouldRetry,\n\t\t\tmessage: errorMessage,\n\t\t};\n\t}\n};\n\nexport default executeSingleJob;\n"],"mappings":"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"}