@boringnode/queue 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -6
- package/build/{chunk-WOUYSNK2.js → chunk-KI47AJ6U.js} +2 -2
- package/build/chunk-PZ5AY32C.js +10 -0
- package/build/chunk-PZ5AY32C.js.map +1 -0
- package/build/{chunk-ZZFSQY36.js → chunk-QEFYHCL7.js} +4 -6
- package/build/{chunk-ZZFSQY36.js.map → chunk-QEFYHCL7.js.map} +1 -1
- package/build/{chunk-OVYXMSSU.js → chunk-VRXHCWNK.js} +113 -31
- package/build/chunk-VRXHCWNK.js.map +1 -0
- package/build/chunk-WVLSICD4.js +20 -0
- package/build/chunk-WVLSICD4.js.map +1 -0
- package/build/index.d.ts +61 -10
- package/build/index.js +64 -51
- package/build/index.js.map +1 -1
- package/build/{index-B1XdqWpN.d.ts → job-Z5fBSzRX.d.ts} +160 -6
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +6 -1
- package/build/src/drivers/fake_adapter.js +4 -2
- package/build/src/drivers/knex_adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.js +2 -1
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +1 -1
- package/build/src/drivers/redis_adapter.js +92 -93
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +1 -1
- package/build/src/drivers/sync_adapter.js +38 -18
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/otel.d.ts +63 -0
- package/build/src/otel.js +245 -0
- package/build/src/otel.js.map +1 -0
- package/build/src/types/index.d.ts +6 -1
- package/build/src/types/main.d.ts +1 -1
- package/build/src/types/tracing_channels.d.ts +34 -0
- package/build/src/types/tracing_channels.js +1 -0
- package/build/src/types/tracing_channels.js.map +1 -0
- package/package.json +36 -14
- package/build/chunk-OVYXMSSU.js.map +0 -1
- /package/build/{chunk-WOUYSNK2.js.map → chunk-KI47AJ6U.js.map} +0 -0
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JobExecutionRuntime
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-KI47AJ6U.js";
|
|
4
4
|
import {
|
|
5
5
|
Locator,
|
|
6
6
|
QueueManager
|
|
7
|
-
} from "../../chunk-
|
|
7
|
+
} from "../../chunk-VRXHCWNK.js";
|
|
8
|
+
import {
|
|
9
|
+
executeChannel
|
|
10
|
+
} from "../../chunk-WVLSICD4.js";
|
|
8
11
|
import {
|
|
9
12
|
DEFAULT_PRIORITY
|
|
10
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-QEFYHCL7.js";
|
|
14
|
+
import "../../chunk-PZ5AY32C.js";
|
|
11
15
|
|
|
12
16
|
// src/drivers/sync_adapter.ts
|
|
13
17
|
import { setTimeout as sleep } from "timers/promises";
|
|
@@ -113,33 +117,49 @@ var SyncAdapter = class {
|
|
|
113
117
|
defaultTimeout: configResolver.getWorkerTimeout()
|
|
114
118
|
});
|
|
115
119
|
const jobFactory = QueueManager.getJobFactory();
|
|
120
|
+
const executionWrapper = QueueManager.getExecutionWrapper();
|
|
116
121
|
let attempts = jobData.attempts;
|
|
117
122
|
while (true) {
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
const acquiredJob = { ...jobData, attempts, acquiredAt: now };
|
|
118
125
|
const context = {
|
|
119
126
|
jobId: jobData.id,
|
|
120
127
|
name: jobData.name,
|
|
121
128
|
attempt: attempts + 1,
|
|
122
129
|
queue,
|
|
123
130
|
priority: jobData.priority ?? DEFAULT_PRIORITY,
|
|
124
|
-
acquiredAt:
|
|
131
|
+
acquiredAt: new Date(now),
|
|
125
132
|
stalledCount: jobData.stalledCount ?? 0
|
|
126
133
|
};
|
|
127
134
|
const jobInstance = jobFactory ? await jobFactory(JobClass) : new JobClass();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
const startTime = performance.now();
|
|
136
|
+
const executeMessage = { job: acquiredJob, queue };
|
|
137
|
+
const run = () => {
|
|
138
|
+
return executeChannel.tracePromise(async () => {
|
|
139
|
+
try {
|
|
140
|
+
await runtime.execute(jobInstance, jobData.payload, context);
|
|
141
|
+
executeMessage.status = "completed";
|
|
142
|
+
} catch (error) {
|
|
143
|
+
const outcome = runtime.resolveFailure(error, attempts);
|
|
144
|
+
executeMessage.error = error;
|
|
145
|
+
if (outcome.type === "failed") {
|
|
146
|
+
executeMessage.status = "failed";
|
|
147
|
+
await jobInstance.failed?.(outcome.hookError);
|
|
148
|
+
} else if (outcome.type === "retry") {
|
|
149
|
+
executeMessage.status = "retrying";
|
|
150
|
+
executeMessage.nextRetryAt = outcome.retryAt;
|
|
151
|
+
}
|
|
142
152
|
}
|
|
153
|
+
executeMessage.duration = Number((performance.now() - startTime).toFixed(2));
|
|
154
|
+
}, executeMessage);
|
|
155
|
+
};
|
|
156
|
+
await executionWrapper(run, acquiredJob, queue);
|
|
157
|
+
if (executeMessage.status !== "retrying") return;
|
|
158
|
+
attempts++;
|
|
159
|
+
if (executeMessage.nextRetryAt) {
|
|
160
|
+
const delay = executeMessage.nextRetryAt.getTime() - Date.now();
|
|
161
|
+
if (delay > 0) {
|
|
162
|
+
await sleep(delay);
|
|
143
163
|
}
|
|
144
164
|
}
|
|
145
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/drivers/sync_adapter.ts"],"sourcesContent":["import { setTimeout as sleep } from 'node:timers/promises'\nimport { Locator } from '../locator.js'\nimport { QueueManager } from '../queue_manager.js'\nimport { JobExecutionRuntime } from '../job_runtime.js'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type {\n JobContext,\n JobData,\n JobRetention,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\n\n/**\n * Create a sync adapter factory.\n */\nexport function sync() {\n return () => new SyncAdapter()\n}\n\n/**\n * Sync adapter executes jobs immediately when pushed.\n * Pop/complete/fail/retry are not supported as jobs are executed synchronously.\n */\nexport class SyncAdapter implements Adapter {\n setWorkerId(_workerId: string): void {}\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushOn(queue: string, jobData: JobData): Promise<void> {\n return this.#execute(jobData, queue)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n setTimeout(() => {\n void this.#execute(jobData, queue).catch((error) => {\n QueueManager.getLogger().error(\n { err: error, jobId: jobData.id, jobName: jobData.name, queue },\n 'Failed to execute delayed sync job'\n )\n })\n }, delay)\n\n return Promise.resolve()\n }\n\n pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n for (const job of jobs) {\n await this.pushOn(queue, job)\n }\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(_queue: string): Promise<number> {\n return Promise.resolve(0)\n }\n\n pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n popFrom(_queue: string): Promise<AcquiredJob | null> {\n throw new Error('SyncAdapter does not support pop - jobs are executed immediately on push')\n }\n\n completeJob(_jobId: string, _queue: string, _removeOnComplete?: JobRetention): Promise<void> {\n return Promise.resolve()\n }\n\n failJob(\n _jobId: string,\n _queue: string,\n _error?: Error,\n _removeOnFail?: JobRetention\n ): Promise<void> {\n return Promise.resolve()\n }\n\n retryJob(_jobId: string, _queue: string, _retryAt?: Date): Promise<void> {\n return Promise.resolve()\n }\n\n recoverStalledJobs(\n _queue: string,\n _stalledThreshold: number,\n _maxStalledCount: number\n ): Promise<number> {\n // SyncAdapter has no stalled jobs - jobs are executed immediately\n return Promise.resolve(0)\n }\n\n getJob(_jobId: string, _queue: string): Promise<null> {\n return Promise.resolve(null)\n }\n\n destroy(): Promise<void> {\n return Promise.resolve()\n }\n\n upsertSchedule(_config: ScheduleConfig): Promise<string> {\n // No-op: schedules don't make sense for sync adapter\n // Return a fake ID so code doesn't break in dev\n return Promise.resolve(`sync-schedule-${Date.now()}`)\n }\n\n /**\n * @deprecated Use `upsertSchedule` instead.\n */\n createSchedule(config: ScheduleConfig): Promise<string> {\n return this.upsertSchedule(config)\n }\n\n getSchedule(_id: string): Promise<ScheduleData | null> {\n return Promise.resolve(null)\n }\n\n listSchedules(_options?: ScheduleListOptions): Promise<ScheduleData[]> {\n return Promise.resolve([])\n }\n\n updateSchedule(\n _id: string,\n _updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n return Promise.resolve()\n }\n\n deleteSchedule(_id: string): Promise<void> {\n return Promise.resolve()\n }\n\n claimDueSchedule(): Promise<ScheduleData | null> {\n // SyncAdapter doesn't support scheduling\n return Promise.resolve(null)\n }\n\n async #execute(jobData: JobData, queue: string = 'default'): Promise<void> {\n const JobClass = Locator.get(jobData.name)\n\n if (!JobClass) {\n throw new Error(`Job class ${jobData.name} not found.`)\n }\n\n const options = JobClass.options || {}\n const configResolver = QueueManager.getConfigResolver()\n const runtime = JobExecutionRuntime.from({\n jobName: jobData.name,\n options,\n retryConfig: configResolver.resolveRetryConfig(queue, options),\n defaultTimeout: configResolver.getWorkerTimeout(),\n })\n const jobFactory = QueueManager.getJobFactory()\n let attempts = jobData.attempts\n\n while (true) {\n const context: JobContext = {\n jobId: jobData.id,\n name: jobData.name,\n attempt: attempts + 1,\n queue,\n priority: jobData.priority ?? DEFAULT_PRIORITY,\n acquiredAt: new Date(),\n stalledCount: jobData.stalledCount ?? 0,\n }\n\n const jobInstance = jobFactory ? await jobFactory(JobClass) : new JobClass()\n\n try {\n await runtime.execute(jobInstance, jobData.payload, context)\n return\n } catch (error) {\n const outcome = runtime.resolveFailure(error as Error, attempts)\n\n if (outcome.type === 'failed') {\n await jobInstance.failed?.(outcome.hookError)\n return\n }\n\n attempts++\n\n if (outcome.type === 'retry' && outcome.retryAt) {\n const delay = outcome.retryAt.getTime() - Date.now()\n\n if (delay > 0) {\n await sleep(delay)\n }\n }\n }\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/drivers/sync_adapter.ts"],"sourcesContent":["import { setTimeout as sleep } from 'node:timers/promises'\nimport { Locator } from '../locator.js'\nimport { QueueManager } from '../queue_manager.js'\nimport { JobExecutionRuntime } from '../job_runtime.js'\nimport { executeChannel } from '../tracing_channels.js'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type {\n JobContext,\n JobData,\n JobRetention,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport type { JobExecuteMessage } from '../types/tracing_channels.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\n\n/**\n * Create a sync adapter factory.\n */\nexport function sync() {\n return () => new SyncAdapter()\n}\n\n/**\n * Sync adapter executes jobs immediately when pushed.\n * Pop/complete/fail/retry are not supported as jobs are executed synchronously.\n */\nexport class SyncAdapter implements Adapter {\n setWorkerId(_workerId: string): void {}\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushOn(queue: string, jobData: JobData): Promise<void> {\n return this.#execute(jobData, queue)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n setTimeout(() => {\n void this.#execute(jobData, queue).catch((error) => {\n QueueManager.getLogger().error(\n { err: error, jobId: jobData.id, jobName: jobData.name, queue },\n 'Failed to execute delayed sync job'\n )\n })\n }, delay)\n\n return Promise.resolve()\n }\n\n pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n for (const job of jobs) {\n await this.pushOn(queue, job)\n }\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(_queue: string): Promise<number> {\n return Promise.resolve(0)\n }\n\n pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n popFrom(_queue: string): Promise<AcquiredJob | null> {\n throw new Error('SyncAdapter does not support pop - jobs are executed immediately on push')\n }\n\n completeJob(_jobId: string, _queue: string, _removeOnComplete?: JobRetention): Promise<void> {\n return Promise.resolve()\n }\n\n failJob(\n _jobId: string,\n _queue: string,\n _error?: Error,\n _removeOnFail?: JobRetention\n ): Promise<void> {\n return Promise.resolve()\n }\n\n retryJob(_jobId: string, _queue: string, _retryAt?: Date): Promise<void> {\n return Promise.resolve()\n }\n\n recoverStalledJobs(\n _queue: string,\n _stalledThreshold: number,\n _maxStalledCount: number\n ): Promise<number> {\n // SyncAdapter has no stalled jobs - jobs are executed immediately\n return Promise.resolve(0)\n }\n\n getJob(_jobId: string, _queue: string): Promise<null> {\n return Promise.resolve(null)\n }\n\n destroy(): Promise<void> {\n return Promise.resolve()\n }\n\n upsertSchedule(_config: ScheduleConfig): Promise<string> {\n // No-op: schedules don't make sense for sync adapter\n // Return a fake ID so code doesn't break in dev\n return Promise.resolve(`sync-schedule-${Date.now()}`)\n }\n\n /**\n * @deprecated Use `upsertSchedule` instead.\n */\n createSchedule(config: ScheduleConfig): Promise<string> {\n return this.upsertSchedule(config)\n }\n\n getSchedule(_id: string): Promise<ScheduleData | null> {\n return Promise.resolve(null)\n }\n\n listSchedules(_options?: ScheduleListOptions): Promise<ScheduleData[]> {\n return Promise.resolve([])\n }\n\n updateSchedule(\n _id: string,\n _updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n return Promise.resolve()\n }\n\n deleteSchedule(_id: string): Promise<void> {\n return Promise.resolve()\n }\n\n claimDueSchedule(): Promise<ScheduleData | null> {\n // SyncAdapter doesn't support scheduling\n return Promise.resolve(null)\n }\n\n async #execute(jobData: JobData, queue: string = 'default'): Promise<void> {\n const JobClass = Locator.get(jobData.name)\n\n if (!JobClass) {\n throw new Error(`Job class ${jobData.name} not found.`)\n }\n\n const options = JobClass.options || {}\n const configResolver = QueueManager.getConfigResolver()\n const runtime = JobExecutionRuntime.from({\n jobName: jobData.name,\n options,\n retryConfig: configResolver.resolveRetryConfig(queue, options),\n defaultTimeout: configResolver.getWorkerTimeout(),\n })\n const jobFactory = QueueManager.getJobFactory()\n const executionWrapper = QueueManager.getExecutionWrapper()\n let attempts = jobData.attempts\n\n while (true) {\n const now = Date.now()\n const acquiredJob: AcquiredJob = { ...jobData, attempts, acquiredAt: now }\n\n const context: JobContext = {\n jobId: jobData.id,\n name: jobData.name,\n attempt: attempts + 1,\n queue,\n priority: jobData.priority ?? DEFAULT_PRIORITY,\n acquiredAt: new Date(now),\n stalledCount: jobData.stalledCount ?? 0,\n }\n\n const jobInstance = jobFactory ? await jobFactory(JobClass) : new JobClass()\n\n const startTime = performance.now()\n const executeMessage: JobExecuteMessage = { job: acquiredJob, queue }\n\n const run = () => {\n return executeChannel.tracePromise(async () => {\n try {\n await runtime.execute(jobInstance, jobData.payload, context)\n executeMessage.status = 'completed'\n } catch (error) {\n const outcome = runtime.resolveFailure(error as Error, attempts)\n executeMessage.error = error as Error\n\n if (outcome.type === 'failed') {\n executeMessage.status = 'failed'\n await jobInstance.failed?.(outcome.hookError)\n } else if (outcome.type === 'retry') {\n executeMessage.status = 'retrying'\n executeMessage.nextRetryAt = outcome.retryAt\n }\n }\n\n executeMessage.duration = Number((performance.now() - startTime).toFixed(2))\n }, executeMessage)\n }\n\n await executionWrapper(run, acquiredJob, queue)\n\n if (executeMessage.status !== 'retrying') return\n\n attempts++\n\n if (executeMessage.nextRetryAt) {\n const delay = executeMessage.nextRetryAt.getTime() - Date.now()\n if (delay > 0) {\n await sleep(delay)\n }\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,cAAc,aAAa;AAoB7B,SAAS,OAAO;AACrB,SAAO,MAAM,IAAI,YAAY;AAC/B;AAMO,IAAM,cAAN,MAAqC;AAAA,EAC1C,YAAY,WAAyB;AAAA,EAAC;AAAA,EAEtC,KAAK,SAAiC;AACpC,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,OAAO,OAAe,SAAiC;AACrD,WAAO,KAAK,SAAS,SAAS,KAAK;AAAA,EACrC;AAAA,EAEA,UAAU,SAAkB,OAA8B;AACxD,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,YAAY,OAAe,SAAkB,OAA8B;AACzE,eAAW,MAAM;AACf,WAAK,KAAK,SAAS,SAAS,KAAK,EAAE,MAAM,CAAC,UAAU;AAClD,qBAAa,UAAU,EAAE;AAAA,UACvB,EAAE,KAAK,OAAO,OAAO,QAAQ,IAAI,SAAS,QAAQ,MAAM,MAAM;AAAA,UAC9D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,GAAG,KAAK;AAER,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,SAAS,MAAgC;AACvC,WAAO,KAAK,WAAW,WAAW,IAAI;AAAA,EACxC;AAAA,EAEA,MAAM,WAAW,OAAe,MAAgC;AAC9D,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,OAAO,OAAO,GAAG;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,OAAwB;AACtB,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,OAAO,QAAiC;AACtC,WAAO,QAAQ,QAAQ,CAAC;AAAA,EAC1B;AAAA,EAEA,MAAmC;AACjC,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,QAAQ,QAA6C;AACnD,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AAAA,EAEA,YAAY,QAAgB,QAAgB,mBAAiD;AAC3F,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,QACE,QACA,QACA,QACA,eACe;AACf,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,SAAS,QAAgB,QAAgB,UAAgC;AACvE,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,mBACE,QACA,mBACA,kBACiB;AAEjB,WAAO,QAAQ,QAAQ,CAAC;AAAA,EAC1B;AAAA,EAEA,OAAO,QAAgB,QAA+B;AACpD,WAAO,QAAQ,QAAQ,IAAI;AAAA,EAC7B;AAAA,EAEA,UAAyB;AACvB,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,eAAe,SAA0C;AAGvD,WAAO,QAAQ,QAAQ,iBAAiB,KAAK,IAAI,CAAC,EAAE;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,QAAyC;AACtD,WAAO,KAAK,eAAe,MAAM;AAAA,EACnC;AAAA,EAEA,YAAY,KAA2C;AACrD,WAAO,QAAQ,QAAQ,IAAI;AAAA,EAC7B;AAAA,EAEA,cAAc,UAAyD;AACrE,WAAO,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC3B;AAAA,EAEA,eACE,KACA,UACe;AACf,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,eAAe,KAA4B;AACzC,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,mBAAiD;AAE/C,WAAO,QAAQ,QAAQ,IAAI;AAAA,EAC7B;AAAA,EAEA,MAAM,SAAS,SAAkB,QAAgB,WAA0B;AACzE,UAAM,WAAW,QAAQ,IAAI,QAAQ,IAAI;AAEzC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,aAAa,QAAQ,IAAI,aAAa;AAAA,IACxD;AAEA,UAAM,UAAU,SAAS,WAAW,CAAC;AACrC,UAAM,iBAAiB,aAAa,kBAAkB;AACtD,UAAM,UAAU,oBAAoB,KAAK;AAAA,MACvC,SAAS,QAAQ;AAAA,MACjB;AAAA,MACA,aAAa,eAAe,mBAAmB,OAAO,OAAO;AAAA,MAC7D,gBAAgB,eAAe,iBAAiB;AAAA,IAClD,CAAC;AACD,UAAM,aAAa,aAAa,cAAc;AAC9C,UAAM,mBAAmB,aAAa,oBAAoB;AAC1D,QAAI,WAAW,QAAQ;AAEvB,WAAO,MAAM;AACX,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,cAA2B,EAAE,GAAG,SAAS,UAAU,YAAY,IAAI;AAEzE,YAAM,UAAsB;AAAA,QAC1B,OAAO,QAAQ;AAAA,QACf,MAAM,QAAQ;AAAA,QACd,SAAS,WAAW;AAAA,QACpB;AAAA,QACA,UAAU,QAAQ,YAAY;AAAA,QAC9B,YAAY,IAAI,KAAK,GAAG;AAAA,QACxB,cAAc,QAAQ,gBAAgB;AAAA,MACxC;AAEA,YAAM,cAAc,aAAa,MAAM,WAAW,QAAQ,IAAI,IAAI,SAAS;AAE3E,YAAM,YAAY,YAAY,IAAI;AAClC,YAAM,iBAAoC,EAAE,KAAK,aAAa,MAAM;AAEpE,YAAM,MAAM,MAAM;AAChB,eAAO,eAAe,aAAa,YAAY;AAC7C,cAAI;AACF,kBAAM,QAAQ,QAAQ,aAAa,QAAQ,SAAS,OAAO;AAC3D,2BAAe,SAAS;AAAA,UAC1B,SAAS,OAAO;AACd,kBAAM,UAAU,QAAQ,eAAe,OAAgB,QAAQ;AAC/D,2BAAe,QAAQ;AAEvB,gBAAI,QAAQ,SAAS,UAAU;AAC7B,6BAAe,SAAS;AACxB,oBAAM,YAAY,SAAS,QAAQ,SAAS;AAAA,YAC9C,WAAW,QAAQ,SAAS,SAAS;AACnC,6BAAe,SAAS;AACxB,6BAAe,cAAc,QAAQ;AAAA,YACvC;AAAA,UACF;AAEA,yBAAe,WAAW,QAAQ,YAAY,IAAI,IAAI,WAAW,QAAQ,CAAC,CAAC;AAAA,QAC7E,GAAG,cAAc;AAAA,MACnB;AAEA,YAAM,iBAAiB,KAAK,aAAa,KAAK;AAE9C,UAAI,eAAe,WAAW,WAAY;AAE1C;AAEA,UAAI,eAAe,aAAa;AAC9B,cAAM,QAAQ,eAAe,YAAY,QAAQ,IAAI,KAAK,IAAI;AAC9D,YAAI,QAAQ,GAAG;AACb,gBAAM,MAAM,KAAK;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import '../job-Z5fBSzRX.js';
|
|
2
|
+
import { JobDispatchMessage, JobExecuteMessage } from './types/tracing_channels.js';
|
|
3
|
+
import { Span } from '@opentelemetry/api';
|
|
4
|
+
import { TracingChannelSubscribers } from 'node:diagnostics_channel';
|
|
5
|
+
import { InstrumentationConfig, InstrumentationBase } from '@opentelemetry/instrumentation';
|
|
6
|
+
|
|
7
|
+
interface QueueInstrumentationConfig extends InstrumentationConfig {
|
|
8
|
+
/**
|
|
9
|
+
* How execution spans relate to the dispatch span.
|
|
10
|
+
*
|
|
11
|
+
* - `'link'` (default): Independent trace, linked to dispatch span
|
|
12
|
+
* - `'parent'`: Child of the dispatch span (same trace)
|
|
13
|
+
*/
|
|
14
|
+
executionSpanLinkMode?: 'link' | 'parent';
|
|
15
|
+
/**
|
|
16
|
+
* The messaging system identifier.
|
|
17
|
+
*
|
|
18
|
+
* @default 'boringqueue'
|
|
19
|
+
*/
|
|
20
|
+
messagingSystem?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* OpenTelemetry instrumentation for @boringnode/queue.
|
|
24
|
+
*
|
|
25
|
+
* Creates PRODUCER spans for job dispatch and CONSUMER spans for
|
|
26
|
+
* job execution, following OTel messaging semantic conventions.
|
|
27
|
+
*
|
|
28
|
+
* Uses `diagnostics_channel` for span lifecycle management and
|
|
29
|
+
* patches `QueueManager.init()` to inject wrappers automatically.
|
|
30
|
+
*/
|
|
31
|
+
declare class QueueInstrumentation extends InstrumentationBase<QueueInstrumentationConfig> {
|
|
32
|
+
#private;
|
|
33
|
+
protected subscribed: boolean;
|
|
34
|
+
protected executeSpans: Map<string, Span>;
|
|
35
|
+
protected dispatchSpans: WeakMap<JobDispatchMessage, Span>;
|
|
36
|
+
protected executeHandlers?: TracingChannelSubscribers<JobExecuteMessage>;
|
|
37
|
+
protected dispatchHandlers?: TracingChannelSubscribers<JobDispatchMessage>;
|
|
38
|
+
constructor(config?: QueueInstrumentationConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Required by InstrumentationBase. Returns undefined since we use
|
|
41
|
+
* diagnostics_channel instead of module patching.
|
|
42
|
+
*/
|
|
43
|
+
protected init(): undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Subscribes to diagnostics_channels for span lifecycle.
|
|
46
|
+
*/
|
|
47
|
+
enable(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Unsubscribes from diagnostics_channels and restores patched methods.
|
|
50
|
+
*/
|
|
51
|
+
disable(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Patches `QueueManager.init()` to auto-inject OTel wrappers
|
|
54
|
+
* and subscribes to diagnostics_channels.
|
|
55
|
+
*/
|
|
56
|
+
manuallyRegister(queueModule: {
|
|
57
|
+
QueueManager: {
|
|
58
|
+
init: (...args: any[]) => any;
|
|
59
|
+
};
|
|
60
|
+
}): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { QueueInstrumentation, type QueueInstrumentationConfig };
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import {
|
|
2
|
+
dispatchChannel,
|
|
3
|
+
executeChannel
|
|
4
|
+
} from "../chunk-WVLSICD4.js";
|
|
5
|
+
import "../chunk-PZ5AY32C.js";
|
|
6
|
+
|
|
7
|
+
// src/otel.ts
|
|
8
|
+
import {
|
|
9
|
+
context,
|
|
10
|
+
propagation,
|
|
11
|
+
trace,
|
|
12
|
+
SpanKind,
|
|
13
|
+
SpanStatusCode,
|
|
14
|
+
ROOT_CONTEXT
|
|
15
|
+
} from "@opentelemetry/api";
|
|
16
|
+
import { suppressTracing } from "@opentelemetry/core";
|
|
17
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
18
|
+
var QueueInstrumentation = class extends InstrumentationBase {
|
|
19
|
+
subscribed = false;
|
|
20
|
+
executeSpans = /* @__PURE__ */ new Map();
|
|
21
|
+
dispatchSpans = /* @__PURE__ */ new WeakMap();
|
|
22
|
+
executeHandlers;
|
|
23
|
+
dispatchHandlers;
|
|
24
|
+
#originalInit;
|
|
25
|
+
#patchedManager;
|
|
26
|
+
constructor(config = {}) {
|
|
27
|
+
super("@boringnode/queue", "0.1.0", config);
|
|
28
|
+
}
|
|
29
|
+
get #messagingSystem() {
|
|
30
|
+
return this.getConfig().messagingSystem ?? "boringqueue";
|
|
31
|
+
}
|
|
32
|
+
get #executionSpanLinkMode() {
|
|
33
|
+
return this.getConfig().executionSpanLinkMode ?? "link";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Required by InstrumentationBase. Returns undefined since we use
|
|
37
|
+
* diagnostics_channel instead of module patching.
|
|
38
|
+
*/
|
|
39
|
+
init() {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Subscribes to diagnostics_channels for span lifecycle.
|
|
44
|
+
*/
|
|
45
|
+
enable() {
|
|
46
|
+
super.enable();
|
|
47
|
+
if (this.subscribed !== void 0) this.#subscribe();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Unsubscribes from diagnostics_channels and restores patched methods.
|
|
51
|
+
*/
|
|
52
|
+
disable() {
|
|
53
|
+
if (this.subscribed !== void 0) {
|
|
54
|
+
this.#unsubscribe();
|
|
55
|
+
this.#unpatchInit();
|
|
56
|
+
}
|
|
57
|
+
super.disable();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Patches `QueueManager.init()` to auto-inject OTel wrappers
|
|
61
|
+
* and subscribes to diagnostics_channels.
|
|
62
|
+
*/
|
|
63
|
+
manuallyRegister(queueModule) {
|
|
64
|
+
this.#patchInit(queueModule.QueueManager);
|
|
65
|
+
this.#subscribe();
|
|
66
|
+
}
|
|
67
|
+
#patchInit(manager) {
|
|
68
|
+
if (this.#originalInit) return;
|
|
69
|
+
this.#patchedManager = manager;
|
|
70
|
+
this.#originalInit = manager.init.bind(manager);
|
|
71
|
+
const instrumentation = this;
|
|
72
|
+
manager.init = async (config) => {
|
|
73
|
+
return this.#originalInit({
|
|
74
|
+
...config,
|
|
75
|
+
internalOperationWrapper: (fn) => {
|
|
76
|
+
return context.with(suppressTracing(context.active()), fn);
|
|
77
|
+
},
|
|
78
|
+
executionWrapper: (fn, job, queue) => {
|
|
79
|
+
return instrumentation.#wrapExecution(fn, job, queue);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
#unpatchInit() {
|
|
85
|
+
if (!this.#originalInit || !this.#patchedManager) return;
|
|
86
|
+
this.#patchedManager.init = this.#originalInit;
|
|
87
|
+
this.#originalInit = void 0;
|
|
88
|
+
this.#patchedManager = void 0;
|
|
89
|
+
}
|
|
90
|
+
#subscribe() {
|
|
91
|
+
if (this.subscribed) return;
|
|
92
|
+
if (!this.isEnabled()) return;
|
|
93
|
+
this.subscribed = true;
|
|
94
|
+
this.executeHandlers = {
|
|
95
|
+
start: () => {
|
|
96
|
+
},
|
|
97
|
+
end: () => {
|
|
98
|
+
},
|
|
99
|
+
asyncStart: () => {
|
|
100
|
+
},
|
|
101
|
+
asyncEnd: (msg) => this.#handleExecuteAsyncEnd(msg),
|
|
102
|
+
error: () => {
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
this.dispatchHandlers = {
|
|
106
|
+
start: (msg) => this.#handleDispatchStart(msg),
|
|
107
|
+
end: () => {
|
|
108
|
+
},
|
|
109
|
+
asyncStart: () => {
|
|
110
|
+
},
|
|
111
|
+
asyncEnd: (msg) => this.#handleDispatchAsyncEnd(msg),
|
|
112
|
+
error: () => {
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
executeChannel.subscribe(this.executeHandlers);
|
|
116
|
+
dispatchChannel.subscribe(this.dispatchHandlers);
|
|
117
|
+
}
|
|
118
|
+
#unsubscribe() {
|
|
119
|
+
if (!this.subscribed) return;
|
|
120
|
+
if (this.executeHandlers) executeChannel.unsubscribe(this.executeHandlers);
|
|
121
|
+
if (this.dispatchHandlers) dispatchChannel.unsubscribe(this.dispatchHandlers);
|
|
122
|
+
this.subscribed = false;
|
|
123
|
+
this.executeHandlers = void 0;
|
|
124
|
+
this.dispatchHandlers = void 0;
|
|
125
|
+
this.executeSpans.clear();
|
|
126
|
+
this.dispatchSpans = /* @__PURE__ */ new WeakMap();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Called on dispatchChannel `start` — injects trace context into jobData
|
|
130
|
+
* and creates/enriches a PRODUCER span.
|
|
131
|
+
*/
|
|
132
|
+
#handleDispatchStart(message) {
|
|
133
|
+
const attributes = this.#buildDispatchAttributes(message);
|
|
134
|
+
const span = this.tracer.startSpan(`publish ${message.queue}`, {
|
|
135
|
+
kind: SpanKind.PRODUCER,
|
|
136
|
+
attributes
|
|
137
|
+
});
|
|
138
|
+
const dispatchContext = trace.setSpan(context.active(), span);
|
|
139
|
+
for (const job of message.jobs) {
|
|
140
|
+
if (!job.traceContext) job.traceContext = {};
|
|
141
|
+
propagation.inject(dispatchContext, job.traceContext);
|
|
142
|
+
}
|
|
143
|
+
this.dispatchSpans.set(message, span);
|
|
144
|
+
}
|
|
145
|
+
#handleDispatchAsyncEnd(message) {
|
|
146
|
+
const span = this.dispatchSpans.get(message);
|
|
147
|
+
if (!span) return;
|
|
148
|
+
if (message.error) {
|
|
149
|
+
span.recordException(message.error);
|
|
150
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: message.error.message });
|
|
151
|
+
}
|
|
152
|
+
span.end();
|
|
153
|
+
this.dispatchSpans.delete(message);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Called by `executionWrapper` config — creates CONSUMER span and wraps
|
|
157
|
+
* execution in OTel context for proper child span parenting.
|
|
158
|
+
*/
|
|
159
|
+
#wrapExecution(fn, job, queue) {
|
|
160
|
+
const extractedContext = this.#extractParentContext(job.traceContext);
|
|
161
|
+
const parentSpanContext = trace.getSpanContext(extractedContext);
|
|
162
|
+
let baseContext;
|
|
163
|
+
let links;
|
|
164
|
+
if (this.#executionSpanLinkMode === "parent" && parentSpanContext) {
|
|
165
|
+
baseContext = extractedContext;
|
|
166
|
+
links = [];
|
|
167
|
+
} else {
|
|
168
|
+
links = parentSpanContext ? [{ context: parentSpanContext }] : [];
|
|
169
|
+
baseContext = ROOT_CONTEXT;
|
|
170
|
+
}
|
|
171
|
+
const span = this.tracer.startSpan(
|
|
172
|
+
`process ${queue}`,
|
|
173
|
+
{
|
|
174
|
+
kind: SpanKind.CONSUMER,
|
|
175
|
+
attributes: this.#buildExecuteAttributes(job, queue),
|
|
176
|
+
links
|
|
177
|
+
},
|
|
178
|
+
baseContext
|
|
179
|
+
);
|
|
180
|
+
if (job.createdAt) {
|
|
181
|
+
span.setAttribute("messaging.job.queue_time_ms", Date.now() - job.createdAt);
|
|
182
|
+
}
|
|
183
|
+
this.executeSpans.set(job.id, span);
|
|
184
|
+
const executionContext = trace.setSpan(baseContext, span);
|
|
185
|
+
return context.with(executionContext, fn);
|
|
186
|
+
}
|
|
187
|
+
#handleExecuteAsyncEnd(message) {
|
|
188
|
+
const span = this.executeSpans.get(message.job.id);
|
|
189
|
+
if (!span) return;
|
|
190
|
+
if (message.status) span.setAttribute("messaging.job.status", message.status);
|
|
191
|
+
if (message.error) span.recordException(message.error);
|
|
192
|
+
if (message.status === "retrying" && message.nextRetryAt) {
|
|
193
|
+
span.addEvent("messaging.retry", {
|
|
194
|
+
"messaging.message.retry.count": message.job.attempts + 1,
|
|
195
|
+
"messaging.job.retry_at": message.nextRetryAt.toISOString()
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (message.status === "failed") {
|
|
199
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: message.error?.message });
|
|
200
|
+
} else {
|
|
201
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
202
|
+
}
|
|
203
|
+
span.end();
|
|
204
|
+
this.executeSpans.delete(message.job.id);
|
|
205
|
+
}
|
|
206
|
+
#extractParentContext(traceContext) {
|
|
207
|
+
if (!traceContext || Object.keys(traceContext).length === 0) return ROOT_CONTEXT;
|
|
208
|
+
return propagation.extract(ROOT_CONTEXT, traceContext);
|
|
209
|
+
}
|
|
210
|
+
#buildDispatchAttributes(message) {
|
|
211
|
+
const firstJob = message.jobs[0];
|
|
212
|
+
const attributes = {
|
|
213
|
+
"messaging.system": this.#messagingSystem,
|
|
214
|
+
"messaging.operation.name": "publish",
|
|
215
|
+
"messaging.operation.type": "send",
|
|
216
|
+
"messaging.destination.name": message.queue,
|
|
217
|
+
"messaging.job.name": firstJob.name
|
|
218
|
+
};
|
|
219
|
+
if (message.jobs.length === 1) attributes["messaging.message.id"] = firstJob.id;
|
|
220
|
+
if (message.jobs.length > 1) attributes["messaging.batch.message_count"] = message.jobs.length;
|
|
221
|
+
if (firstJob.groupId) attributes["messaging.job.group_id"] = firstJob.groupId;
|
|
222
|
+
if (firstJob.priority !== void 0) attributes["messaging.job.priority"] = firstJob.priority;
|
|
223
|
+
if (message.delay !== void 0) attributes["messaging.job.delay_ms"] = message.delay;
|
|
224
|
+
return attributes;
|
|
225
|
+
}
|
|
226
|
+
#buildExecuteAttributes(job, queue) {
|
|
227
|
+
const attributes = {
|
|
228
|
+
"entry_point.type": "job",
|
|
229
|
+
"messaging.system": this.#messagingSystem,
|
|
230
|
+
"messaging.operation.name": "process",
|
|
231
|
+
"messaging.operation.type": "process",
|
|
232
|
+
"messaging.destination.name": queue,
|
|
233
|
+
"messaging.message.id": job.id,
|
|
234
|
+
"messaging.message.retry.count": job.attempts,
|
|
235
|
+
"messaging.job.name": job.name
|
|
236
|
+
};
|
|
237
|
+
if (job.groupId) attributes["messaging.job.group_id"] = job.groupId;
|
|
238
|
+
if (job.priority !== void 0) attributes["messaging.job.priority"] = job.priority;
|
|
239
|
+
return attributes;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
export {
|
|
243
|
+
QueueInstrumentation
|
|
244
|
+
};
|
|
245
|
+
//# sourceMappingURL=otel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/otel.ts"],"sourcesContent":["import {\n context,\n propagation,\n trace,\n SpanKind,\n SpanStatusCode,\n ROOT_CONTEXT,\n type Span,\n type Link,\n} from '@opentelemetry/api'\nimport type { TracingChannelSubscribers } from 'node:diagnostics_channel'\nimport { suppressTracing } from '@opentelemetry/core'\nimport { InstrumentationBase } from '@opentelemetry/instrumentation'\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation'\nimport { dispatchChannel, executeChannel } from './tracing_channels.js'\nimport type { AcquiredJob } from './contracts/adapter.js'\nimport type { JobDispatchMessage, JobExecuteMessage } from './types/tracing_channels.js'\n\nexport interface QueueInstrumentationConfig extends InstrumentationConfig {\n /**\n * How execution spans relate to the dispatch span.\n *\n * - `'link'` (default): Independent trace, linked to dispatch span\n * - `'parent'`: Child of the dispatch span (same trace)\n */\n executionSpanLinkMode?: 'link' | 'parent'\n\n /**\n * The messaging system identifier.\n *\n * @default 'boringqueue'\n */\n messagingSystem?: string\n}\n\n/**\n * OpenTelemetry instrumentation for @boringnode/queue.\n *\n * Creates PRODUCER spans for job dispatch and CONSUMER spans for\n * job execution, following OTel messaging semantic conventions.\n *\n * Uses `diagnostics_channel` for span lifecycle management and\n * patches `QueueManager.init()` to inject wrappers automatically.\n */\nexport class QueueInstrumentation extends InstrumentationBase<QueueInstrumentationConfig> {\n protected subscribed = false\n protected executeSpans = new Map<string, Span>()\n protected dispatchSpans = new WeakMap<JobDispatchMessage, Span>()\n protected executeHandlers?: TracingChannelSubscribers<JobExecuteMessage>\n protected dispatchHandlers?: TracingChannelSubscribers<JobDispatchMessage>\n\n #originalInit?: (...args: any[]) => any\n #patchedManager?: { init: (...args: any[]) => any }\n\n constructor(config: QueueInstrumentationConfig = {}) {\n super('@boringnode/queue', '0.1.0', config)\n }\n\n get #messagingSystem(): string {\n return this.getConfig().messagingSystem ?? 'boringqueue'\n }\n\n get #executionSpanLinkMode(): 'link' | 'parent' {\n return this.getConfig().executionSpanLinkMode ?? 'link'\n }\n\n /**\n * Required by InstrumentationBase. Returns undefined since we use\n * diagnostics_channel instead of module patching.\n */\n protected init() {\n return undefined\n }\n\n /**\n * Subscribes to diagnostics_channels for span lifecycle.\n */\n enable() {\n super.enable()\n if (this.subscribed !== undefined) this.#subscribe()\n }\n\n /**\n * Unsubscribes from diagnostics_channels and restores patched methods.\n */\n disable() {\n if (this.subscribed !== undefined) {\n this.#unsubscribe()\n this.#unpatchInit()\n }\n\n super.disable()\n }\n\n /**\n * Patches `QueueManager.init()` to auto-inject OTel wrappers\n * and subscribes to diagnostics_channels.\n */\n manuallyRegister(queueModule: { QueueManager: { init: (...args: any[]) => any } }) {\n this.#patchInit(queueModule.QueueManager)\n this.#subscribe()\n }\n\n #patchInit(manager: { init: (...args: any[]) => any }) {\n if (this.#originalInit) return\n\n this.#patchedManager = manager\n this.#originalInit = manager.init.bind(manager)\n const instrumentation = this\n\n manager.init = async (config: any) => {\n return this.#originalInit!({\n ...config,\n internalOperationWrapper: <T>(fn: () => Promise<T>) => {\n return context.with(suppressTracing(context.active()), fn)\n },\n executionWrapper: <T>(fn: () => Promise<T>, job: AcquiredJob, queue: string) => {\n return instrumentation.#wrapExecution(fn, job, queue)\n },\n })\n }\n }\n\n #unpatchInit() {\n if (!this.#originalInit || !this.#patchedManager) return\n\n this.#patchedManager.init = this.#originalInit\n this.#originalInit = undefined\n this.#patchedManager = undefined\n }\n\n #subscribe() {\n if (this.subscribed) return\n if (!this.isEnabled()) return\n\n this.subscribed = true\n\n this.executeHandlers = {\n start: () => {},\n end: () => {},\n asyncStart: () => {},\n asyncEnd: (msg) => this.#handleExecuteAsyncEnd(msg as unknown as JobExecuteMessage),\n error: () => {},\n }\n\n this.dispatchHandlers = {\n start: (msg) => this.#handleDispatchStart(msg as unknown as JobDispatchMessage),\n end: () => {},\n asyncStart: () => {},\n asyncEnd: (msg) => this.#handleDispatchAsyncEnd(msg as unknown as JobDispatchMessage),\n error: () => {},\n }\n\n executeChannel.subscribe(this.executeHandlers as any)\n dispatchChannel.subscribe(this.dispatchHandlers as any)\n }\n\n #unsubscribe() {\n if (!this.subscribed) return\n\n if (this.executeHandlers) executeChannel.unsubscribe(this.executeHandlers as any)\n if (this.dispatchHandlers) dispatchChannel.unsubscribe(this.dispatchHandlers as any)\n\n this.subscribed = false\n this.executeHandlers = undefined\n this.dispatchHandlers = undefined\n this.executeSpans.clear()\n this.dispatchSpans = new WeakMap()\n }\n\n /**\n * Called on dispatchChannel `start` — injects trace context into jobData\n * and creates/enriches a PRODUCER span.\n */\n #handleDispatchStart(message: JobDispatchMessage) {\n const attributes = this.#buildDispatchAttributes(message)\n const span = this.tracer.startSpan(`publish ${message.queue}`, {\n kind: SpanKind.PRODUCER,\n attributes,\n })\n\n const dispatchContext = trace.setSpan(context.active(), span)\n for (const job of message.jobs) {\n if (!job.traceContext) job.traceContext = {}\n propagation.inject(dispatchContext, job.traceContext)\n }\n\n this.dispatchSpans.set(message, span)\n }\n\n #handleDispatchAsyncEnd(message: JobDispatchMessage) {\n const span = this.dispatchSpans.get(message)\n if (!span) return\n\n if (message.error) {\n span.recordException(message.error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: message.error.message })\n }\n\n span.end()\n this.dispatchSpans.delete(message)\n }\n\n /**\n * Called by `executionWrapper` config — creates CONSUMER span and wraps\n * execution in OTel context for proper child span parenting.\n */\n #wrapExecution<T>(fn: () => Promise<T>, job: AcquiredJob, queue: string): Promise<T> {\n const extractedContext = this.#extractParentContext(job.traceContext)\n const parentSpanContext = trace.getSpanContext(extractedContext)\n\n let baseContext: typeof extractedContext\n let links: Link[]\n\n if (this.#executionSpanLinkMode === 'parent' && parentSpanContext) {\n baseContext = extractedContext\n links = []\n } else {\n links = parentSpanContext ? [{ context: parentSpanContext }] : []\n baseContext = ROOT_CONTEXT\n }\n\n const span = this.tracer.startSpan(\n `process ${queue}`,\n {\n kind: SpanKind.CONSUMER,\n attributes: this.#buildExecuteAttributes(job, queue),\n links,\n },\n baseContext\n )\n\n if (job.createdAt) {\n span.setAttribute('messaging.job.queue_time_ms', Date.now() - job.createdAt)\n }\n\n this.executeSpans.set(job.id, span)\n const executionContext = trace.setSpan(baseContext, span)\n\n return context.with(executionContext, fn)\n }\n\n #handleExecuteAsyncEnd(message: JobExecuteMessage) {\n const span = this.executeSpans.get(message.job.id)\n if (!span) return\n\n if (message.status) span.setAttribute('messaging.job.status', message.status)\n if (message.error) span.recordException(message.error)\n\n if (message.status === 'retrying' && message.nextRetryAt) {\n span.addEvent('messaging.retry', {\n 'messaging.message.retry.count': message.job.attempts + 1,\n 'messaging.job.retry_at': message.nextRetryAt.toISOString(),\n })\n }\n\n if (message.status === 'failed') {\n span.setStatus({ code: SpanStatusCode.ERROR, message: message.error?.message })\n } else {\n span.setStatus({ code: SpanStatusCode.OK })\n }\n\n span.end()\n this.executeSpans.delete(message.job.id)\n }\n\n #extractParentContext(traceContext?: Record<string, string>) {\n if (!traceContext || Object.keys(traceContext).length === 0) return ROOT_CONTEXT\n return propagation.extract(ROOT_CONTEXT, traceContext)\n }\n\n #buildDispatchAttributes(message: JobDispatchMessage) {\n const firstJob = message.jobs[0]\n const attributes: Record<string, string | number | boolean> = {\n 'messaging.system': this.#messagingSystem,\n 'messaging.operation.name': 'publish',\n 'messaging.operation.type': 'send',\n 'messaging.destination.name': message.queue,\n 'messaging.job.name': firstJob.name,\n }\n\n if (message.jobs.length === 1) attributes['messaging.message.id'] = firstJob.id\n if (message.jobs.length > 1) attributes['messaging.batch.message_count'] = message.jobs.length\n if (firstJob.groupId) attributes['messaging.job.group_id'] = firstJob.groupId\n if (firstJob.priority !== undefined) attributes['messaging.job.priority'] = firstJob.priority\n if (message.delay !== undefined) attributes['messaging.job.delay_ms'] = message.delay\n\n return attributes\n }\n\n #buildExecuteAttributes(job: AcquiredJob, queue: string) {\n const attributes: Record<string, string | number | boolean> = {\n 'entry_point.type': 'job',\n 'messaging.system': this.#messagingSystem,\n 'messaging.operation.name': 'process',\n 'messaging.operation.type': 'process',\n 'messaging.destination.name': queue,\n 'messaging.message.id': job.id,\n 'messaging.message.retry.count': job.attempts,\n 'messaging.job.name': job.name,\n }\n\n if (job.groupId) attributes['messaging.job.group_id'] = job.groupId\n if (job.priority !== undefined) attributes['messaging.job.priority'] = job.priority\n\n return attributes\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
export { b as AcquiredJob, A as Adapter,
|
|
1
|
+
export { b as AcquiredJob, A as Adapter, r as AdapterFactory, B as BackoffConfig, s as BackoffStrategy, t as DispatchManyResult, u as DispatchResult, D as Duration, a as JobClass, v as JobContext, J as JobData, i as JobFactory, g as JobOptions, d as JobRecord, c as JobRetention, w as JobStatus, L as Logger, h as QueueConfig, Q as QueueManagerConfig, R as RetryConfig, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions, x as ScheduleResult, k as ScheduleStatus, y as WorkerConfig, W as WorkerCycle } from '../../job-Z5fBSzRX.js';
|
|
2
|
+
export { JobDispatchMessage, JobExecuteMessage } from './tracing_channels.js';
|
|
3
|
+
export { QueueInstrumentationConfig } from '../otel.js';
|
|
4
|
+
import '@opentelemetry/api';
|
|
5
|
+
import 'node:diagnostics_channel';
|
|
6
|
+
import '@opentelemetry/instrumentation';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { r as AdapterFactory, B as BackoffConfig, s as BackoffStrategy, t as DispatchManyResult, u as DispatchResult, D as Duration, a as JobClass, v as JobContext, J as JobData, i as JobFactory, g as JobOptions, d as JobRecord, c as JobRetention, w as JobStatus, L as Logger, h as QueueConfig, Q as QueueManagerConfig, R as RetryConfig, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions, x as ScheduleResult, k as ScheduleStatus, y as WorkerConfig, W as WorkerCycle } from '../../job-Z5fBSzRX.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { J as JobData, b as AcquiredJob } from '../../job-Z5fBSzRX.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tracing data structure for job dispatch events.
|
|
5
|
+
*/
|
|
6
|
+
type JobDispatchMessage = {
|
|
7
|
+
/** The jobs being dispatched (single dispatch = array of one) */
|
|
8
|
+
jobs: JobData[];
|
|
9
|
+
/** Target queue name */
|
|
10
|
+
queue: string;
|
|
11
|
+
/** Delay in milliseconds before the job becomes available */
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** Error that caused the dispatch to fail */
|
|
14
|
+
error?: Error;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Tracing data structure for job execution events.
|
|
18
|
+
*/
|
|
19
|
+
type JobExecuteMessage = {
|
|
20
|
+
/** The acquired job being executed */
|
|
21
|
+
job: AcquiredJob;
|
|
22
|
+
/** Queue the job was acquired from */
|
|
23
|
+
queue: string;
|
|
24
|
+
/** Execution outcome (set in asyncEnd) */
|
|
25
|
+
status?: 'completed' | 'failed' | 'retrying';
|
|
26
|
+
/** Execution duration in milliseconds (set in asyncEnd) */
|
|
27
|
+
duration?: number;
|
|
28
|
+
/** Error that caused the failure (set in asyncEnd) */
|
|
29
|
+
error?: Error;
|
|
30
|
+
/** When the next retry is scheduled (set in asyncEnd for retrying jobs) */
|
|
31
|
+
nextRetryAt?: Date;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type { JobDispatchMessage, JobExecuteMessage };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=tracing_channels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boringnode/queue",
|
|
3
3
|
"description": "A simple and efficient queue system for Node.js applications",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.2",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
],
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./build/index.js",
|
|
12
|
+
"./otel": "./build/src/otel.js",
|
|
12
13
|
"./drivers/*": "./build/src/drivers/*.js",
|
|
13
14
|
"./contracts/*": "./build/src/contracts/*.js",
|
|
14
15
|
"./types": "./build/src/types/index.js"
|
|
@@ -40,29 +41,46 @@
|
|
|
40
41
|
"@japa/expect-type": "^2.0.4",
|
|
41
42
|
"@japa/file-system": "^3.0.0",
|
|
42
43
|
"@japa/runner": "^5.3.0",
|
|
44
|
+
"@opentelemetry/api": "^1.9.0",
|
|
45
|
+
"@opentelemetry/context-async-hooks": "^2.6.0",
|
|
46
|
+
"@opentelemetry/core": "^2.6.0",
|
|
47
|
+
"@opentelemetry/instrumentation": "^0.213.0",
|
|
48
|
+
"@opentelemetry/sdk-trace-base": "^2.6.0",
|
|
43
49
|
"@poppinss/ts-exec": "^1.4.4",
|
|
44
50
|
"@types/better-sqlite3": "^7.6.13",
|
|
45
|
-
"@types/node": "^
|
|
46
|
-
"@types/pg": "^8.
|
|
47
|
-
"better-sqlite3": "^12.
|
|
48
|
-
"bullmq": "^5.
|
|
51
|
+
"@types/node": "^25.5.0",
|
|
52
|
+
"@types/pg": "^8.20.0",
|
|
53
|
+
"better-sqlite3": "^12.8.0",
|
|
54
|
+
"bullmq": "^5.71.0",
|
|
49
55
|
"c8": "^11.0.0",
|
|
50
56
|
"del-cli": "^7.0.0",
|
|
51
|
-
"ioredis": "^5.10.
|
|
52
|
-
"knex": "
|
|
53
|
-
"oxfmt": "^0.
|
|
54
|
-
"oxlint": "^1.
|
|
55
|
-
"oxlint-tsgolint": "^0.
|
|
56
|
-
"pg": "^8.
|
|
57
|
+
"ioredis": "^5.10.1",
|
|
58
|
+
"knex": "3.1.0",
|
|
59
|
+
"oxfmt": "^0.41.0",
|
|
60
|
+
"oxlint": "^1.56.0",
|
|
61
|
+
"oxlint-tsgolint": "^0.17.1",
|
|
62
|
+
"pg": "^8.20.0",
|
|
57
63
|
"release-it": "^19.2.4",
|
|
58
64
|
"tsup": "^8.5.1",
|
|
59
65
|
"typescript": "^5.9.3"
|
|
60
66
|
},
|
|
61
67
|
"peerDependencies": {
|
|
68
|
+
"@opentelemetry/api": "^1.9.0",
|
|
69
|
+
"@opentelemetry/core": "^1.30.0 || ^2.0.0",
|
|
70
|
+
"@opentelemetry/instrumentation": "^0.200.0",
|
|
62
71
|
"ioredis": "^5.0.0",
|
|
63
72
|
"knex": "^3.0.0"
|
|
64
73
|
},
|
|
65
74
|
"peerDependenciesMeta": {
|
|
75
|
+
"@opentelemetry/api": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"@opentelemetry/core": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@opentelemetry/instrumentation": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
66
84
|
"ioredis": {
|
|
67
85
|
"optional": true
|
|
68
86
|
},
|
|
@@ -89,7 +107,8 @@
|
|
|
89
107
|
],
|
|
90
108
|
"publishConfig": {
|
|
91
109
|
"access": "public",
|
|
92
|
-
"tag": "latest"
|
|
110
|
+
"tag": "latest",
|
|
111
|
+
"provenance": true
|
|
93
112
|
},
|
|
94
113
|
"release-it": {
|
|
95
114
|
"git": {
|
|
@@ -99,8 +118,11 @@
|
|
|
99
118
|
},
|
|
100
119
|
"github": {
|
|
101
120
|
"release": true,
|
|
102
|
-
"releaseName": "v${version}"
|
|
103
|
-
|
|
121
|
+
"releaseName": "v${version}"
|
|
122
|
+
},
|
|
123
|
+
"npm": {
|
|
124
|
+
"publish": true,
|
|
125
|
+
"skipChecks": true
|
|
104
126
|
}
|
|
105
127
|
},
|
|
106
128
|
"packageManager": "yarn@4.12.0",
|