Skip to content

Conversation

@guillermolg00
Copy link

✅ Checklist

  • I have followed every step in the contributing guide
  • The PR title follows the convention.
  • I ran and tested the code works

Testing

  • Added unit tests for the GCRA rate limiter in internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • Added schema validation tests in packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • Manually tested rate limiting behavior using the test-trigger reference project

To run the tests:
cd internal-packages/run-engine
pnpm run test ./src/run-queue/tests/rateLimit.test.ts---

Changelog

Add GCRA (Generic Cell Rate Algorithm) rate limiting support for task queues. This feature allows users to configure rate limits on their tasks to control execution throughput.

Changes:

  • Added GCRARateLimiter implementation in @internal/run-engine
  • Added rate limit schemas to @trigger.dev/core
  • Integrated rate limiting into @trigger.dev/sdk task definitions
  • Updated enqueueRun service to respect rate limits

💯

@changeset-bot
Copy link

changeset-bot bot commented Dec 18, 2025

🦋 Changeset detected

Latest commit: f325c8d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 26 packages
Name Type
@trigger.dev/sdk Minor
@trigger.dev/core Minor
@trigger.dev/python Minor
d3-chat Patch
references-d3-openai-agents Patch
references-nextjs-realtime Patch
references-realtime-hooks-test Patch
references-realtime-streams Patch
references-telemetry Patch
@trigger.dev/build Minor
trigger.dev Minor
@trigger.dev/redis-worker Minor
@trigger.dev/schema-to-json Minor
@internal/cache Patch
@internal/clickhouse Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/schedule-engine Patch
@internal/testcontainers Patch
@internal/tracing Patch
@internal/zod-worker Patch
@trigger.dev/react-hooks Minor
@trigger.dev/rsc Minor
@trigger.dev/database Minor
@trigger.dev/otlp-importer Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

Adds GCRA-based Redis rate limiting across the task queue system: new duration parsing utilities and schemas, queue-level rateLimit configuration and per-run rateLimitKey propagation, a GCRARateLimiter implementation (Lua-backed) and RunQueue extensions to store/enforce configs, database schema and queue record changes, environment flag to disable rate limits, API/SDK plumbing for rateLimit and rateLimitKey, and unit/integration tests for duration parsing, key generation, enqueue/dequeue, and rate-limit behaviors.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • internal-packages/run-engine/src/rate-limiter/gcra.ts — verify GCRA math, Lua script correctness, TTL/expiration and error handling
  • internal-packages/run-engine/src/run-queue/index.ts — Lua dequeue changes, integration of rate checks with concurrency, and Redis command surface
  • internal-packages/run-engine/src/run-queue/keyProducer.ts and types.ts — key naming, parsing, and API signatures consistency
  • internal-packages/run-engine/src/engine/* and internal-packages/run-engine/src/index.ts — propagation of rateLimitKey, RunQueue options, and public exports
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts and apps/webapp/app/v3/runQueue.server.ts — persistence of rateLimitConfig, Redis sync logic, and environment-driven disable behavior
  • packages/core/src/v3/schemas/* and packages/core/src/v3/types/* — schema/typing correctness for duration strings and rateLimit config
  • Tests: internal-packages/run-engine/src/run-queue/tests/* and apps/webapp/test/parseDurationToMs.test.ts — ensure tests reflect intended behavior and cover edge cases (burst, per-key buckets, disabled mode)

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Implement queue rate limiting with GCRA algorithm' clearly and specifically describes the main change: adding GCRA-based rate limiting for task queues.
Description check ✅ Passed The PR description follows the required template with all key sections completed: checklist items checked, testing steps documented, detailed changelog, and proper formatting.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0beb5f8 and f325c8d.

📒 Files selected for processing (1)
  • packages/trigger-sdk/src/v3/shared.ts (13 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/trigger-sdk/src/v3/shared.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
.changeset/forty-houses-doubt.md (1)

6-6: Consider hyphenating compound adjective (optional refinement).

While not incorrect, "rate-limiting algorithm" is the more standard hyphenation for a compound adjective. However, this is a minor style preference and the current text is perfectly acceptable.

If you prefer strict grammatical convention:

-Add GCRA rate limiting algorithm for task queue management
+Add GCRA rate-limiting algorithm for task queue management
internal-packages/run-engine/src/engine/systems/enqueueSystem.ts (1)

86-104: LGTM! Rate limit key fallback logic is correct.

The implementation correctly handles rate limit key propagation with a clear fallback chain: explicit parameter → stored run value → undefined. The comment on lines 86-89 helpfully explains the behavior for re-enqueue scenarios (checkpoint, delay, waitpoint, pendingVersion).

Optional simplification for Line 90:

-      const effectiveRateLimitKey = rateLimitKey ?? run.rateLimitKey ?? undefined;
+      const effectiveRateLimitKey = rateLimitKey ?? run.rateLimitKey;

The final ?? undefined is redundant since the nullish coalescing already returns undefined when both operands are nullish.

packages/core/src/v3/schemas/schemas.ts (1)

149-161: Consider consolidating duplicate duration regex and aligning burst constraints.

The RateLimitConfig schema duplicates the duration regex from DurationStringSchema (line 154 vs line 106). Also, burst here has min(1) but no max, while limit is capped at 100000.

Consider:

  1. Reusing DurationStringSchema instead of inline regex
  2. Adding a max constraint to burst for consistency
 export const RateLimitConfig = z.object({
   /** Maximum number of requests allowed in the period */
   limit: z.number().int().min(1).max(100000),
   /** Time window - must be a valid duration string (e.g., "1s", "100ms", "5m", "1h") */
-  period: z.string().regex(/^\d+(\.\d+)?(ms|s|m|h|d)$/, {
-    message: 'Period must be a valid duration string (e.g., "1s", "100ms", "5m", "1h")',
-  }),
+  period: DurationStringSchema,
   /** Optional burst allowance - allows temporary exceeding of rate limit (defaults to limit) */
-  burst: z.number().int().min(1).optional(),
+  burst: z.number().int().min(1).max(100000).optional(),
 });

Note: If you need the custom error message, you could add .refine() or keep the inline approach, but the duplicate regex increases maintenance burden.

internal-packages/run-engine/src/rate-limiter/gcra.ts (1)

6-75: Consider using types instead of interfaces per coding guidelines.

The coding guidelines specify "Use types over interfaces for TypeScript". The current implementation uses interface for QueueRateLimitConfig, StoredQueueRateLimitConfig, GCRAParams, GCRARateLimiterOptions, and RateLimitResult.

While functionally equivalent here, for consistency with project conventions:

-export interface QueueRateLimitConfig {
+export type QueueRateLimitConfig = {
   /** Maximum number of requests allowed within the period */
   limit: number;
   /** Time period in milliseconds */
   periodMs: number;
   /** Optional burst capacity (defaults to 1) */
   burst?: number;
-}
+};

Apply similar changes to the other interfaces.

apps/webapp/app/v3/services/createBackgroundWorker.server.ts (1)

373-388: Consider whether silently continuing without rate limiting is the desired behavior.

When parseDurationToMs throws (e.g., invalid period format like "invalid"), the error is logged but rateLimitConfig remains null, and the queue is created without rate limiting. This is a graceful degradation approach.

If rate limiting is critical for the user's use case, they might expect the deployment to fail rather than silently omit the rate limit. Consider whether this should throw a ServiceValidationError instead to surface the configuration error to the user.

If you want to fail fast on invalid rate limit configuration:

     } catch (error) {
       logger.error("createWorkerQueue: invalid rate limit period format", {
         queueName,
         rateLimit: queue.rateLimit,
         error,
       });
+      throw new ServiceValidationError(
+        `Invalid rate limit period format "${queue.rateLimit.period}" for queue "${queueName}". Expected format: number + unit (ms, s, m, h, d)`
+      );
     }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff80742 and 72c8ce0.

