feat(processor): fork event processing for configured isolated destinations - #7207
Merged
Conversation
1 task
atzoum
force-pushed
the
feat.procPoolFork
branch
2 times, most recently
from
July 22, 2026 12:16
cd0c95e to
66f34fc
Compare
atzoum
marked this pull request as draft
July 22, 2026 13:33
atzoum
force-pushed
the
feat.procPoolFork
branch
from
July 22, 2026 14:22
66f34fc to
070bfc1
Compare
atzoum
force-pushed
the
feat.procPoolFork
branch
from
July 22, 2026 15:30
070bfc1 to
a0348c4
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #7207 +/- ##
==========================================
+ Coverage 79.78% 80.34% +0.56%
==========================================
Files 599 600 +1
Lines 66862 67012 +150
==========================================
+ Hits 53343 53839 +496
+ Misses 10380 10014 -366
- Partials 3139 3159 +20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
atzoum
force-pushed
the
feat.procPoolFork
branch
6 times, most recently
from
July 24, 2026 07:50
057ab17 to
6ebf9dc
Compare
atzoum
added a commit
that referenced
this pull request
Jul 27, 2026
# Description Introduces the **proc pool**: a second processor worker pool that consumes a new intermediate **`proc` jobsdb** and runs the post-fan-out stages — `rebuild → user transform → destination transform → store` — isolated **per destination**, so a slow/failing destination no longer blocks the others. It reuses the existing processor stages as-is; only the `rebuild` entry stage is new. This PR is the **consumer side only**. Nothing writes to the `proc` jobsdb yet — the gw-pool siphon that fans out and persists jobs into it is out of scope and will follow up in a separate pull request. With `Processor.DestinationIsolation.enabled=false` (default) the pool is a no-op. **Multi-consumer jobsdb.** The `proc` jobsdb is multi-consumer: one job carries multiple consumer IDs (destination IDs), and each `(job, consumer)` pair has its own independent status lifecycle. The proc pool: - discovers partitions via `GetDistinctConsumers` (one partition = one destination), - picks up jobs scoped to a consumer (`GetUnprocessed` with `Consumer`), and - commits per-consumer statuses through the shared `storeStage`, redirected to write against `procDB` instead of `gatewayDB`. **Rebuild assumptions.** Proc jobs persist only IDs + the event/metadata; at consume time `rebuild` re-hydrates `Destination`/`Connection`/`Libraries`/`Credentials` from the **live** backend config. Therefore: - Destination filtering and consent are **not** re-applied — they were already decided in the gw pool, and a job is only stored for the destinations that passed them. - A destination deleted/disabled between fan-out and consume is dropped gracefully to a terminal status. Drop is silent, i.e. it is not being reported, similar to what is happening already during the pretransform stage when we fan out events. ## Linear Ticket resolves PIPE-3048 resolves PIPE-3054 ## Security - [x] The code changed/added as part of this pull request won't create any security issues with how the software is being used. <!-- GitButler Footer Boundary Top --> --- This is **part 1 of 2 in a stack** made with GitButler: - <kbd> 2 </kbd> #7207 - <kbd> 1 </kbd> #7202 👈 <!-- GitButler Footer Boundary Bottom -->
atzoum
force-pushed
the
feat.procPoolFork
branch
from
July 27, 2026 06:47
6ebf9dc to
73a6fb4
Compare
atzoum
marked this pull request as ready for review
July 27, 2026 06:47
atzoum
force-pushed
the
feat.procPoolFork
branch
5 times, most recently
from
July 28, 2026 09:12
306e0c7 to
ad28fad
Compare
…ations chore: fixes chore: partition buffer store consistency chore: improvements
atzoum
force-pushed
the
feat.procPoolFork
branch
from
July 28, 2026 17:22
ad28fad to
3c0cd35
Compare
1 task
This was referenced Aug 3, 2026
Merged
itsmihir
pushed a commit
that referenced
this pull request
Aug 3, 2026
🤖 I have created a release *beep* *boop* --- ## [1.82.0-rc.1](v1.81.0...v1.82.0-rc.1) (2026-08-03) ### Features * **processor:** fork event processing for configured isolated destinations ([#7207](#7207)) ([6eb91b4](6eb91b4)) * **processor:** isolated destination processing pipeline ([#7202](#7202)) ([a684641](a684641)) * **router:** detect and classify transformer-proxy contract breaches (INT-6839) ([#7184](#7184)) ([c7a15a8](c7a15a8)) * **router:** gate 296 delivered-with-warning behind per-workspace rollout ([#7217](#7217)) ([1d772a1](1d772a1)) ### Bug Fixes * apply configured client QPS and Burst settings in restConfig ([#7220](#7220)) ([762bc06](762bc06)) * gcs manager allows non-service account credentials and warehouse ssl keys can escape their containing path ([#7227](#7227)) ([3e126b2](3e126b2)) * mid-run pod deaths under concurrent test runs ([#7212](#7212)) ([5279b64](5279b64)) * normalize null BingAds offline conversion fields ([#7236](#7236)) ([678f130](678f130)) * use correct JSON tag for adjustedConversionTime in bing-ads ([#7204](#7204)) ([50df0f7](50df0f7)) ### Miscellaneous * add new readme logo ([#7200](#7200)) ([ab86afd](ab86afd)) * **deps:** bump the go-deps group across 1 directory with 32 updates ([#7199](#7199)) ([0eaa833](0eaa833)) * **jobsdb:** graceful shutdown of addNewDSLoop and refreshDSListLoop ([#7210](#7210)) ([78e104c](78e104c)) * remove archiveInPreProcess processor toggle ([#7205](#7205)) ([f471b1d](f471b1d)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: rudderstack-github-actions[bot] <236995729+rudderstack-github-actions[bot]@users.noreply.github.com>
itsmihir
pushed a commit
that referenced
this pull request
Aug 4, 2026
🤖 I have created a release *beep* *boop* --- ## [1.82.0](v1.81.0...v1.82.0) (2026-08-03) ### Features * **processor:** fork event processing for configured isolated destinations ([#7207](#7207)) ([6eb91b4](6eb91b4)) * **processor:** isolated destination processing pipeline ([#7202](#7202)) ([a684641](a684641)) * **router:** detect and classify transformer-proxy contract breaches (INT-6839) ([#7184](#7184)) ([c7a15a8](c7a15a8)) * **router:** gate 296 delivered-with-warning behind per-workspace rollout ([#7217](#7217)) ([1d772a1](1d772a1)) ### Bug Fixes * apply configured client QPS and Burst settings in restConfig ([#7220](#7220)) ([762bc06](762bc06)) * gcs manager allows non-service account credentials and warehouse ssl keys can escape their containing path ([#7227](#7227)) ([3e126b2](3e126b2)) * mid-run pod deaths under concurrent test runs ([#7212](#7212)) ([5279b64](5279b64)) * normalize null BingAds offline conversion fields ([#7236](#7236)) ([678f130](678f130)) * use correct JSON tag for adjustedConversionTime in bing-ads ([#7204](#7204)) ([50df0f7](50df0f7)) ### Miscellaneous * add new readme logo ([#7200](#7200)) ([ab86afd](ab86afd)) * **deps:** bump the go-deps group across 1 directory with 32 updates ([#7199](#7199)) ([0eaa833](0eaa833)) * **jobsdb:** graceful shutdown of addNewDSLoop and refreshDSListLoop ([#7210](#7210)) ([78e104c](78e104c)) * remove archiveInPreProcess processor toggle ([#7205](#7205)) ([f471b1d](f471b1d)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: rudderstack-github-actions[bot] <236995729+rudderstack-github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds the write side of the destination-isolated pipeline: the gw pool now forks events for configured destinations into the intermediate
procjobsdb (consumed by the proc pool from #7202) instead of transforming them inline, so a slow/failing destination's events are siphoned off and no longer hold up the shared pipeline.Forking is opt-in per destination via a hierarchical, config which is by default disabled:
The fork. At the
pretransformfan-out stage, each event's forked destinations are split off from the inlinegroupedEvents. Oneprocjob is written per forked event, carrying the forked destination IDs as its consumers (multi-consumer job) and a source-level payload. The forked-job list is carried over through the transform stages and finally committed during thestorestage.Atomicity. The
procwrite commits in the same transaction as the gateway status update so there is no room for duplicate jobs to be written inprocjobsdb in case of a crash.rsources/retl excluded (for now). Events carrying a
sourceJobRunIDstay inline until the intermediate stage is modelled in rsources accounting, otherwise a retl source could be reported complete while its events still sit in the queue.Supporting jobsdb changes
internalStoreJobsInTx,pendingEventsJobsDB). WithWithStoreSafeTxFromTxthe caller's transaction is reused across the internal stale-dataset-list retry, so registering before the op double-fired the pending-events / cache / stat listeners on every retry.consumersarray. The partition-migration buffer (proc_buf) is a single-consumer handle that relays multi-consumerprocjobs between nodes; it previously collapsed their consumers to the legacy'', so migrated jobs resurfaced under an unknown destination and were dropped.procis the first multi-consumer jobsdb to be migrated, so this was never hit before.checkIfCompactDSrewrite: the terminal-job-count check (single- and multi-consumer) now does an index-only grouped scan instead of a per-job correlated subquery against thev_last_*views — pure perf, same result. Needed onceproccompaction is exercised at the volumes below.WithDefaultSkipStatusCompaction: pass-through jobsdbs (gw, esch, arc, proc, and their_bufmigration-buffer counterparts, err_idx) skip status compaction by default — they have no router-style retry backlog, so there's nothing to reclaim.Deciding when to enable: fan-out visibility
An event on a source wired to N destinations produces N forked jobs, each needing its own proc consumer status update. So isolating all destinations is cheap on narrowly-wired sources but gets progressively more expensive as its source's fan-out grows — even if most of those destinations are filtered out downstream by a user transformation before they'd ever reach the router. For narrow-fanout scenarios we can safely isolate all destinations, while for wide-fanout ones we may need to isolate only specific slow/failing destinations rather than paying the fan-out cost for all of them. Two new stats provide visibility to make that call:
processor_source_to_destination_fanout(histogram) andprocessor_max_source_to_destination_fanout(gauge), recorded per event at fan-out, before filtering.BenchmarkProcessorDestinationIsolationquantifies this trade-off directly: 100k events fanned out to 10/20/50 destinations (all but 2 filtered downstream), with and without forking, across a fewJobsDB.proc.maxDSSizesettings.Benchmark results
Single-run wall-clock drain times (local Docker Postgres + transformer — directional, not a production SLA):
maxDSSize)Full results
maxDSSize=100kmaxDSSize=20kmaxDSSize=10kmaxDSSize=100kmaxDSSize=20kmaxDSSize=10kmaxDSSize=20kmaxDSSize=10kmaxDSSize=5kTests
TestProcessorDestinationIsolation), generalized to N destinations with partial forking and user-transformation filtering: all-forked / mixed / none / wide-fanout-mostly-filtered — asserts every event reaches the router regardless of path, the expectedprocjob count and per-consumer fan-out, and full drain.BenchmarkProcessorDestinationIsolation— proc consumer status-update throughput at scale, exercisingmaxProcDSSize/compaction settings.Linear Ticket
resolves PIPE-3049
Security
This is part 1 of 2 in a stack made with GitButler: