@trigger.dev/redis-worker 0.0.0-prerelease-20251209154359 → 0.0.0-prerelease-20251211173228

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.cjs CHANGED
@@ -11784,12 +11784,7 @@ var ConcurrencyManager = class {
11784
11784
  );
11785
11785
  const keys = groupData.map((g) => g.key);
11786
11786
  const limits = groupData.map((g) => g.limit.toString());
11787
- const result = await this.redis.reserveConcurrency(
11788
- keys.length.toString(),
11789
- messageId,
11790
- ...keys,
11791
- ...limits
11792
- );
11787
+ const result = await this.redis.reserveConcurrency(keys.length, keys, messageId, ...limits);
11793
11788
  return result === 1;
11794
11789
  }
11795
11790
  /**
@@ -11885,16 +11880,14 @@ var ConcurrencyManager = class {
11885
11880
  // ============================================================================
11886
11881
  #registerCommands() {
11887
11882
  this.redis.defineCommand("reserveConcurrency", {
11888
- numberOfKeys: 0,
11889
- // Will pass number of keys in ARGV
11890
11883
  lua: `
11891
- local numGroups = tonumber(ARGV[1])
11892
- local messageId = ARGV[2]
11884
+ local numGroups = #KEYS
11885
+ local messageId = ARGV[1]
11893
11886
 
11894
11887
  -- Check all groups first
11895
11888
  for i = 1, numGroups do
11896
- local key = ARGV[2 + i] -- Keys start at ARGV[3]
11897
- local limit = tonumber(ARGV[2 + numGroups + i]) -- Limits come after keys
11889
+ local key = KEYS[i]
11890
+ local limit = tonumber(ARGV[1 + i]) -- Limits start at ARGV[2]
11898
11891
  local current = redis.call('SCARD', key)
11899
11892
 
11900
11893
  if current >= limit then
@@ -11904,7 +11897,7 @@ end
11904
11897
 
11905
11898
  -- All groups have capacity, add message to all
11906
11899
  for i = 1, numGroups do
11907
- local key = ARGV[2 + i]
11900
+ local key = KEYS[i]
11908
11901
  redis.call('SADD', key, messageId)
11909
11902
  end
11910
11903
 
@@ -13222,7 +13215,20 @@ var DRRScheduler = class extends BaseScheduler {
13222
13215
  const eligibleTenants = tenantData.filter(
13223
13216
  (t) => !t.isAtCapacity && t.deficit >= 1
13224
13217
  );
13218
+ const blockedTenants = tenantData.filter((t) => t.isAtCapacity);
13219
+ if (blockedTenants.length > 0) {
13220
+ this.logger.debug("DRR: tenants blocked by concurrency", {
13221
+ blockedCount: blockedTenants.length,
13222
+ blockedTenants: blockedTenants.map((t) => t.tenantId)
13223
+ });
13224
+ }
13225
13225
  eligibleTenants.sort((a, b) => b.deficit - a.deficit);
13226
+ this.logger.debug("DRR: queue selection complete", {
13227
+ totalQueues: queues.length,
13228
+ totalTenants: tenantIds.length,
13229
+ eligibleTenants: eligibleTenants.length,
13230
+ topTenantDeficit: eligibleTenants[0]?.deficit
13231
+ });
13226
13232
  return eligibleTenants.map((t) => ({
13227
13233
  tenantId: t.tenantId,
13228
13234
  queues: t.queues
@@ -13878,6 +13884,7 @@ var FairQueue = class {
13878
13884
  this.cooloffEnabled = options.cooloff?.enabled ?? true;
13879
13885
  this.cooloffThreshold = options.cooloff?.threshold ?? 10;
13880
13886
  this.cooloffPeriodMs = options.cooloff?.periodMs ?? 1e4;
13887
+ this.globalRateLimiter = options.globalRateLimiter;
13881
13888
  this.telemetry = new FairQueueTelemetry({
13882
13889
  tracer: options.tracer,
13883
13890
  meter: options.meter,
@@ -13948,6 +13955,8 @@ var FairQueue = class {
13948
13955
  cooloffThreshold;
13949
13956
  cooloffPeriodMs;
13950
13957
  queueCooloffStates = /* @__PURE__ */ new Map();
13958
+ // Global rate limiter
13959
+ globalRateLimiter;
13951
13960
  // Runtime state
13952
13961
  messageHandler;
13953
13962
  isRunning = false;