📒 Files selected for processing (29)
  • .changeset/forty-houses-doubt.md (1 hunks)
  • apps/webapp/app/env.server.ts (1 hunks)
  • apps/webapp/app/runEngine/services/triggerTask.server.ts (1 hunks)
  • apps/webapp/app/v3/runEngine.server.ts (1 hunks)
  • apps/webapp/app/v3/runQueue.server.ts (2 hunks)
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts (9 hunks)
  • apps/webapp/app/v3/services/enqueueRun.server.ts (3 hunks)
  • apps/webapp/app/v3/utils/durations.ts (1 hunks)
  • apps/webapp/test/parseDurationToMs.test.ts (1 hunks)
  • internal-packages/database/prisma/schema.prisma (1 hunks)
  • internal-packages/run-engine/src/engine/index.ts (5 hunks)
  • internal-packages/run-engine/src/engine/systems/enqueueSystem.ts (4 hunks)
  • internal-packages/run-engine/src/engine/types.ts (2 hunks)
  • internal-packages/run-engine/src/index.ts (1 hunks)
  • internal-packages/run-engine/src/rate-limiter/gcra.ts (1 hunks)
  • internal-packages/run-engine/src/rate-limiter/index.ts (1 hunks)
  • internal-packages/run-engine/src/run-queue/index.ts (8 hunks)
  • internal-packages/run-engine/src/run-queue/keyProducer.ts (2 hunks)
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts (1 hunks)
  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts (1 hunks)
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts (1 hunks)
  • internal-packages/run-engine/src/run-queue/types.ts (2 hunks)
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts (1 hunks)
  • packages/core/src/v3/schemas/api.ts (2 hunks)
  • packages/core/src/v3/schemas/schemas.ts (3 hunks)
  • packages/core/src/v3/types/queues.ts (1 hunks)
  • packages/core/src/v3/types/tasks.ts (2 hunks)
  • packages/trigger-sdk/package.json (1 hunks)
  • packages/trigger-sdk/src/v3/shared.ts (29 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/run-queue/types.ts
  • apps/webapp/app/v3/utils/durations.ts
  • internal-packages/run-engine/src/index.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • packages/core/src/v3/types/queues.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/run-queue/keyProducer.ts
  • internal-packages/run-engine/src/rate-limiter/gcra.ts
  • internal-packages/run-engine/src/engine/systems/enqueueSystem.ts
  • internal-packages/run-engine/src/rate-limiter/index.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • internal-packages/run-engine/src/run-queue/index.ts
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts
  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/schemas.ts
  • packages/trigger-sdk/src/v3/shared.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/run-queue/types.ts
  • apps/webapp/app/v3/utils/durations.ts
  • internal-packages/run-engine/src/index.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • packages/core/src/v3/types/queues.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/run-queue/keyProducer.ts
  • internal-packages/run-engine/src/rate-limiter/gcra.ts
  • internal-packages/run-engine/src/engine/systems/enqueueSystem.ts
  • internal-packages/run-engine/src/rate-limiter/index.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • internal-packages/run-engine/src/run-queue/index.ts
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts
  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/schemas.ts
  • packages/trigger-sdk/src/v3/shared.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/trigger-sdk/package.json
  • internal-packages/run-engine/src/run-queue/types.ts
  • apps/webapp/app/v3/utils/durations.ts
  • internal-packages/run-engine/src/index.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • packages/core/src/v3/types/queues.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/run-queue/keyProducer.ts
  • internal-packages/run-engine/src/rate-limiter/gcra.ts
  • internal-packages/run-engine/src/engine/systems/enqueueSystem.ts
  • internal-packages/run-engine/src/rate-limiter/index.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • internal-packages/run-engine/src/run-queue/index.ts
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts
  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/schemas.ts
  • packages/trigger-sdk/src/v3/shared.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptive describe and it blocks
Avoid mocks or stubs in tests; use helpers from @internal/testcontainers when Redis or Postgres are needed
Use vitest for unit tests

Files:

  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/v3/utils/durations.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/__tests__/rateLimit.test.ts
  • packages/core/src/v3/types/queues.ts
  • packages/core/src/v3/types/tasks.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • packages/core/src/v3/schemas/schemas.ts
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/v3/utils/durations.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/v3/utils/durations.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
  • apps/webapp/test/parseDurationToMs.test.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
apps/webapp/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Test files should only import classes and functions from app/**/*.ts files and should not import env.server.ts directly or indirectly; pass configuration through options instead

Files:

  • apps/webapp/test/parseDurationToMs.test.ts
apps/webapp/app/v3/services/**/*.server.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Organize services in the webapp following the pattern app/v3/services/*/*.server.ts

Files:

  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
packages/trigger-sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Files:

  • packages/trigger-sdk/src/v3/shared.ts
🧠 Learnings (48)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Control concurrency using the `queue` property with `concurrencyLimit` option
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Control concurrency using the `queue` property with `concurrencyLimit` option

Applied to files:

  • internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/run-engine/src/run-queue/types.ts
  • internal-packages/run-engine/src/index.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts
  • packages/core/src/v3/types/queues.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/run-queue/keyProducer.ts
  • .changeset/forty-houses-doubt.md
  • internal-packages/run-engine/src/engine/systems/enqueueSystem.ts
  • apps/webapp/app/v3/runQueue.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • internal-packages/run-engine/src/run-queue/index.ts
  • internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts
  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/schemas.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
  • packages/trigger-sdk/package.json
  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Subscribe to run updates using `runs.subscribeToRun()` for realtime monitoring of task execution

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/run-engine/src/engine/index.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify runtime environment (node or bun) in trigger.config.ts using the `runtime` property

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • .changeset/forty-houses-doubt.md
  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Limit task duration using the `maxDuration` property (in seconds)

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • .changeset/forty-houses-doubt.md
  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • .changeset/forty-houses-doubt.md
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.trigger()` to trigger a task from inside another task with specified payload

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/types/tasks.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/trigger-sdk/package.json
  • packages/core/src/v3/types/tasks.ts
  • .changeset/forty-houses-doubt.md
  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • .changeset/forty-houses-doubt.md
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • internal-packages/run-engine/src/engine/index.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/types/tasks.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The SDK at packages/trigger-sdk is an isomorphic TypeScript SDK

Applied to files:

  • packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings

Applied to files:

  • packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path

Applied to files:

  • packages/trigger-sdk/package.json
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • packages/trigger-sdk/package.json
  • apps/webapp/app/env.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • .changeset/forty-houses-doubt.md
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Use the Run Engine 2.0 from `internal/run-engine` for new run lifecycle code in the webapp instead of the legacy run engine

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp

Applied to files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
📚 Learning: 2025-08-19T09:49:07.011Z
Learnt from: julienvanbeveren
Repo: triggerdotdev/trigger.dev PR: 2417
File: apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts:56-61
Timestamp: 2025-08-19T09:49:07.011Z
Learning: In the Trigger.dev codebase, environment variables should default to `isSecret: false` when not explicitly marked as secrets in the syncEnvVars functionality. This is the intended behavior for both regular variables and parent variables.

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-10T09:09:07.399Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2663
File: apps/webapp/app/env.server.ts:1205-1206
Timestamp: 2025-11-10T09:09:07.399Z
Learning: In the trigger.dev webapp, S2_ACCESS_TOKEN and S2_DEPLOYMENT_LOGS_BASIN_NAME environment variables must remain optional until an OSS version of S2 is available, to avoid breaking environments that don't have S2 provisioned.

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:27:48.109Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-27T16:27:48.109Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vitest for unit tests

Applied to files:

  • apps/webapp/test/parseDurationToMs.test.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Use vitest for all tests in the Trigger.dev repository

Applied to files:

  • apps/webapp/test/parseDurationToMs.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerAndWait()` to batch trigger multiple different tasks and wait for results

Applied to files:

  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTriggerAndWait()` to batch trigger tasks and wait for all results from a parent task

Applied to files:

  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerByTaskAndWait()` to batch trigger tasks by passing task instances and wait for results

Applied to files:

  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTrigger()` to trigger multiple runs of a task from inside another task

Applied to files:

  • apps/webapp/app/v3/services/enqueueRun.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerByTask()` to batch trigger tasks by passing task instances for static task sets

Applied to files:

  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/types/tasks.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Configure task retry behavior using the `retry` property with options like maxAttempts, factor, and timeout values

Applied to files:

  • packages/core/src/v3/types/tasks.ts
  • packages/core/src/v3/schemas/schemas.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Specify machine resources using the `machine` property with preset options like 'large-1x'

Applied to files:

  • packages/core/src/v3/types/tasks.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach cron schedules declaratively using the `cron` property or imperatively using `schedules.create()`

Applied to files:

  • .changeset/forty-houses-doubt.md
  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use metadata methods (set, del, replace, append, remove, increment, decrement, stream, flush) to update metadata during task execution

Applied to files:

  • .changeset/forty-houses-doubt.md
  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks

Applied to files:

  • .changeset/forty-houses-doubt.md
📚 Learning: 2025-11-14T16:03:06.917Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2681
File: apps/webapp/app/services/platform.v3.server.ts:258-302
Timestamp: 2025-11-14T16:03:06.917Z
Learning: In `apps/webapp/app/services/platform.v3.server.ts`, the `getDefaultEnvironmentConcurrencyLimit` function intentionally throws an error (rather than falling back to org.maximumConcurrencyLimit) when the billing client returns undefined plan limits. This fail-fast behavior prevents users from receiving more concurrency than their plan entitles them to. The org.maximumConcurrencyLimit fallback is only for self-hosted deployments where no billing client exists.

Applied to files:

  • apps/webapp/app/v3/runQueue.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.trigger()` to trigger multiple different tasks at once from backend code

Applied to files:

  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Use `trigger.dev/redis-worker` for background job and worker system needs in the webapp and run engine

Applied to files:

  • apps/webapp/app/v3/services/createBackgroundWorker.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure global retry settings in trigger.config.ts using `retries` object with defaults for all tasks

Applied to files:

  • packages/core/src/v3/schemas/schemas.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.triggerAndWait()` to trigger a task and wait for its result from a parent task

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `runs.subscribeToBatch()` to subscribe to changes for all runs in a batch

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
🧬 Code graph analysis (12)
internal-packages/run-engine/src/run-queue/tests/keyProducer.test.ts (1)
internal-packages/run-engine/src/run-queue/keyProducer.ts (1)
  • queueKey (101-127)
internal-packages/run-engine/src/run-queue/types.ts (1)
packages/trigger-sdk/src/v3/shared.ts (1)
  • queue (131-149)
apps/webapp/app/v3/utils/durations.ts (1)
apps/webapp/app/v3/runQueue.server.ts (1)
  • parseDurationToMs (6-6)
apps/webapp/app/v3/runEngine.server.ts (2)
apps/webapp/app/env.server.ts (1)
  • env (1285-1285)
apps/supervisor/src/env.ts (1)
  • env (119-119)
apps/webapp/test/parseDurationToMs.test.ts (1)
apps/webapp/app/v3/utils/durations.ts (1)
  • parseDurationToMs (5-31)
packages/core/src/v3/schemas/__tests__/rateLimit.test.ts (1)
packages/core/src/v3/schemas/schemas.ts (2)
  • DurationStringSchema (106-106)
  • QueueRateLimitConfigSchema (110-114)
internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts (3)
internal-packages/run-engine/src/run-queue/keyProducer.ts (1)
  • RunQueueFullKeyProducer (24-394)
internal-packages/database/src/transaction.ts (1)
  • Decimal (21-21)
internal-packages/testcontainers/src/index.ts (2)
  • redisTest (167-167)
  • redisContainer (116-130)
internal-packages/run-engine/src/run-queue/keyProducer.ts (1)
internal-packages/run-engine/src/run-queue/types.ts (1)
  • RunQueueKeyProducerEnvironment (50-53)
apps/webapp/app/v3/services/createBackgroundWorker.server.ts (3)
internal-packages/run-engine/src/rate-limiter/index.ts (2)
  • QueueRateLimitConfig (7-7)
  • parseDurationToMs (4-4)
apps/webapp/app/v3/runQueue.server.ts (3)
  • QueueRateLimitConfig (11-18)
  • parseDurationToMs (6-6)
  • updateQueueRateLimitConfig (60-66)
apps/webapp/app/v3/utils/durations.ts (1)
  • parseDurationToMs (5-31)
internal-packages/run-engine/src/run-queue/index.ts (5)
internal-packages/run-engine/src/index.ts (2)
  • QueueRateLimitConfig (29-29)
  • configToGCRAParams (25-25)
internal-packages/run-engine/src/rate-limiter/gcra.ts (2)
  • QueueRateLimitConfig (6-13)
  • configToGCRAParams (117-124)
internal-packages/run-engine/src/rate-limiter/index.ts (2)
  • QueueRateLimitConfig (7-7)
  • configToGCRAParams (3-3)
apps/webapp/app/v3/runQueue.server.ts (1)
  • QueueRateLimitConfig (11-18)
packages/core/src/v3/schemas/schemas.ts (1)
  • QueueRateLimitConfig (116-116)
internal-packages/run-engine/src/run-queue/tests/enqueueMessage.test.ts (3)
internal-packages/testcontainers/src/index.ts (3)
  • redisTest (167-167)
  • redisContainer (116-130)
  • assertNonNullable (19-19)
internal-packages/run-engine/src/run-queue/types.ts (2)
  • InputPayload (5-17)
  • InputPayload (18-18)
internal-packages/testcontainers/src/utils.ts (1)
  • assertNonNullable (173-176)
packages/core/src/v3/schemas/schemas.ts (2)
internal-packages/run-engine/src/index.ts (1)
  • QueueRateLimitConfig (29-29)
apps/webapp/app/v3/runQueue.server.ts (1)
  • QueueRateLimitConfig (11-18)
🪛 LanguageTool
.changeset/forty-houses-doubt.md

[grammar] ~6-~6: Use a hyphen to join words.
Context: ...gger.dev/core": minor --- Add GCRA rate limiting algorithm for task queue manage...

(QB_NEW_EN_HYPHEN)

@vibe-kanban-cloud
Copy link

Vibe Kanban Review Complete

Your code review is ready!

View Review

@guillermolg00 guillermolg00 changed the title Feat/rate limit Implement queue rate limiting with GCRA algorithm' Dec 18, 2025
@guillermolg00 guillermolg00 changed the title Implement queue rate limiting with GCRA algorithm' Implement queue rate limiting with GCRA algorithm Dec 18, 2025
@guillermolg00
Copy link
Author

guillermolg00 commented Dec 18, 2025

Declarative GCRA Rate Limiting for Queue Processing

Context & Need

Previously, rate limiting in Trigger.dev required manual implementation within task code. This approach had several limitations:

  • Developer burden: Each task needed custom rate limiting logic
  • Inconsistent enforcement: Rate limits could be bypassed or implemented incorrectly
  • Resource waste: Tasks would start, hit rate limits, and fail—consuming compute unnecessarily
  • No queue-level control: Impossible to throttle entire queues declaratively

Goal: Enable declarative rate limiting at the queue level, enforced before task execution begins.


Solution Overview

Implemented GCRA (Generic Cell Rate Algorithm) directly in the Redis dequeue operation via Lua scripting. This ensures:

  • Atomic enforcement — Rate limit check + message handling in a single Redis operation
  • Zero wasted compute — Rate-limited messages never leave the queue
  • Declarative config — Define rate limits in task/queue definitions
  • Per-key buckets — Support for tenant-specific rate limiting via rateLimitKey at trigger time

Architecture

flowchart TB
    subgraph SDK["@trigger.dev/sdk"]
        TaskDef["Task Definition\nqueue: { rateLimit: { limit, period } }"]
        TriggerOpts["Trigger Options\n{ rateLimitKey: 'tenant-123' }"]
    end

    subgraph CLI["trigger.dev CLI"]
        Manifest["Queue Manifest\nwith rateLimit config"]
    end

    subgraph Webapp["apps/webapp"]
        Sync["createBackgroundWorker.server.ts"]
        RunQueue1["runQueue.server.ts"]
        EnvVar["TRIGGER_DISABLE_QUEUE_RATE_LIMITS"]
    end

    subgraph RunEngine["@internal/run-engine"]
        direction TB
        GCRA["gcra.ts\nconfigToGCRAParams()"]
        RunQueue2["RunQueue"]
        LuaScript["Lua Script\ndequeueMessagesFromQueue"]
    end

    subgraph Redis["Redis"]
        QueueData["Sorted Set\n{queue}:messages"]
        RLConfig["Hash\n{queue}:rl:config"]
        RLBucket["String\n{queue}:rl:{rateLimitKey}"]
        Concurrency["Concurrency Keys"]
    end

    TaskDef --> Manifest
    TriggerOpts --> RunQueue1
    Manifest --> Sync
    Sync --> GCRA
    GCRA --> RLConfig
    EnvVar -.->|"disable flag"| RunQueue2
    RunQueue1 --> RunQueue2
    RunQueue2 --> LuaScript
    LuaScript --> QueueData
    LuaScript --> RLConfig
    LuaScript --> RLBucket
    LuaScript -.->|"if allowed"| Concurrency
Loading

Dequeue Flow with Rate Limiting

sequenceDiagram
    participant W as Worker
    participant RQ as RunQueue
    participant Lua as Lua Script
    participant Redis as Redis

    W->>RQ: dequeueFromMasterQueue()
    RQ->>Lua: EVALSHA dequeueMessagesFromQueue
    
    loop For each message in queue
        Lua->>Redis: ZRANGEBYSCORE (get eligible messages)
        Lua->>Redis: GET {queue}:rl:config
        
        alt Rate limit config exists & not disabled
            Lua->>Lua: Extract rateLimitKey from message
            Lua->>Lua: gcra_check(bucket, emission, burst)
            Lua->>Redis: GET/SET bucket TAT
            
            alt GCRA allows
                Lua->>Redis: Update concurrency
                Lua->>Redis: ZREM from queue
                Lua-->>RQ: Return message
            else Rate limited
                Lua->>Lua: Calculate retryAfter + jitter
                Lua->>Redis: ZADD with new score (delayed)
                Note over Lua: Skip concurrency increment
            end
        else No rate limit
            Lua->>Redis: Update concurrency
            Lua->>Redis: ZREM from queue
            Lua-->>RQ: Return message
        end
    end
    
    RQ-->>W: Dequeued messages
Loading

GCRA Algorithm Details

The Generic Cell Rate Algorithm uses a "virtual scheduling" approach:

┌─────────────────────────────────────────────────────────────┐
│  GCRA Parameters                                            │
├─────────────────────────────────────────────────────────────┤
│  emissionInterval = period / limit                          │
│  burstTolerance   = (burst - 1) × emissionInterval          │
│  TAT              = Theoretical Arrival Time (stored state) │
└─────────────────────────────────────────────────────────────┘

Check Logic:
  newTAT = max(currentTime, lastTAT) + emissionInterval
  
  if newTAT - currentTime > burstTolerance:
      DENY (retryAfter = newTAT - burstTolerance - currentTime)
  else:
      ALLOW (update TAT = newTAT)

Example: { limit: 10, period: "1m", burst: 3 }

  • emissionInterval = 60000ms / 10 = 6000ms (one request every 6 seconds average)
  • burstTolerance = (3-1) × 6000ms = 12000ms (can burst 3 requests instantly)

Test Coverage

New test file: internal-packages/run-engine/src/run-queue/tests/rateLimit.test.ts

Test Case Description
Basic rate limiting Enforces limit, re-scores excess messages
Disabled rate limiting disableRateLimits: true bypasses checks
Per-key rate limiting Separate buckets per rateLimitKey
No config fallback Messages dequeue normally without config
Concurrency isolation Rate-limited messages don't increment concurrency

Configuration

Queue-level rate limit

export const myTask = task({
  id: "my-task",
  queue: {
    name: "my-queue",
    rateLimit: {
      limit: 100,
      period: "1m",
      burst: 10 // optional, defaults to 1
    }
  },
  run: async (payload) => { /* ... */ }
});

