@trigger.dev/redis-worker 4.0.0-v4-beta.20 → 4.0.0-v4-beta.21

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/dist/index.d.cts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { Callback, Result, RedisOptions } from '@internal/redis';
2
2
  import { Logger } from '@trigger.dev/core/logger';
3
3
  import { z } from 'zod';
4
- import { Tracer } from '@internal/tracing';
4
+ import { Tracer, Meter } from '@internal/tracing';
5
5
  import { RetryOptions } from '@trigger.dev/core/v3/schemas';
6
- import { Registry } from 'prom-client';
7
6
 
8
7
  interface MessageCatalogSchema {
9
8
  [key: string]: z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion<any, any>;
@@ -49,6 +48,14 @@ declare class SimpleQueue<TMessageCatalog extends MessageCatalogSchema> {
49
48
  availableAt?: Date;
50
49
  visibilityTimeoutMs: number;
51
50
  }): Promise<void>;
51
+ enqueueOnce({ id, job, item, attempt, availableAt, visibilityTimeoutMs, }: {
52
+ id: string;
53
+ job: MessageCatalogKey<TMessageCatalog>;
54
+ item: MessageCatalogValue<TMessageCatalog, MessageCatalogKey<TMessageCatalog>>;
55
+ attempt?: number;
56
+ availableAt?: Date;
57
+ visibilityTimeoutMs: number;
58
+ }): Promise<boolean>;
52
59
  dequeue(count?: number): Promise<Array<QueueItem<TMessageCatalog>>>;
53
60
  ack(id: string, deduplicationKey?: string): Promise<void>;
54
61
  reschedule(id: string, availableAt: Date): Promise<void>;
@@ -67,6 +74,7 @@ declare module "@internal/redis" {
67
74
  ackItem(queue: string, items: string, id: string, deduplicationKey: string, callback?: Callback<number>): Result<number, Context>;
68
75
  redriveFromDeadLetterQueue(queue: string, items: string, dlq: string, dlqItems: string, id: string, callback?: Callback<number>): Result<number, Context>;
69
76
  moveToDeadLetterQueue(queue: string, items: string, dlq: string, dlqItems: string, id: string, errorMessage: string, callback?: Callback<number>): Result<number, Context>;
77
+ enqueueItemOnce(queue: string, items: string, id: string, score: number, serializedItem: string, callback?: Callback<number>): Result<number, Context>;
70
78
  }
71
79
  }
72
80
 
@@ -105,14 +113,14 @@ type WorkerOptions<TCatalog extends WorkerCatalog> = {
105
113
  shutdownTimeoutMs?: number;
106
114
  logger?: Logger;
107
115
  tracer?: Tracer;
108
- metrics?: {
109
- register: Registry;
110
- };
116
+ meter?: Meter;
111
117
  };
112
118
  declare class Worker<TCatalog extends WorkerCatalog> {
119
+ #private;
113
120
  private options;
114
121
  private subscriber;
115
122
  private tracer;
123
+ private meter;
116
124
  private metrics;
117
125
  queue: SimpleQueue<QueueCatalogFromWorkerCatalog<TCatalog>>;
118
126
  private jobs;
@@ -141,6 +149,23 @@ declare class Worker<TCatalog extends WorkerCatalog> {
141
149
  visibilityTimeoutMs?: number;
142
150
  availableAt?: Date;
143
151
  }): Promise<void>;
152
+ /**
153
+ * Enqueues a job for processing once. If the job is already in the queue, it will be ignored.
154
+ * @param options - The enqueue options.
155
+ * @param options.id - Required unique identifier for the job.
156
+ * @param options.job - The job type from the worker catalog.
157
+ * @param options.payload - The job payload that matches the schema defined in the catalog.
158
+ * @param options.visibilityTimeoutMs - Optional visibility timeout in milliseconds. Defaults to value from catalog.
159
+ * @param options.availableAt - Optional date when the job should become available for processing. Defaults to now.
160
+ * @returns A promise that resolves when the job is enqueued.
161
+ */
162
+ enqueueOnce<K extends keyof TCatalog>({ id, job, payload, visibilityTimeoutMs, availableAt, }: {
163
+ id: string;
164
+ job: K;
165
+ payload: z.infer<TCatalog[K]["schema"]>;
166
+ visibilityTimeoutMs?: number;
167
+ availableAt?: Date;
168
+ }): Promise<boolean>;
144
169
  /**
145
170
  * Reschedules an existing job to a new available date.
146
171
  * If the job isn't in the queue, it will be ignored.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { Callback, Result, RedisOptions } from '@internal/redis';
2
2
  import { Logger } from '@trigger.dev/core/logger';
3
3
  import { z } from 'zod';
4
- import { Tracer } from '@internal/tracing';
4
+ import { Tracer, Meter } from '@internal/tracing';
5
5
  import { RetryOptions } from '@trigger.dev/core/v3/schemas';
6
- import { Registry } from 'prom-client';
7
6
 
8
7
  interface MessageCatalogSchema {
9
8
  [key: string]: z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion<any, any>;
@@ -49,6 +48,14 @@ declare class SimpleQueue<TMessageCatalog extends MessageCatalogSchema> {
49
48
  availableAt?: Date;
50
49
  visibilityTimeoutMs: number;
51
50
  }): Promise<void>;
51
+ enqueueOnce({ id, job, item, attempt, availableAt, visibilityTimeoutMs, }: {
52
+ id: string;
53
+ job: MessageCatalogKey<TMessageCatalog>;
54
+ item: MessageCatalogValue<TMessageCatalog, MessageCatalogKey<TMessageCatalog>>;
55
+ attempt?: number;
56
+ availableAt?: Date;
57
+ visibilityTimeoutMs: number;
58
+ }): Promise<boolean>;
52
59
  dequeue(count?: number): Promise<Array<QueueItem<TMessageCatalog>>>;
53
60
  ack(id: string, deduplicationKey?: string): Promise<void>;
54
61
  reschedule(id: string, availableAt: Date): Promise<void>;
@@ -67,6 +74,7 @@ declare module "@internal/redis" {
67
74
  ackItem(queue: string, items: string, id: string, deduplicationKey: string, callback?: Callback<number>): Result<number, Context>;
68
75
  redriveFromDeadLetterQueue(queue: string, items: string, dlq: string, dlqItems: string, id: string, callback?: Callback<number>): Result<number, Context>;
69
76
  moveToDeadLetterQueue(queue: string, items: string, dlq: string, dlqItems: string, id: string, errorMessage: string, callback?: Callback<number>): Result<number, Context>;
77
+ enqueueItemOnce(queue: string, items: string, id: string, score: number, serializedItem: string, callback?: Callback<number>): Result<number, Context>;
70
78
  }
71
79
  }
72
80
 
@@ -105,14 +113,14 @@ type WorkerOptions<TCatalog extends WorkerCatalog> = {
105
113
  shutdownTimeoutMs?: number;
106
114
  logger?: Logger;
107
115
  tracer?: Tracer;
108
- metrics?: {
109
- register: Registry;
110
- };
116
+ meter?: Meter;
111
117
  };
112
118
  declare class Worker<TCatalog extends WorkerCatalog> {
119
+ #private;
113
120
  private options;
114
121
  private subscriber;
115
122
  private tracer;
123
+ private meter;
116
124
  private metrics;
117
125
  queue: SimpleQueue<QueueCatalogFromWorkerCatalog<TCatalog>>;
118
126
  private jobs;
@@ -141,6 +149,23 @@ declare class Worker<TCatalog extends WorkerCatalog> {
141
149
  visibilityTimeoutMs?: number;
142
150
  availableAt?: Date;
143
151
  }): Promise<void>;
152
+ /**
153
+ * Enqueues a job for processing once. If the job is already in the queue, it will be ignored.
154
+ * @param options - The enqueue options.
155
+ * @param options.id - Required unique identifier for the job.
156
+ * @param options.job - The job type from the worker catalog.
157
+ * @param options.payload - The job payload that matches the schema defined in the catalog.
158
+ * @param options.visibilityTimeoutMs - Optional visibility timeout in milliseconds. Defaults to value from catalog.
159
+ * @param options.availableAt - Optional date when the job should become available for processing. Defaults to now.
160
+ * @returns A promise that resolves when the job is enqueued.
161
+ */
162
+ enqueueOnce<K extends keyof TCatalog>({ id, job, payload, visibilityTimeoutMs, availableAt, }: {
163
+ id: string;
164
+ job: K;
165
+ payload: z.infer<TCatalog[K]["schema"]>;
166
+ visibilityTimeoutMs?: number;
167
+ availableAt?: Date;
168
+ }): Promise<boolean>;
144
169
  /**
145
170
  * Reschedules an existing job to a new available date.
146
171
  * If the job isn't in the queue, it will be ignored.
package/dist/index.js CHANGED
@@ -7,7 +7,6 @@ import { webcrypto } from 'node:crypto';
7
7
  import '@trigger.dev/core/v3/utils/flattenAttributes';
8
8
  import { calculateNextRetryDelay } from '@trigger.dev/core/v3';
9
9
  import { shutdownManager } from '@trigger.dev/core/v3/serverOnly';
10
- import { Histogram } from 'prom-client';
11
10
 
12
11
  const require = createRequire(import.meta.url || process.cwd() + '/index.js');
13
12
  var __create = Object.create;
@@ -9503,6 +9502,39 @@ var SimpleQueue = class {
9503
9502
  throw e;
9504
9503
  }
9505
9504
  }
9505
+ async enqueueOnce({
9506
+ id,
9507
+ job,
9508
+ item,
9509
+ attempt,
9510
+ availableAt,
9511
+ visibilityTimeoutMs
9512
+ }) {
9513
+ if (!id) {
9514
+ throw new Error("enqueueOnce requires an id");
9515
+ }
9516
+ try {
9517
+ const score = availableAt ? availableAt.getTime() : Date.now();
9518
+ const deduplicationKey = nanoid();
9519
+ const serializedItem = JSON.stringify({
9520
+ job,
9521
+ item,
9522
+ visibilityTimeoutMs,
9523
+ attempt,
9524
+ deduplicationKey
9525
+ });
9526
+ const result = await this.redis.enqueueItemOnce(`queue`, `items`, id, score, serializedItem);
9527
+ return result === 1;
9528
+ } catch (e) {
9529
+ this.logger.error(`SimpleQueue ${this.name}.enqueueOnce(): error enqueuing`, {
9530
+ queue: this.name,
9531
+ error: e,
9532
+ id,
9533
+ item
9534
+ });
9535
+ throw e;
9536
+ }
9537
+ }
9506
9538
  async dequeue(count = 1) {
9507
9539
  const now = Date.now();
9508
9540
  try {
@@ -9817,6 +9849,25 @@ var SimpleQueue = class {
9817
9849
  return 1
9818
9850
  `
9819
9851
  });
9852
+ this.redis.defineCommand("enqueueItemOnce", {
9853
+ numberOfKeys: 2,
9854
+ lua: `
9855
+ local queue = KEYS[1]
9856
+ local items = KEYS[2]
9857
+ local id = ARGV[1]
9858
+ local score = ARGV[2]
9859
+ local serializedItem = ARGV[3]
9860
+
9861
+ -- Only add if not exists
9862
+ local added = redis.call('HSETNX', items, id, serializedItem)
9863
+ if added == 1 then
9864
+ redis.call('ZADD', queue, 'NX', score, id)
9865
+ return 1
9866
+ else
9867
+ return 0
9868
+ end
9869
+ `
9870
+ });
9820
9871
  }
9821
9872
  };
9822
9873
 
@@ -10173,6 +10224,173 @@ var BaseContext = (
10173
10224
  );
10174
10225
  var ROOT_CONTEXT = new BaseContext();
10175
10226
 
10227
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeter.js
10228
+ var __extends = /* @__PURE__ */ function() {
10229
+ var extendStatics = function(d, b) {
10230
+ extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
10231
+ d2.__proto__ = b2;
10232
+ } || function(d2, b2) {
10233
+ for (var p in b2) if (Object.prototype.hasOwnProperty.call(b2, p)) d2[p] = b2[p];
10234
+ };
10235
+ return extendStatics(d, b);
10236
+ };
10237
+ return function(d, b) {
10238
+ if (typeof b !== "function" && b !== null)
10239
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
10240
+ extendStatics(d, b);
10241
+ function __() {
10242
+ this.constructor = d;
10243
+ }
10244
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
10245
+ };
10246
+ }();
10247
+ var NoopMeter = (
10248
+ /** @class */
10249
+ function() {
10250
+ function NoopMeter2() {
10251
+ }
10252
+ NoopMeter2.prototype.createGauge = function(_name, _options) {
10253
+ return NOOP_GAUGE_METRIC;
10254
+ };
10255
+ NoopMeter2.prototype.createHistogram = function(_name, _options) {
10256
+ return NOOP_HISTOGRAM_METRIC;
10257
+ };
10258
+ NoopMeter2.prototype.createCounter = function(_name, _options) {
10259
+ return NOOP_COUNTER_METRIC;
10260
+ };
10261
+ NoopMeter2.prototype.createUpDownCounter = function(_name, _options) {
10262
+ return NOOP_UP_DOWN_COUNTER_METRIC;
10263
+ };
10264
+ NoopMeter2.prototype.createObservableGauge = function(_name, _options) {
10265
+ return NOOP_OBSERVABLE_GAUGE_METRIC;
10266
+ };
10267
+ NoopMeter2.prototype.createObservableCounter = function(_name, _options) {
10268
+ return NOOP_OBSERVABLE_COUNTER_METRIC;
10269
+ };
10270
+ NoopMeter2.prototype.createObservableUpDownCounter = function(_name, _options) {
10271
+ return NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC;
10272
+ };
10273
+ NoopMeter2.prototype.addBatchObservableCallback = function(_callback, _observables) {
10274
+ };
10275
+ NoopMeter2.prototype.removeBatchObservableCallback = function(_callback) {
10276
+ };
10277
+ return NoopMeter2;
10278
+ }()
10279
+ );
10280
+ var NoopMetric = (
10281
+ /** @class */
10282
+ /* @__PURE__ */ function() {
10283
+ function NoopMetric2() {
10284
+ }
10285
+ return NoopMetric2;
10286
+ }()
10287
+ );
10288
+ var NoopCounterMetric = (
10289
+ /** @class */
10290
+ function(_super) {
10291
+ __extends(NoopCounterMetric2, _super);
10292
+ function NoopCounterMetric2() {
10293
+ return _super !== null && _super.apply(this, arguments) || this;
10294
+ }
10295
+ NoopCounterMetric2.prototype.add = function(_value, _attributes) {
10296
+ };
10297
+ return NoopCounterMetric2;
10298
+ }(NoopMetric)
10299
+ );
10300
+ var NoopUpDownCounterMetric = (
10301
+ /** @class */
10302
+ function(_super) {
10303
+ __extends(NoopUpDownCounterMetric2, _super);
10304
+ function NoopUpDownCounterMetric2() {
10305
+ return _super !== null && _super.apply(this, arguments) || this;
10306
+ }
10307
+ NoopUpDownCounterMetric2.prototype.add = function(_value, _attributes) {
10308
+ };
10309
+ return NoopUpDownCounterMetric2;
10310
+ }(NoopMetric)
10311
+ );
10312
+ var NoopGaugeMetric = (
10313
+ /** @class */
10314
+ function(_super) {
10315
+ __extends(NoopGaugeMetric2, _super);
10316
+ function NoopGaugeMetric2() {
10317
+ return _super !== null && _super.apply(this, arguments) || this;
10318
+ }
10319
+ NoopGaugeMetric2.prototype.record = function(_value, _attributes) {
10320
+ };
10321
+ return NoopGaugeMetric2;
10322
+ }(NoopMetric)
10323
+ );
10324
+ var NoopHistogramMetric = (
10325
+ /** @class */
10326
+ function(_super) {
10327
+ __extends(NoopHistogramMetric2, _super);
10328
+ function NoopHistogramMetric2() {
10329
+ return _super !== null && _super.apply(this, arguments) || this;
10330
+ }
10331
+ NoopHistogramMetric2.prototype.record = function(_value, _attributes) {
10332
+ };
10333
+ return NoopHistogramMetric2;
10334
+ }(NoopMetric)
10335
+ );
10336
+ var NoopObservableMetric = (
10337
+ /** @class */
10338
+ function() {
10339
+ function NoopObservableMetric2() {
10340
+ }
10341
+ NoopObservableMetric2.prototype.addCallback = function(_callback) {
10342
+ };
10343
+ NoopObservableMetric2.prototype.removeCallback = function(_callback) {
10344
+ };
10345
+ return NoopObservableMetric2;
10346
+ }()
10347
+ );
10348
+ var NoopObservableCounterMetric = (
10349
+ /** @class */
10350
+ function(_super) {
10351
+ __extends(NoopObservableCounterMetric2, _super);
10352
+ function NoopObservableCounterMetric2() {
10353
+ return _super !== null && _super.apply(this, arguments) || this;
10354
+ }
10355
+ return NoopObservableCounterMetric2;
10356
+ }(NoopObservableMetric)
10357
+ );
10358
+ var NoopObservableGaugeMetric = (
10359
+ /** @class */
10360
+ function(_super) {
10361
+ __extends(NoopObservableGaugeMetric2, _super);
10362
+ function NoopObservableGaugeMetric2() {
10363
+ return _super !== null && _super.apply(this, arguments) || this;
10364
+ }
10365
+ return NoopObservableGaugeMetric2;
10366
+ }(NoopObservableMetric)
10367
+ );
10368
+ var NoopObservableUpDownCounterMetric = (
10369
+ /** @class */
10370
+ function(_super) {
10371
+ __extends(NoopObservableUpDownCounterMetric2, _super);
10372
+ function NoopObservableUpDownCounterMetric2() {
10373
+ return _super !== null && _super.apply(this, arguments) || this;
10374
+ }
10375
+ return NoopObservableUpDownCounterMetric2;
10376
+ }(NoopObservableMetric)
10377
+ );
10378
+ var NOOP_METER = new NoopMeter();
10379
+ var NOOP_COUNTER_METRIC = new NoopCounterMetric();
10380
+ var NOOP_GAUGE_METRIC = new NoopGaugeMetric();
10381
+ var NOOP_HISTOGRAM_METRIC = new NoopHistogramMetric();
10382
+ var NOOP_UP_DOWN_COUNTER_METRIC = new NoopUpDownCounterMetric();
10383
+ var NOOP_OBSERVABLE_COUNTER_METRIC = new NoopObservableCounterMetric();
10384
+ var NOOP_OBSERVABLE_GAUGE_METRIC = new NoopObservableGaugeMetric();
10385
+ var NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC = new NoopObservableUpDownCounterMetric();
10386
+
10387
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics/Metric.js
10388
+ var ValueType;
10389
+ (function(ValueType2) {
10390
+ ValueType2[ValueType2["INT"] = 0] = "INT";
10391
+ ValueType2[ValueType2["DOUBLE"] = 1] = "DOUBLE";
10392
+ })(ValueType || (ValueType = {}));
10393
+
10176
10394
  // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js
10177
10395
  var __read3 = function(o, n) {
10178
10396
  var m = typeof Symbol === "function" && o[Symbol.iterator];
@@ -10535,8 +10753,54 @@ var SpanStatusCode;
10535
10753
  SpanStatusCode2[SpanStatusCode2["ERROR"] = 2] = "ERROR";
10536
10754
  })(SpanStatusCode || (SpanStatusCode = {}));
10537
10755
 
10756
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeterProvider.js
10757
+ var NoopMeterProvider = (
10758
+ /** @class */
10759
+ function() {
10760
+ function NoopMeterProvider2() {
10761
+ }
10762
+ NoopMeterProvider2.prototype.getMeter = function(_name, _version, _options) {
10763
+ return NOOP_METER;
10764
+ };
10765
+ return NoopMeterProvider2;
10766
+ }()
10767
+ );
10768
+ var NOOP_METER_PROVIDER = new NoopMeterProvider();
10769
+
10770
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/api/metrics.js
10771
+ var API_NAME3 = "metrics";
10772
+ var MetricsAPI = (
10773
+ /** @class */
10774
+ function() {
10775
+ function MetricsAPI2() {
10776
+ }
10777
+ MetricsAPI2.getInstance = function() {
10778
+ if (!this._instance) {
10779
+ this._instance = new MetricsAPI2();
10780
+ }
10781
+ return this._instance;
10782
+ };
10783
+ MetricsAPI2.prototype.setGlobalMeterProvider = function(provider) {
10784
+ return registerGlobal(API_NAME3, provider, DiagAPI.instance());
10785
+ };
10786
+ MetricsAPI2.prototype.getMeterProvider = function() {
10787
+ return getGlobal(API_NAME3) || NOOP_METER_PROVIDER;
10788
+ };
10789
+ MetricsAPI2.prototype.getMeter = function(name, version, options) {
10790
+ return this.getMeterProvider().getMeter(name, version, options);
10791
+ };
10792
+ MetricsAPI2.prototype.disable = function() {
10793
+ unregisterGlobal(API_NAME3, DiagAPI.instance());
10794
+ };
10795
+ return MetricsAPI2;
10796
+ }()
10797
+ );
10798
+
10799
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics-api.js
10800
+ var metrics = MetricsAPI.getInstance();
10801
+
10538
10802
  // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/api/trace.js