@@ -13958,6 +13967,30 @@ var FairQueue = class {
13958
13967
  // Queue descriptor cache for message processing
13959
13968
  queueDescriptorCache = /* @__PURE__ */ new Map();
13960
13969
  // ============================================================================
13970
+ // Public API - Telemetry
13971
+ // ============================================================================
13972
+ /**
13973
+ * Register observable gauge callbacks for telemetry.
13974
+ * Call this after FairQueue is created to enable gauge metrics.
13975
+ *
13976
+ * @param options.observedTenants - List of tenant IDs to observe for DLQ metrics
13977
+ */
13978
+ registerTelemetryGauges(options) {
13979
+ this.telemetry.registerGaugeCallbacks({
13980
+ getMasterQueueLength: async (shardId) => {
13981
+ return await this.masterQueue.getShardQueueCount(shardId);
13982
+ },
13983
+ getInflightCount: async (shardId) => {
13984
+ return await this.visibilityManager.getInflightCount(shardId);
13985
+ },
13986
+ getDLQLength: async (tenantId) => {
13987
+ return await this.getDeadLetterQueueLength(tenantId);
13988
+ },
13989
+ shardCount: this.shardCount,
13990
+ observedTenants: options?.observedTenants
13991
+ });
13992
+ }
13993
+ // ============================================================================
13961
13994
  // Public API - Message Handler
13962
13995
  // ============================================================================
13963
13996
  /**
@@ -14409,6 +14442,16 @@ var FairQueue = class {
14409
14442
  return false;
14410
14443
  }
14411
14444
  }
14445
+ if (this.globalRateLimiter) {
14446
+ const result = await this.globalRateLimiter.limit();
14447
+ if (!result.allowed && result.resetAt) {
14448
+ const waitMs = Math.max(0, result.resetAt - Date.now());
14449
+ if (waitMs > 0) {
14450
+ this.logger.debug("Global rate limit reached, waiting", { waitMs, loopId });
14451
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
14452
+ }
14453
+ }
14454
+ }
14412
14455
  const claimResult = await this.visibilityManager.claim(
14413
14456
  queueId,
14414
14457
  queueKey,
@@ -14534,18 +14577,33 @@ var FairQueue = class {
14534
14577
  return;
14535
14578
  }
14536
14579
  for (const { tenantId, queues } of tenantQueues) {
14537
- for (const queueId of queues) {
14538
- if (this.cooloffEnabled && this.#isInCooloff(queueId)) {
14539
- continue;
14580
+ let availableSlots = 1;
14581
+ if (this.concurrencyManager) {
14582
+ const [current, limit] = await Promise.all([
14583
+ this.concurrencyManager.getCurrentConcurrency("tenant", tenantId),
14584
+ this.concurrencyManager.getConcurrencyLimit("tenant", tenantId)
14585
+ ]);
14586
+ availableSlots = Math.max(1, limit - current);
14587
+ }
14588
+ let slotsUsed = 0;
14589
+ queueLoop: for (const queueId of queues) {
14590
+ while (slotsUsed < availableSlots) {
14591
+ if (this.cooloffEnabled && this.#isInCooloff(queueId)) {
14592
+ break;
14593
+ }
14594
+ const processed = await this.#processOneMessage(loopId, queueId, tenantId, shardId);
14595
+ if (processed) {
14596
+ await this.scheduler.recordProcessed?.(tenantId, queueId);
14597
+ this.#resetCooloff(queueId);
14598
+ slotsUsed++;
14599
+ } else {
14600
+ this.#incrementCooloff(queueId);
14601
+ break;
14602
+ }
14540
14603
  }
14541
- const processed = await this.#processOneMessage(loopId, queueId, tenantId, shardId);
14542
- if (processed) {
14543
- await this.scheduler.recordProcessed?.(tenantId, queueId);
14544
- this.#resetCooloff(queueId);
14545
- } else {
14546
- this.#incrementCooloff(queueId);
14604
+ if (slotsUsed >= availableSlots) {
14605
+ break queueLoop;
14547
14606
  }
14548
- break;
14549
14607
  }
14550
14608
  }
14551
14609
  }
@@ -14564,6 +14622,16 @@ var FairQueue = class {
14564
14622
  return false;
14565
14623
  }
14566
14624
  }
14625
+ if (this.globalRateLimiter) {
14626
+ const result = await this.globalRateLimiter.limit();
14627
+ if (!result.allowed && result.resetAt) {
14628
+ const waitMs = Math.max(0, result.resetAt - Date.now());
14629
+ if (waitMs > 0) {
14630
+ this.logger.debug("Global rate limit reached, waiting", { waitMs, loopId });
14631
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
14632
+ }
14633
+ }
14634
+ }
14567
14635
  const claimResult = await this.visibilityManager.claim(
14568
14636
  queueId,
14569
14637
  queueKey,
@@ -14888,6 +14956,11 @@ var FairQueue = class {
14888
14956
  tag: "cooloff",
14889
14957
  expiresAt: Date.now() + this.cooloffPeriodMs
14890
14958
  });
14959
+ this.logger.debug("Queue entered cooloff", {
14960
+ queueId,
14961
+ cooloffPeriodMs: this.cooloffPeriodMs,
14962
+ consecutiveFailures: newFailures
14963
+ });
14891
14964
  } else {
14892
14965
  this.queueCooloffStates.set(queueId, {
14893
14966
  tag: "normal",