Per-tenant rate limiting (at trigger time)

// Using task instance
await myTask.trigger(payload, { 
  rateLimitKey: `tenant-${tenantId}` 
});

// Using tasks.trigger() - works consistently
await tasks.trigger("my-task", payload, { 
  rateLimitKey: `tenant-${tenantId}` 
});

// Batch triggering with per-item keys
await myTask.batchTrigger([
  { payload: item1, options: { rateLimitKey: "tenant-A" } },
  { payload: item2, options: { rateLimitKey: "tenant-B" } },
]);

Environment variable

# Disable all queue rate limits (useful for testing/debugging)
TRIGGER_DISABLE_QUEUE_RATE_LIMITS=1

Key Design Decisions

  1. Lua-inline GCRA: Avoids extra Redis round-trips; atomic check + action
  2. Pre-calculated params: configToGCRAParams() runs at sync time, not dequeue time
  3. Key prefix handling: Lua script prepends keyPrefix to rate limit keys for ioredis compatibility
  4. Jitter on re-score: Prevents thundering herd when rate-limited messages retry
  5. No concurrency increment for rate-limited: Prevents "phantom" concurrency consumption
  6. rateLimitKey at trigger time: Allows per-tenant/per-user rate limiting consistently across all trigger methods (task.trigger(), tasks.trigger(), batch operations)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72c8ce0 and 6028065.

