Skip to content

fix(webapp): mollifier trigger-time decisions#3773

Closed
deepshekhardas wants to merge 15 commits into
triggerdotdev:mainfrom
deepshekhardas:pr/3753-fix
Closed

fix(webapp): mollifier trigger-time decisions#3773
deepshekhardas wants to merge 15 commits into
triggerdotdev:mainfrom
deepshekhardas:pr/3753-fix

Conversation

@deepshekhardas
Copy link
Copy Markdown

Summary

Phase 2 of the mollifier rollout — move the divert decision from the drainer (dequeue time) to the call site (trigger time). When the gate returns \mollify, the trigger pipeline writes a canonical engine.trigger snapshot into the Redis buffer and returns a synthesised HTTP 200 to the customer immediately. The materialised TaskRun row is created later when the drainer replays the snapshot through engine.trigger.

Key changes

triggerTask.server.ts

  • Gate evaluation moved inside the traceRun span (so mollified runs get a ClickHouse PARTIAL event immediately)
  • #buildEngineTriggerInput()\ extracted to share engine.trigger input shape between mollify and pass-through paths
  • Phase-1 dual-write (buffer.accept + engine.trigger) replaced with \mollifyTrigger()\ — writes buffer, returns synthetic result, never calls engine.trigger
  • Idempotency claim ownership threaded through the pipeline (publish on success, release on error)

Test changes

  • MockTraceEventConcern produces realistic W3C traceparent values
  • Mollify test now asserts synthetic result with no Postgres row
  • Removed orphan-entry tests (no longer applicable — mollify path does not call engine.trigger)
  • Removed debounce-match orphan test (bypassed at the gate level)

Fixes #3753

Deploy Bot and others added 15 commits February 2, 2026 16:16
- Include reproduction scripts for Sentry (triggerdotdev#2900) and engine strictness (triggerdotdev#2913)
- Include PR body drafts for consolidated tracking
- Include reproduction scripts for Sentry (triggerdotdev#2900) and engine strictness (triggerdotdev#2913)
- Include PR body drafts for consolidated tracking
Phase 2 of the mollifier rollout — move the divert decision from the
drainer (dequeue time) to the call site (trigger time). When the gate
returns mollify, the trigger pipeline writes a canonical engine.trigger
snapshot into the Redis buffer and returns a synthesised HTTP 200 to the
customer immediately. The materialised TaskRun row is created later when
the drainer replays the snapshot through engine.trigger.

Call-site changes
------------------
- Evaluate the mollifier gate inside the traceRun span instead of before
  it, so the mollified run gets a ClickHouse PARTIAL event immediately.
- Extract #buildEngineTriggerInput() so the same engine.trigger shape is
  shared between the mollify (buffer snapshot) and pass-through paths.
- Replace the Phase-1 dual-write (buffer.accept + engine.trigger) with
  mollifyTrigger(), which writes the buffer and returns a synthetic
  result. engine.trigger is no longer called on the mollify path.
- Thread idempotency claim ownership through the call pipeline: publish
  the claim with the synthesised runId on success, release on error.

Test changes
-------------
- MockTraceEventConcern now produces realistic W3C traceparent values.
- mollify dual-write test replaced with synthetic-result + no-PG test.
- Removed orphan-entry and debounce-match orphan tests.

Drive-by: MollifierBuffer no longer accepts entryTtlSeconds (the
sliding-window TTL is now managed internally by the buffer class).
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 29, 2026

🦋 Changeset detected

Latest commit: f870f30

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

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

@github-actions
Copy link
Copy Markdown
Contributor

Hi @deepshekhardas, thanks for your interest in contributing!

This project requires that pull request authors are vouched, and you are not in the list of vouched users.

This PR will be closed automatically. See https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md for more details.

@github-actions github-actions Bot closed this May 29, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9840b0d3-cc0b-4c9a-9b28-d5847af47951

📥 Commits

Reviewing files that changed from the base of the PR and between c043c4a and f870f30.

📒 Files selected for processing (34)
  • .changeset/fix-console-interceptor-2900.md
  • .changeset/fix-docker-hub-rate-limit-2911.md
  • .changeset/fix-github-install-node-version-2913.md
  • .changeset/fix-orphaned-workers-2909.md
  • .changeset/fix-sentry-oom-2920.md
  • .server-changes/mollifier-trigger.md
  • apps/webapp/app/runEngine/concerns/idempotencyKeys.server.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/v3/mollifier/idempotencyClaim.server.ts
  • apps/webapp/app/v3/mollifier/mollifierGate.server.ts
  • apps/webapp/app/v3/mollifier/mollifierMollify.server.ts
  • apps/webapp/app/v3/mollifier/readFallback.server.ts
  • apps/webapp/test/engine/triggerTask.test.ts
  • apps/webapp/test/mollifierClaimResolution.test.ts
  • apps/webapp/test/mollifierGate.test.ts
  • apps/webapp/test/mollifierIdempotencyClaim.test.ts
  • apps/webapp/test/mollifierMollify.test.ts
  • apps/webapp/test/mollifierReadFallback.test.ts
  • apps/webapp/test/mollifierTripEvaluator.test.ts
  • consolidated_pr_body.md
  • packages/cli-v3/src/cli/common.ts
  • packages/cli-v3/src/commands/deploy.ts
  • packages/cli-v3/src/commands/dev.ts
  • packages/cli-v3/src/commands/login.ts
  • packages/cli-v3/src/commands/update.test.ts
  • packages/cli-v3/src/commands/update.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/utilities/sourceMaps.test.ts
  • packages/cli-v3/src/utilities/sourceMaps.ts
  • packages/core/src/v3/consoleInterceptor.ts

Walkthrough

This PR implements a major mollifier (burst buffer) feature for trigger idempotency alongside multiple infrastructure and reliability fixes. The mollifier integration adds gate-based bypass logic, Redis-backed claim coordination, buffer-backed synthetic run synthesis, and claim lifecycle management integrated into the trigger task service and idempotency concern. Supporting changes include environment-driven source map support refactoring across workers, SIGINT/SIGTERM handlers in the dev command, Docker Hub authentication for build images, engine-check bypass for deployments, console interceptor restoration to preserve other interceptor chains, and comprehensive test coverage for all new behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

@@ -260,8 +261,7 @@ export async function updateTriggerPackages(
await installDependencies({ cwd: projectPath, silent: true });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 ignoreEngines option accepted but never passed to installDependencies

The ignoreEngines flag is added to CommonCommandOptions and UpdateCommandOptions, and deploy.ts:262 passes { ...options, ignoreEngines: true } to updateTriggerPackages. However, the installDependencies call at packages/cli-v3/src/commands/update.ts:261 is await installDependencies({ cwd: projectPath, silent: true }) — it never reads options.ignoreEngines and never passes engine-ignoring flags (--no-engine-strict for npm, --config.engine-strict=false for pnpm, --ignore-engines for yarn) to the package manager. The tests in packages/cli-v3/src/commands/update.test.ts:74-112 expect these args to be present, so they will fail. The fix described in changeset fix-github-install-node-version-2913.md ("Ignore engine checks during deployment install phase") is completely non-functional — deployments on build servers with Node version mismatches will still fail.

Prompt for agents
The `installDependencies` call at line 261 of `packages/cli-v3/src/commands/update.ts` needs to read `options.ignoreEngines` and pass the appropriate engine-strict-ignoring flag as `args` to `installDependencies`. The flag differs per package manager:

- npm: `--no-engine-strict`
- pnpm: `--config.engine-strict=false`
- yarn: `--ignore-engines`

The `packageManager` variable is already detected at line 254 via `detectPackageManager(projectPath)`. The fix should:
1. Build an `args` array based on `options.ignoreEngines` and the detected `packageManager.name`.
2. Pass `args` to the `installDependencies` call: `await installDependencies({ cwd: projectPath, silent: true, args })`.

The test file `packages/cli-v3/src/commands/update.test.ts` already validates the expected behavior for npm, pnpm, and yarn.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +97 to +100
switch (severityNumber) {
case SeverityNumber.INFO:
this.originalConsole.log(...args);
break;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 console.info() delegates to originalConsole.log() instead of originalConsole.info()

Both log() and info() methods emit SeverityNumber.INFO, and the switch in #handleLog maps SeverityNumber.INFO to this.originalConsole.log(). This means when the user calls console.info("x"), it's intercepted and forwarded to the original console.log("x") instead of the original console.info("x"). Sentry (the explicit motivation in changeset fix-console-interceptor-2900.md) patches console.info and console.log separately, classifying them as different breadcrumb levels. Routing info through log means Sentry records them at the wrong level, partially undermining the stated fix of "preserving the log chain when other interceptors (like Sentry) are present".

Prompt for agents
The switch statement in `#handleLog` at `packages/core/src/v3/consoleInterceptor.ts` dispatches on `severityNumber`, but both `log()` and `info()` use `SeverityNumber.INFO`. To preserve the console.info vs console.log distinction for downstream interceptors like Sentry, there are two approaches:

1. Use `severityText` (which is "Log" vs "Info") as a secondary discriminator within the `SeverityNumber.INFO` case to choose between `this.originalConsole.log()` and `this.originalConsole.info()`.

2. Use different severity numbers for log vs info (e.g., INFO for info, INFO2 or a custom constant for log).

Approach 1 is simpler and backward-compatible:
case SeverityNumber.INFO:
  if (severityText === "Info") {
    this.originalConsole.info(...args);
  } else {
    this.originalConsole.log(...args);
  }
  break;

This requires threading `severityText` into the `#handleLog` dispatch (it's already a parameter).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +202 to +206
const signalHandler = async (signal: string) => {
logger.debug(`Received ${signal}, cleaning up...`);
await cleanup();
process.exit(0);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Dev command signal handlers: async cleanup in signal handler may not complete before exit

The signal handler at packages/cli-v3/src/commands/dev.ts:202-206 calls await cleanup() then process.exit(0). Since cleanup() calls devInstance.stop() which may involve async operations (stopping watchers, removing lock files), and process.exit() is called immediately after, there's a question of whether the event loop drains pending microtasks before the process terminates. In Node.js, process.exit() forces immediate exit and does NOT wait for pending I/O. However, the await before process.exit(0) ensures the cleanup() promise resolves first (since the signal handler is async and awaited implicitly). The process.exit(0) only fires after the await completes. This is correct but worth noting: if cleanup() rejects, the unhandled rejection would bypass the process.exit(0) call, leaving the process hanging.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

1 participant