OUT-4101: Process webhook changes per channel instead of a global serializer - #142
Conversation
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis 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.
Confidence Score: 5/5The 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
Sequence DiagramsequenceDiagram
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
Reviews (1): Last reviewed commit: "fix(OUT-4101): dedupe delta entries by i..." | Re-trigger Greptile |
7ddee11
into
feature/performance-improvements
Removes the global one-at-a-time serialization in the Dropbox webhook change path so different channels/accounts process in parallel.
What changed
handleChannelFileChanges(queueconcurrencyLimit: 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.fanOutAndWait(task, items, channelSyncId)(was: unkeyedbatchTriggerAndWaitfor delete/create, a serialfor…offor 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.updateDropboxFileInAssemblykeys its inner delete+recreate bychannelSyncId, and the recreate is nowtriggerAndWait(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:
classifyDbxChangesnow appliesdedupeByIdKeepLastper bucket — duplicatedbxFileIdentries 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.processDropboxChanges's per-accountconcurrencyLimit: 1means a follow-up webhook for the same account can't read stale state and re-dispatch the same file.Testing
classifyDbxChangestests: content-update / created / deleted bucket dedup, and rename-pair preservation.pnpm typecheck+pnpm lintclean.Note for OUT-4102
Keying
syncDropboxFileToAssemblyper channel (here) / per portal (OUT-4100) means itsconcurrencyLimit: 25is 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)
fanOutAndWaitdiscards per-item batch failures (pre-existing, shared by 4 call sites) — tighten in a dedicated ticket.concurrencyKeyserialization can't be observed by the inline test double; confirm on a real Trigger.dev run.🤖 Generated with Claude Code