fix(bulk-cdk-load): scope state bookkeeping per stream to stop spurious unflushed-state failures - #83238
fix(bulk-cdk-load): scope state bookkeeping per stream to stop spurious unflushed-state failures#83238devin-ai-integration[bot] wants to merge 3 commits into
Conversation
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
👋 Greetings, Airbyte Team Member!Here are some helpful tips and reminders for your convenience. 💡 Show Tips and TricksPR Slash CommandsAirbyte Maintainers (that's you!) can execute the following slash commands on your PR:
📚 Show Repo GuidanceHelpful Resources
|
Co-Authored-By: bot_apk <apk@cognition.ai>
|
CI status: 5 failing checks, none of which I believe are caused by this change. Evidence for each:
Counter-evidence that the CDK change itself is healthy: the |
|
↪️ Triggering Reason: Draft PR whose 5 red checks were shown to reproduce on unrelated bulk-CDK PRs; validating the per-stream state bookkeeping fix against live destination connections is the next pipeline gate for: |
|
🧪 Fix Validation Evidence🟢 Outcome: Fix ProvenIsolated testing only — this is a CDK-only PR, so there is no connector image to pre-release and no connection was pinned or synced. I reconstructed the failure independently against pre-fix 🚦 Next Steps
📋 Connector & PR DetailsComponent: bulk-load CDK — 📝 Evidence PlanTesting strategy decision
Proving criteria
Disproving criteria
Cases attempted
✅ Pre-flight Checks
Design-intent notes (non-blocking, for reviewers):
📊 Detailed Evidence Log1. Pre-fix reproduction — scratch worktree at
2. Post-fix — branch head, 3. Destination integration matrix on head 4. Call-site sweep across Scope note: the defect is socket-mode only. Connection IDs and customer details are recorded in the linked private issue. |
|
↪️ Triggering Reason: |
Reviewing PR for connector safety and quality.
|
🛡️ AI PR Review Report⚪ Review Action: SKIPPED (OUT OF SCOPE)This PR does not contain connector or user-facing documentation changes and is outside the scope of the AI PR Review system.
|
|
🙋 Escalated to #human-in-the-loop per Hands-Free AI Triage Project triage next step. Routed to the DB/DW connectors oncall. |
What
Destination syncs (reported on
destination-snowflake, but the defect is in the bulk-load CDK and affects every bulk destination) intermittently fail at the very end with:All streams reach
COMPLETEand all data is written; only the final checkpoint reconciliation fails, so the sync is marked failed and retried. The diagnostic line in the logs is always the same shape — a single leftover stream state whose expected count was never recorded:Root cause:
StateHistogramStoreis a process-wide singleton holding oneexpectedhistogram keyed byStateKey(id, partitionKeys)and oneflushedhistogram keyed byPartitionKey— neither carries stream identity — while the state queues inStateStoreare per stream. In socket mode the key material comes straight from the source:idis a per-feed counter (so every stream's first checkpoint isid=1) andpartition_idis a 4-character random string. When two streams in one sync draw the same(id, partition_id), their buckets merge; the first stream to reconcile callsremove(key)and deletes the shared expected/flushed entries, after which the second stream's queued state hasexpectedCount = nullforever,isComplete()evaluatesnull == 0.0→ false, andPipelineRunnerfails the sync. The collision is probabilistic, which matches the observed pattern (affected connections succeed on retry and on subsequent syncs).Resolves https://github.com/airbytehq/oncall/issues/12123:
How
Give the bookkeeping the stream identity its keys lack, without changing any key derivation on the source or record side:
StateScope—GlobalorStream(descriptor).StateHistogramStorenow holdsflushed: Map<Descriptor, PartitionHistogram>andexpected: Map<StateScope, StateHistogram>;acceptFlushedCounts,acceptExpectedCounts,isComplete,whyIsStateIncompleteandremoveall take the scope (or descriptor). Two streams can no longer touch each other's buckets.outer_partitioncovers records from several streams), so forStateScope.Globalthe flushed lookup sums a partition key across all descriptors andremoveclears it from all of them.mappedDescriptor(that isAggregateStore's key and whatStateStage/PipelineCompletionHandleralready have in hand), whereas checkpoints carry the unmapped descriptor, soStateStoreinjectsDestinationCatalogand translates unmapped → mapped. An unknown descriptor falls back to the unmapped one with a warning rather than throwing.Bulk CDK
loadversion bumped to1.0.21with a changelog entry. No connector files change here — the fix reachesdestination-snowflakeand the other bulk destinations on their next CDK bump.Breaking change evaluation
Not breaking: no schema, spec, primary key, cursor, stream, or state-format change. The only signature changes are on internal bulk-CDK classes with no external consumers, and global-state accounting is unchanged by construction.
Review guide
StateHistogramStore.kt— the scoping itself; check theGlobalbranches preserve cross-stream summing/removal.StateStore.kt— scope derivation per message type and the unmapped → mapped descriptor translation.StateStage.kt/PipelineCompletionHandler.kt— one-line descriptor plumbing.StateHistogramStoreTest.kt/StateStoreTest.kt— new tests.Test Coverage
New unit tests, all in
:airbyte-cdk:bulk:core:bulk-cdk-core-load:StateHistogramStoreTest: twoStreamscopes sharing an identicalStateKey(1, [PartitionKey("abcd")])stay independent, and removing stream A's key leaves stream B complete. Verified this test fails against the pre-fix unscoped implementation (AssertionErroron the post-removal assertion) and passes with the fix.StateHistogramStoreTest:Globalscope sums flushed counts across descriptors andremoveclears them everywhere (regression guard for global mode).StateStoreTest: two stream checkpoints for different streams with identical state keys both flush and leavehasStates() == false— i.e. the reported failure no longer occurs.StateStoreTest: a stream whose mapped descriptor differs from its unmapped descriptor completes when flushed counts arrive under the mapped descriptor (guards the translation).The full
:airbyte-cdk:bulk:core:bulk-cdk-core-load:testsuite passes locally. In CI, thedestination-snowflake,s3,s3-data-lake,bigquery,clickhouse,mssql,azure-blob-storageanddev-nullintegration suites all pass against this branch.Reproduction of the customer sync itself was not attempted: it needs the customers' source and destination credentials, and the trigger is a random key collision. The unit reproduction above exercises the exact failing invariant instead.
User Impact
Syncs no longer fail spuriously at the end of an otherwise successful run when two streams collide on a checkpoint key. No behavior change for global states, and no change to what is written to the destination. Data was never lost by this bug — only the checkpoint commit and the sync's success status were.
Can this PR be safely reverted and rolled back?
Link to Devin session: https://app.devin.ai/sessions/cd8ad0bcacd24e97b2aeea63acf755c1