📒 Files selected for processing (3)
  • packages/core/src/v3/types/queues.ts (1 hunks)
  • packages/core/src/v3/types/tasks.ts (2 hunks)
  • packages/trigger-sdk/src/v3/shared.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/v3/types/tasks.ts
  • packages/core/src/v3/types/queues.ts
🧰 Additional context used
📓 Path-based instructions (4)
packages/trigger-sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Files:

  • packages/trigger-sdk/src/v3/shared.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • packages/trigger-sdk/src/v3/shared.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • packages/trigger-sdk/src/v3/shared.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • packages/trigger-sdk/src/v3/shared.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Control concurrency using the `queue` property with `concurrencyLimit` option
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Control concurrency using the `queue` property with `concurrencyLimit` option

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use metadata methods (set, del, replace, append, remove, increment, decrement, stream, flush) to update metadata during task execution

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerByTask()` to batch trigger tasks by passing task instances for static task sets

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.trigger()` to trigger multiple different tasks at once from backend code

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
🧬 Code graph analysis (1)
packages/trigger-sdk/src/v3/shared.ts (1)
packages/core/src/v3/resource-catalog-api.ts (1)
  • resourceCatalog (5-5)
🔇 Additional comments (3)
packages/trigger-sdk/src/v3/shared.ts (3)

