Skip to content

OUT-4101: Process webhook changes per channel instead of a global serializer - #142

Merged
SandipBajracharya merged 2 commits into
feature/performance-improvementsfrom
OUT-4101
Aug 27, 2026
Merged

OUT-4101: Process webhook changes per channel instead of a global serializer#142
SandipBajracharya merged 2 commits into
feature/performance-improvementsfrom
OUT-4101

Conversation

@SandipBajracharya

Copy link
Copy Markdown
Collaborator

Removes the global one-at-a-time serialization in the Dropbox webhook change path so different channels/accounts process in parallel.

What changed

  • handleChannelFileChanges (queue concurrencyLimit: 1, previously triggered unkeyed → one channel's change-processing at a time across the whole system) is now triggered with { concurrencyKey: channelSyncId } → channels/accounts process in parallel, still serial per channel.
  • Inside it, the delete / create / content-update dispatches all go through fanOutAndWait(task, items, channelSyncId) (was: unkeyed batchTriggerAndWait for delete/create, a serial for…of for content-updates). This keys them per channel and — bonus — chunks within Trigger.dev's 500-item batch limit (a big folder rename could exceed it before). Delete→create→update ordering is preserved by awaiting each step.
  • updateDropboxFileInAssembly keys its inner delete+recreate by channelSyncId, and the recreate is now triggerAndWait (was fire-and-forget) so the mapping row is committed before the update reports done.

Duplicate protection (from review)

Two doors that could create a file twice (Assembly or Dropbox) are closed:

  • Within one delta: classifyDbxChanges now applies dedupeByIdKeepLast per bucket — duplicate dbxFileId entries in one batch can't fan out concurrent same-file syncs. Per-bucket so a rename's delete+create pair (same id, different buckets) is preserved.
  • Across webhooks: awaiting the recreate + processDropboxChanges's per-account concurrencyLimit: 1 means a follow-up webhook for the same account can't read stale state and re-dispatch the same file.

Testing

  • New classifyDbxChanges tests: content-update / created / deleted bucket dedup, and rename-pair preservation.
  • Full suite green: 273 unit + 179 integration, pnpm typecheck + pnpm lint clean.

Note for OUT-4102

Keying syncDropboxFileToAssembly per channel (here) / per portal (OUT-4100) means its concurrencyLimit: 25 is no longer a global ceiling — total concurrent syncs now scale with active channels/portals. Intended fairness tradeoff; right-sizing the limit + confirming the DB pool holds up is OUT-4102.

Follow-ups (not in this PR)

  • fanOutAndWait discards per-item batch failures (pre-existing, shared by 4 call sites) — tighten in a dedicated ticket.
  • The per-channel concurrencyKey serialization can't be observed by the inline test double; confirm on a real Trigger.dev run.

🤖 Generated with Claude Code

@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

OUT-4101

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dropbox-integration Ready Ready Preview Aug 26, 2026 3:10pm

Request Review

@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces global Dropbox change serialization with per-channel serialization while preserving delete-before-create ordering. It also batches content updates, waits for replacement recreation, and deduplicates repeated Dropbox IDs within each classification bucket.

  • Adds channel-scoped concurrency keys to Dropbox change processing and replacement child tasks.
  • Routes delete, create, and content-update work through chunked keyed fan-out.
  • Deduplicates each change bucket while preserving rename delete/create pairs.
  • Adds unit coverage for bucket deduplication and rename preservation.

Confidence Score: 5/5

The PR appears safe to merge; no changed-code-triggered blocking or independently actionable non-blocking issue was identified.

Per-channel keying preserves the required channel isolation and delete-before-create sequence, replacement work now waits for recreation, and bucket-local deduplication does not violate downstream ordering assumptions.

Important Files Changed

Filename Overview
src/features/webhook/dropbox/lib/webhook.service.ts Keys channel change coordination by channelSyncId while retaining the existing result check before cursor advancement.
src/trigger/processFileSync.ts Replaces global/serial dispatch with channel-keyed chunked fan-out and waits for both stages of content replacement.
src/utils/classify-dbx-changes.ts Deduplicates repeated Dropbox IDs independently within delete, create, and content-update buckets.
src/utils/tests/classify-dbx-changes.test.ts Covers deduplication in every bucket and confirms that rename delete/create pairs remain intact.

Sequence Diagram

sequenceDiagram
    participant D as Dropbox webhook
    participant A as Account processor
    participant C as Channel-keyed coordinator
    participant F as File tasks
    participant DB as Mapping database
    D->>A: Account change notification
    A->>A: Read cursor pages
    A->>C: triggerAndWait(changes, channelSyncId)
    C->>C: Classify and dedupe per bucket
    C->>F: Fan out deletes
    F->>DB: Remove counterparts and settle mappings
    F-->>C: Delete batch complete
    C->>F: Fan out creates
    F->>DB: Create counterparts and settle mappings
    F-->>C: Create batch complete
    C->>F: Fan out content replacements
    F->>F: Delete then recreate
    F->>DB: Commit replacement mappings
    F-->>C: Update batch complete
    C-->>A: Channel processing complete
    A->>DB: Advance channel cursor
Loading

Reviews (1): Last reviewed commit: "fix(OUT-4101): dedupe delta entries by i..." | Re-trigger Greptile

@priosshrsth priosshrsth left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm

@SandipBajracharya
SandipBajracharya merged commit 7ddee11 into feature/performance-improvements Aug 27, 2026
3 of 5 checks passed
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