New batch trigger system with larger payloads, streaming ingestion, larger batch sizes, and a fair processing system. This PR introduces a new `FairQueue` abstraction inspired by our own `RunQueue` that enables multi-tenant fair queueing with concurrency limits. The new `BatchQueue` is built on top of the `FairQueue`, and handles processing Batch triggers in a fair manner with per-environment concurrency limits defined per-org. Additionally, there is a global concurrency limit to prevent the BatchQueue system from creating too many runs too quickly, which can cause downstream issues. For this new BatchQueue system we have a completely new batch trigger creation and ingestion system. Previously this was a single endpoint with a single JSON body that defined details about the batch as well as all the items in the batch. We're introducing a two-phase batch trigger ingestion system. In the first phase, the BatchTaskRun record is created (and possibly rate limited). The second phase is another endpoint that accepts an NDJSON body with each line being a single item/run with payload and options. At ingestion time all items are added to a queue, in order, and then processed by the BatchQueue system. ## New batch trigger rate limits This PR implements a new batch trigger specific rate limit, configured on the `Organization.batchRateLimitConfig` column, and defaults using these environment variables: - `BATCH_RATE_LIMIT_REFILL_RATE` defaults to 10 - `BATCH_RATE_LIMIT_REFILL_INTERVAL` the duration interval, defaults to `"10s"` - `BATCH_RATE_LIMIT_MAX` defaults to 1200 This rate limiter is scoped to the environment ID and controls how many runs can be submitted via batch triggers per interval. The SDK handles the retrying side. ## Batch queue concurrency limits The new column `Organization.batchQueueConcurrencyConfig` now defines an org specific `processingConcurrency` value, with a backup of the env var `BATCH_CONCURRENCY_LIMIT_DEFAULT` which defaults to 10. This controls how many batch queue items are processed concurrently per environment. There is also a global rate limit for the batch queue set via the `BATCH_QUEUE_GLOBAL_RATE_LIMIT` which defaults to being disabled. If set, the entire batch queue system won't process more than `BATCH_QUEUE_GLOBAL_RATE_LIMIT` items per second. This allows controlling the maximum number of runs created per second via batch triggers. ## Batch trigger settings - `STREAMING_BATCH_MAX_ITEMS` controls the maximum number of items in a single batch - `STREAMING_BATCH_ITEM_MAXIMUM_SIZE` controls the maximum size of each item in a batch - `BATCH_CONCURRENCY_DEFAULT_CONCURRENCY` controls the default environment concurrency - `BATCH_QUEUE_DRR_QUANTUM` how many credits each environment gets each round for the DRR scheduler - `BATCH_QUEUE_MAX_DEFICIT` the maximum deficit for the DRR scheduler - `BATCH_QUEUE_CONSUMER_COUNT` how many queue consumers to run - `BATCH_QUEUE_CONSUMER_INTERVAL_MS` how frequently they poll for items in the queue ### Configuration Recommendations by Use Case **High-throughput priority (fairness acceptable at 0.98+):** ```env BATCH_QUEUE_DRR_QUANTUM=25 BATCH_QUEUE_MAX_DEFICIT=100 BATCH_QUEUE_CONSUMER_COUNT=10 BATCH_QUEUE_CONSUMER_INTERVAL_MS=50 BATCH_CONCURRENCY_DEFAULT_CONCURRENCY=25 ``` **Strict fairness priority (throughput can be lower):** ```env BATCH_QUEUE_DRR_QUANTUM=5 BATCH_QUEUE_MAX_DEFICIT=25 BATCH_QUEUE_CONSUMER_COUNT=3 BATCH_QUEUE_CONSUMER_INTERVAL_MS=100 BATCH_CONCURRENCY_DEFAULT_CONCURRENCY=5 ```
Trigger.dev References
Contains code that tests or uses the @trigger.dev/* packages in some way, either by using them to test out a framework adapter, an integration, or parts of the main SDK.
All the dependencies to the @trigger.dev/* packages will be both referenced in the package.json dependencies as workspace:*, as well as using a direct path from the tsconfig.json file like so:
{
"extends": "@trigger.dev/tsconfig/node18.json",
"include": ["./src/**/*.ts"],
"compilerOptions": {
"baseUrl": ".",
"lib": ["DOM", "DOM.Iterable"],
"paths": {
"@/*": ["./src/*"],
"@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
"@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
"@trigger.dev/express": ["../../packages/express/src/index"],
"@trigger.dev/express/*": ["../../packages/express/src/*"],
"@trigger.dev/core": ["../../packages/core/src/index"],
"@trigger.dev/core/*": ["../../packages/core/src/*"],
"@trigger.dev/integration-kit": ["../../packages/integration-kit/src/index"],
"@trigger.dev/integration-kit/*": ["../../packages/integration-kit/src/*"],
"@trigger.dev/github": ["../../integrations/github/src/index"],
"@trigger.dev/github/*": ["../../integrations/github/src/*"],
"@trigger.dev/slack": ["../../integrations/slack/src/index"],
"@trigger.dev/slack/*": ["../../integrations/slack/src/*"],
"@trigger.dev/openai": ["../../integrations/openai/src/index"],
"@trigger.dev/openai/*": ["../../integrations/openai/src/*"],
"@trigger.dev/resend": ["../../integrations/resend/src/index"],
"@trigger.dev/resend/*": ["../../integrations/resend/src/*"],
"@trigger.dev/typeform": ["../../integrations/typeform/src/index"],
"@trigger.dev/typeform/*": ["../../integrations/typeform/src/*"],
"@trigger.dev/plain": ["../../integrations/plain/src/index"],
"@trigger.dev/plain/*": ["../../integrations/plain/src/*"],
"@trigger.dev/supabase": ["../../integrations/supabase/src/index"],
"@trigger.dev/supabase/*": ["../../integrations/supabase/src/*"],
"@trigger.dev/stripe": ["../../integrations/stripe/src/index"],
"@trigger.dev/stripe/*": ["../../integrations/stripe/src/*"],
"@trigger.dev/sendgrid": ["../../integrations/sendgrid/src/index"],
"@trigger.dev/sendgrid/*": ["../../integrations/sendgrid/src/*"],
"@trigger.dev/airtable": ["../../integrations/airtable/src/index"],
"@trigger.dev/airtable/*": ["../../integrations/airtable/src/*"]
}
}
}
Creating a New Reference Project
This guide assumes that you have followed the Contributing.md instructions to set up a local trigger.dev instance. If not, please complete the setup before continuing.
Step-by-Step Instructions
- Run an HTTP tunnel: You will need to run an HTTP tunnel to expose your local webapp, it is required for some API calls during building the image to deploy on your local instance. This is optional if you do not plan to test deployment on your local instance.
- Download the ngrok CLI. This can be done by following the instructions on ngrok's website.
- Create an account on ngrok to obtain the authtoken and add it to the CLI.
ngrok config add-authtoken <your-auth-token>
Replace the with the token you obtain from ngrok.
- Run the tunnel.
ngrok http <your-app-port>
Replace the with the webapp port, default is 3030.
-
Add your tunnel URL to the env: After running the ngrok tunnel, you will see URL in your terminal, it will look something like
https://<your-tunnel-address>.ngrok-free.app. Replace theAPP_ORIGINvariable with this URL in your.envfile in the root of the trigger.dev project. -
Run the webapp on localhost:
pnpm run dev --filter webapp --filter coordinator --filter docker-provider
- Build the CLI in a new terminal window:
# Build the CLI
pnpm run build --filter trigger.dev
# Make it accessible to `pnpm exec`
pnpm i
- Set up a new project in the webapp:
- Open the webapp running on
localhost:3030. - Create a new project in the webapp UI.
- Go to the Project Settings page and copy the project reference id from there.
- Copy the hello-world project as a template:
cp -r references/hello-world references/<new-project>
Replace <new-project> with your desired project name.
- Update project details:
-
Open
<new-project>/package.jsonand change the name field. (Tip: Use the same name as in the webapp to avoid confusion.) -
Open
<new-project>/trigger.config.tsand update the project field with the project reference you copied from the webapp. -
Run
pnpm iin your<new-project>directory to sync the dependencies.
- Authorize the CLI for your project:
pnpm exec trigger login -a http://localhost:3030 --profile local
- Run the new project: You can now run your project using the CLI with the following command:
pnpm exec trigger dev --profile local
You can also deploy them against your local instance with the following command:
pnpm exec trigger deploy --self-hosted --load-image --profile local