10539
- var API_NAME3 = "trace";
10803
+ var API_NAME4 = "trace";
10540
10804
  var TraceAPI = (
10541
10805
  /** @class */
10542
10806
  function() {
@@ -10558,20 +10822,20 @@ var TraceAPI = (
10558
10822
  return this._instance;
10559
10823
  };
10560
10824
  TraceAPI2.prototype.setGlobalTracerProvider = function(provider) {
10561
- var success = registerGlobal(API_NAME3, this._proxyTracerProvider, DiagAPI.instance());
10825
+ var success = registerGlobal(API_NAME4, this._proxyTracerProvider, DiagAPI.instance());
10562
10826
  if (success) {
10563
10827
  this._proxyTracerProvider.setDelegate(provider);
10564
10828
  }
10565
10829
  return success;
10566
10830
  };
10567
10831
  TraceAPI2.prototype.getTracerProvider = function() {
10568
- return getGlobal(API_NAME3) || this._proxyTracerProvider;
10832
+ return getGlobal(API_NAME4) || this._proxyTracerProvider;
10569
10833
  };
10570
10834
  TraceAPI2.prototype.getTracer = function(name, version) {
10571
10835
  return this.getTracerProvider().getTracer(name, version);
10572
10836
  };
10573
10837
  TraceAPI2.prototype.disable = function() {
10574
- unregisterGlobal(API_NAME3, DiagAPI.instance());
10838
+ unregisterGlobal(API_NAME4, DiagAPI.instance());
10575
10839
  this._proxyTracerProvider = new ProxyTracerProvider();
10576
10840
  };
10577
10841
  return TraceAPI2;
@@ -10751,6 +11015,7 @@ var Worker = class _Worker {
10751
11015
  this.options = options;
10752
11016
  this.logger = options.logger ?? new Logger("Worker", "debug");
10753
11017
  this.tracer = options.tracer ?? trace.getTracer(options.name);
11018
+ this.meter = options.meter ?? metrics.getMeter(options.name);
10754
11019
  this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? 6e4;
10755
11020
  const schema = Object.fromEntries(
10756
11021
  Object.entries(this.options.catalog).map(([key, value]) => [key, value.schema])
@@ -10765,56 +11030,47 @@ var Worker = class _Worker {
10765
11030
  const { workers = 1, tasksPerWorker = 1, limit = 10 } = options.concurrency ?? {};
10766
11031
  this.concurrency = { workers, tasksPerWorker, limit };
10767
11032
  this.limiter = pLimit(this.concurrency.limit);
10768
- this.metrics.register = options.metrics?.register;
10769
- if (!this.metrics.register) {
10770
- return;
10771
- }
10772
- this.metrics.enqueueDuration = new Histogram({
10773
- name: "redis_worker_enqueue_duration_seconds",
10774
- help: "The duration of enqueue operations",
10775
- labelNames: ["worker_name", "job_type", "has_available_at"],
10776
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10777
- registers: [this.metrics.register]
10778
- });
10779
- this.metrics.dequeueDuration = new Histogram({
10780
- name: "redis_worker_dequeue_duration_seconds",
10781
- help: "The duration of dequeue operations",
10782
- labelNames: ["worker_name", "worker_id", "task_count"],
10783
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10784
- registers: [this.metrics.register]
10785
- });
10786
- this.metrics.jobDuration = new Histogram({
10787
- name: "redis_worker_job_duration_seconds",
10788
- help: "The duration of job operations",
10789
- labelNames: ["worker_name", "worker_id", "batch_size", "job_type", "attempt"],
10790
- // use different buckets here as jobs can take a while to run
10791
- buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 20, 30, 45, 60],
10792
- registers: [this.metrics.register]
10793
- });
10794
- this.metrics.ackDuration = new Histogram({
10795
- name: "redis_worker_ack_duration_seconds",
10796
- help: "The duration of ack operations",
10797
- labelNames: ["worker_name"],
10798
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10799
- registers: [this.metrics.register]
10800
- });
10801
- this.metrics.redriveDuration = new Histogram({
10802
- name: "redis_worker_redrive_duration_seconds",
10803
- help: "The duration of redrive operations",
10804
- labelNames: ["worker_name"],
10805
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10806
- registers: [this.metrics.register]
10807
- });
10808
- this.metrics.rescheduleDuration = new Histogram({
10809
- name: "redis_worker_reschedule_duration_seconds",
10810
- help: "The duration of reschedule operations",
10811
- labelNames: ["worker_name"],
10812
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10813
- registers: [this.metrics.register]
11033
+ const masterQueueObservableGauge = this.meter.createObservableGauge("redis_worker.queue.size", {
11034
+ description: "The number of items in the queue",
11035
+ unit: "items",
11036
+ valueType: ValueType.INT
10814
11037
  });
11038
+ masterQueueObservableGauge.addCallback(this.#updateQueueSizeMetric.bind(this));
11039
+ const deadLetterQueueObservableGauge = this.meter.createObservableGauge(
11040
+ "redis_worker.queue.dead_letter_size",
11041
+ {
11042
+ description: "The number of items in the dead letter queue",
11043
+ unit: "items",
11044
+ valueType: ValueType.INT
11045
+ }
11046
+ );
11047
+ deadLetterQueueObservableGauge.addCallback(this.#updateDeadLetterQueueSizeMetric.bind(this));
11048
+ const concurrencyLimitActiveObservableGauge = this.meter.createObservableGauge(
11049
+ "redis_worker.concurrency.active",
11050
+ {
11051
+ description: "The number of active workers",
11052
+ unit: "workers",
11053
+ valueType: ValueType.INT
11054
+ }
11055
+ );
11056
+ concurrencyLimitActiveObservableGauge.addCallback(
11057
+ this.#updateConcurrencyLimitActiveMetric.bind(this)
11058
+ );
11059
+ const concurrencyLimitPendingObservableGauge = this.meter.createObservableGauge(
11060
+ "redis_worker.concurrency.pending",
11061
+ {
11062
+ description: "The number of pending workers",
11063
+ unit: "workers",
11064
+ valueType: ValueType.INT
11065
+ }
11066
+ );
11067
+ concurrencyLimitPendingObservableGauge.addCallback(
11068
+ this.#updateConcurrencyLimitPendingMetric.bind(this)
11069
+ );
10815
11070
  }
10816
11071
  subscriber;
10817
11072
  tracer;
11073
+ meter;
10818
11074
  metrics = {};
10819
11075
  queue;
10820
11076
  jobs;
@@ -10825,6 +11081,28 @@ var Worker = class _Worker {
10825
11081
  shutdownTimeoutMs;
10826
11082
  // The p-limit limiter to control overall concurrency.
10827
11083
  limiter;
11084
+ async #updateQueueSizeMetric(observableResult) {
11085
+ const queueSize = await this.queue.size();
11086
+ observableResult.observe(queueSize, {
11087
+ worker_name: this.options.name
11088
+ });
11089
+ }
11090
+ async #updateDeadLetterQueueSizeMetric(observableResult) {
11091
+ const deadLetterQueueSize = await this.queue.sizeOfDeadLetterQueue();
11092
+ observableResult.observe(deadLetterQueueSize, {
11093
+ worker_name: this.options.name
11094
+ });
11095
+ }
11096
+ async #updateConcurrencyLimitActiveMetric(observableResult) {
11097
+ observableResult.observe(this.limiter.activeCount, {
11098
+ worker_name: this.options.name
11099
+ });
11100
+ }
11101
+ async #updateConcurrencyLimitPendingMetric(observableResult) {
11102
+ observableResult.observe(this.limiter.pendingCount, {
11103
+ worker_name: this.options.name
11104
+ });
11105
+ }
10828
11106
  start() {
10829
11107
  const { workers, tasksPerWorker } = this.concurrency;
10830
11108
  for (let i = 0; i < workers; i++) {
@@ -10892,6 +11170,56 @@ var Worker = class _Worker {
10892
11170
  }
10893
11171
  );
10894
11172
  }
11173
+ /**
11174
+ * Enqueues a job for processing once. If the job is already in the queue, it will be ignored.
11175
+ * @param options - The enqueue options.
11176
+ * @param options.id - Required unique identifier for the job.
11177
+ * @param options.job - The job type from the worker catalog.
11178
+ * @param options.payload - The job payload that matches the schema defined in the catalog.
11179
+ * @param options.visibilityTimeoutMs - Optional visibility timeout in milliseconds. Defaults to value from catalog.
11180
+ * @param options.availableAt - Optional date when the job should become available for processing. Defaults to now.
11181
+ * @returns A promise that resolves when the job is enqueued.
11182
+ */
11183
+ enqueueOnce({
11184
+ id,
11185
+ job,
11186
+ payload,
11187
+ visibilityTimeoutMs,
11188
+ availableAt
11189
+ }) {
11190
+ return startSpan(
11191
+ this.tracer,
11192
+ "enqueueOnce",
11193
+ async (span) => {
11194
+ const timeout = visibilityTimeoutMs ?? this.options.catalog[job]?.visibilityTimeoutMs;
11195
+ if (!timeout) {
11196
+ throw new Error(`No visibility timeout found for job ${String(job)} with id ${id}`);
11197
+ }
11198
+ span.setAttribute("job_visibility_timeout_ms", timeout);
11199
+ return this.withHistogram(
11200
+ this.metrics.enqueueDuration,
11201
+ this.queue.enqueueOnce({
11202
+ id,
11203
+ job,
11204
+ item: payload,
11205
+ visibilityTimeoutMs: timeout,
11206
+ availableAt
11207
+ }),
11208
+ {
11209
+ job_type: String(job),
11210
+ has_available_at: availableAt ? "true" : "false"
11211
+ }
11212
+ );
11213
+ },
11214
+ {
11215
+ kind: SpanKind.PRODUCER,
11216
+ attributes: {
11217
+ job_type: String(job),
11218
+ job_id: id
11219
+ }
11220
+ }
11221
+ );
11222
+ }
10895
11223
  /**
10896
11224
  * Reschedules an existing job to a new available date.
10897
11225
  * If the job isn't in the queue, it will be ignored.
@@ -11076,14 +11404,15 @@ var Worker = class _Worker {
11076
11404
  });
11077
11405
  }
11078
11406
  async withHistogram(histogram, promise, labels) {
11079
- if (!histogram || !this.metrics.register) {
11407
+ if (!histogram) {
11080
11408
  return promise;
11081
11409
  }
11082
- const end = histogram.startTimer({ worker_name: this.options.name, ...labels });
11410
+ const start = Date.now();
11083
11411
  try {
11084
11412
  return await promise;
11085
11413
  } finally {
11086
- end();
11414
+ const duration = (Date.now() - start) / 1e3;
11415
+ histogram.record(duration, { worker_name: this.options.name, ...labels });
11087
11416
  }
11088
11417
  }
11089
11418
  // A simple helper to delay for a given number of milliseconds.