131-142: LGTM! Rate limit added to queue registration.

The rateLimit parameter is correctly added to queue metadata registration, following the same pattern as concurrencyLimit.


249-257: LGTM! Consistent queue metadata registration.

Both createTask and createSchemaTask correctly propagate rateLimit from the queue configuration to the metadata registration, ensuring consistent handling across different task creation methods.

Also applies to: 382-390


131-142: Well-structured rate limiting integration.

The changes follow existing patterns and maintain consistency across the codebase. The implementation:

  • Uses optional chaining appropriately to handle undefined values
  • Follows the same pattern as existing features like concurrencyKey
  • Adheres to SDK coding guidelines (isomorphic code, function declarations, types over interfaces)
  • Maintains backward compatibility by making rate limit features optional

Also applies to: 249-257, 382-390, 1955-1955, 2009-2009, 2050-2050, 2129-2129, 2283-2283, 2377-2377

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/trigger-sdk/src/v3/shared.ts (4)

636-660: Add missing rateLimitKey to batch-by-id array path.

The array path for batchTriggerById is missing rateLimitKey propagation from item.options?.rateLimitKey. Per-item rate limit keys are supported by the BatchByIdItem type (via TriggerOptions) but not forwarded to the server.

🔎 Apply this diff to add the missing field:
         return {
           index,
           task: item.id,
           payload: payloadPacket.data,
           options: {
             queue: item.options?.queue ? { name: item.options.queue } : undefined,
             concurrencyKey: item.options?.concurrencyKey,
+            rateLimitKey: item.options?.rateLimitKey,
             test: taskContext.ctx?.run.isTest,
             payloadType: payloadPacket.dataType,
             delay: item.options?.delay,

892-915: Add missing rateLimitKey to batch-by-id-and-wait array path.

The array path for batchTriggerByIdAndWait is missing rateLimitKey propagation from item.options?.rateLimitKey.

🔎 Apply this diff to add the missing field:
         return {
           index,
           task: item.id,
           payload: payloadPacket.data,
           options: {
             lockToVersion: taskContext.worker?.version,
             queue: item.options?.queue ? { name: item.options.queue } : undefined,
             concurrencyKey: item.options?.concurrencyKey,
+            rateLimitKey: item.options?.rateLimitKey,
             test: taskContext.ctx?.run.isTest,
             payloadType: payloadPacket.dataType,

1152-1176: Add missing rateLimitKey to batch-by-task array path.

The array path for batchTriggerTasks is missing rateLimitKey propagation from item.options?.rateLimitKey. Per-item rate limit keys are supported by the BatchByTaskItem type but not forwarded.

🔎 Apply this diff to add the missing field:
         return {
           index,
           task: item.task.id,
           payload: payloadPacket.data,
           options: {
             queue: item.options?.queue ? { name: item.options.queue } : undefined,
             concurrencyKey: item.options?.concurrencyKey,
+            rateLimitKey: item.options?.rateLimitKey,
             test: taskContext.ctx?.run.isTest,
             payloadType: payloadPacket.dataType,

1413-1437: Add missing rateLimitKey to batch-by-task-and-wait array path.

The array path for batchTriggerAndWaitTasks is missing rateLimitKey propagation from item.options?.rateLimitKey.

🔎 Apply this diff to add the missing field:
         return {
           index,
           task: item.task.id,
           payload: payloadPacket.data,
           options: {
             lockToVersion: taskContext.worker?.version,
             queue: item.options?.queue ? { name: item.options.queue } : undefined,
             concurrencyKey: item.options?.concurrencyKey,
+            rateLimitKey: item.options?.rateLimitKey,
             test: taskContext.ctx?.run.isTest,
             payloadType: payloadPacket.dataType,
♻️ Duplicate comments (4)
packages/trigger-sdk/src/v3/shared.ts (4)

1749-1773: Add missing rateLimitKey to batch-by-id streaming path.

The streaming transform for batchTriggerById is missing rateLimitKey propagation from item.options?.rateLimitKey, preventing per-item rate limiting in streamed batch-by-id operations.

🔎 Apply this diff to add the missing field:
     yield {
       index: index++,
       task: item.id,
       payload: payloadPacket.data,
       options: {
         queue: item.options?.queue ? { name: item.options.queue } : undefined,
         concurrencyKey: item.options?.concurrencyKey,
+        rateLimitKey: item.options?.rateLimitKey,
         test: taskContext.ctx?.run.isTest,
         payloadType: payloadPacket.dataType,

1801-1825: Add missing rateLimitKey to batch-by-id-and-wait streaming path.

The streaming transform for batchTriggerByIdAndWait is missing rateLimitKey propagation from item.options?.rateLimitKey.

🔎 Apply this diff to add the missing field:
     yield {
       index: index++,
       task: item.id,
       payload: payloadPacket.data,
       options: {
         lockToVersion: taskContext.worker?.version,
         queue: item.options?.queue ? { name: item.options.queue } : undefined,
         concurrencyKey: item.options?.concurrencyKey,
+        rateLimitKey: item.options?.rateLimitKey,
         test: taskContext.ctx?.run.isTest,
         payloadType: payloadPacket.dataType,

1852-1876: Add missing rateLimitKey to batch-by-task streaming path.

The streaming transform for batchTriggerTasks is missing rateLimitKey propagation from item.options?.rateLimitKey.

🔎 Apply this diff to add the missing field:
     yield {
       index: index++,
       task: item.task.id,
       payload: payloadPacket.data,
       options: {
         queue: item.options?.queue ? { name: item.options.queue } : undefined,
         concurrencyKey: item.options?.concurrencyKey,
+        rateLimitKey: item.options?.rateLimitKey,
         test: taskContext.ctx?.run.isTest,
         payloadType: payloadPacket.dataType,

1903-1927: Add missing rateLimitKey to batch-by-task-and-wait streaming path.

The streaming transform for batchTriggerAndWaitTasks is missing rateLimitKey propagation from item.options?.rateLimitKey.

🔎 Apply this diff to add the missing field:
     yield {
       index: index++,
       task: item.task.id,
       payload: payloadPacket.data,
       options: {
         lockToVersion: taskContext.worker?.version,
         queue: item.options?.queue ? { name: item.options.queue } : undefined,
         concurrencyKey: item.options?.concurrencyKey,
+        rateLimitKey: item.options?.rateLimitKey,
         test: taskContext.ctx?.run.isTest,
         payloadType: payloadPacket.dataType,
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6028065 and 0beb5f8.

📒 Files selected for processing (9)
  • apps/webapp/app/env.server.ts (1 hunks)
  • apps/webapp/app/runEngine/services/triggerTask.server.ts (1 hunks)
  • apps/webapp/app/v3/runEngine.server.ts (1 hunks)
  • internal-packages/database/prisma/schema.prisma (1 hunks)
  • internal-packages/run-engine/src/engine/index.ts (5 hunks)
  • internal-packages/run-engine/src/engine/types.ts (2 hunks)
  • packages/core/src/v3/schemas/api.ts (2 hunks)
  • packages/core/src/v3/types/tasks.ts (2 hunks)
  • packages/trigger-sdk/src/v3/shared.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • packages/core/src/v3/schemas/api.ts
  • internal-packages/run-engine/src/engine/types.ts
  • packages/core/src/v3/types/tasks.ts
  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/database/prisma/schema.prisma
  • apps/webapp/app/env.server.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • apps/webapp/app/v3/runEngine.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/v3/runEngine.server.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • apps/webapp/app/v3/runEngine.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/v3/runEngine.server.ts
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/v3/runEngine.server.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • apps/webapp/app/v3/runEngine.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
packages/trigger-sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Files:

  • packages/trigger-sdk/src/v3/shared.ts
🧠 Learnings (21)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Control concurrency using the `queue` property with `concurrencyLimit` option
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Control concurrency using the `queue` property with `concurrencyLimit` option

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Use the Run Engine 2.0 from `internal/run-engine` for new run lifecycle code in the webapp instead of the legacy run engine

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify runtime environment (node or bun) in trigger.config.ts using the `runtime` property

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use metadata methods (set, del, replace, append, remove, increment, decrement, stream, flush) to update metadata during task execution

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `.withStreams()` to subscribe to realtime streams from task metadata in addition to run changes

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.trigger()` to trigger multiple different tasks at once from backend code

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerByTask()` to batch trigger tasks by passing task instances for static task sets

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerByTaskAndWait()` to batch trigger tasks by passing task instances and wait for results

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `batch.triggerAndWait()` to batch trigger multiple different tasks and wait for results

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTrigger()` to trigger multiple runs of a task from inside another task

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTriggerAndWait()` to batch trigger tasks and wait for all results from a parent task

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `runs.subscribeToBatch()` to subscribe to changes for all runs in a batch

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
🧬 Code graph analysis (1)
packages/trigger-sdk/src/v3/shared.ts (1)
packages/core/src/v3/resource-catalog-api.ts (1)
  • resourceCatalog (5-5)
🔇 Additional comments (3)
apps/webapp/app/v3/runEngine.server.ts (1)

83-83: LGTM: Rate limit disable flag properly configured.

The flag correctly reads from the environment variable and follows the webapp pattern for boolean environment variable conversion.

packages/trigger-sdk/src/v3/shared.ts (2)

132-136: LGTM: Queue rate limit metadata properly registered.

The rateLimit field is consistently added to queue metadata registration across all task creation paths.

Also applies to: 255-255, 388-388


1963-1963: LGTM: Rate limit key properly propagated in single-task operations.

The rateLimitKey is correctly threaded through direct trigger operations and single-task batch operations (both array and streaming paths).

Also applies to: 2018-2018, 2060-2060, 2140-2140, 2294-2294, 2389-2389

@guillermolg00 guillermolg00 marked this pull request as draft December 18, 2025 18:36
@guillermolg00 guillermolg00 marked this pull request as ready for review December 18, 2025 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants