fix(source-postgres): handle null filenode in Xmin incremental sync for Postgres < 14#76088
fix(source-postgres): handle null filenode in Xmin incremental sync for Postgres < 14#76088devin-ai-integration[bot] wants to merge 8 commits intomasterfrom
Conversation
…or Postgres < 14 When the source Postgres server is version < 14, TID range scans are not supported and filenode is null. The Xmin incremental code path had two bugs: 1. Cold start path threw an error instead of falling back to an unsplittable snapshot partition when filenode was null. Now falls back to PostgresSourceJdbcUnsplittableSnapshotWithXminPartition. 2. Incremental ongoing path guarded partition creation with filenode?.let, but PostgresSourceJdbcXminIncrementalPartition does not use filenode. Removed the unnecessary null guard. Resolves airbytehq/oncall#11886 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>
Co-Authored-By: bot_apk <apk@cognition.ai>
|
Deploy preview for airbyte-docs ready! ✅ Preview Built with commit c1e8ab7. |
|
/format-fix |
|
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
|
/format-fix |
|
↪️ Triggering Reason: Connector tests passed (94 tests, 0 failures). Format Check failure is non-blocking (not required). Fix addresses null filenode crash for Postgres < 14 Xmin incremental sync. |
|
🔬 Prove-Fix Validation —
|
| Check | Result |
|---|---|
| Viability | ✅ Code changes directly address the IllegalStateException with null filenode |
| Safety | ✅ No malicious code, no credential harvesting, no data exfiltration |
| Breaking Change | ✅ NOT breaking — graceful fallback, no config/schema/state changes |
| Reversibility | ✅ Can be safely rolled back |
Evidence Plan
Proving Criteria: A connection previously failing with IllegalStateException: Unexpected incremental sync for a table ... with no filenode at PostgresSourceJdbcPartitionFactory.kt:310 successfully completes a sync after pinning to 3.8.0-preview.c1e8ab7.
Disproving Criteria: The connection still fails with the same or a new error after pinning.
Fix Analysis: The PR addresses two code paths in PostgresSourceJdbcPartitionFactory.kt:
- Cold start path (line ~94-109): When filenode is null, falls back to
PostgresSourceJdbcUnsplittableSnapshotWithXminPartitioninstead of throwing - Incremental ongoing path (line ~300-312): Removes the unnecessary
filenode?.letguard sincePostgresSourceJdbcXminIncrementalPartitiondoesn't use filenode
Candidate Connection: Found a connection in the affected workspace actively failing with the exact error across all 27 streams. Postgres < 14 instance using Xmin incremental sync — ideal proving case.
Workflows Triggered
| Workflow | Status | URL |
|---|---|---|
| Pre-release Publish | 🔄 In progress | Publish workflow |
| Regression Tests | 🔄 In progress | Regression test workflow |
Next Steps
- ⏳ Wait for pre-release publish to complete
- ⏳ Wait for regression tests to complete
- 🔒 Request human approval to pin pre-release to affected connection
- 🧪 Pin
3.8.0-preview.c1e8ab7and trigger a sync - 📊 Monitor sync results and document findings
|
What
Fixes https://github.com/airbytehq/oncall/issues/11886
Xmin incremental syncs fail with
java.lang.IllegalStateException: Unexpected incremental sync for a table ... with no filenode.when the source Postgres server is version < 14. This is because TID range scans are not supported on older Postgres versions, sofilenodeis null — and two code paths inPostgresSourceJdbcPartitionFactoryincorrectly threw errors instead of handling this case.All 27 streams in the affected connection fail. Bug was introduced in 3.8.0-rc.1 (PR #75637, the Java→Kotlin rewrite).
How
Two fixes in
PostgresSourceJdbcPartitionFactory.kt:Cold start path (line ~93-109):
PostgresSourceJdbcSplittableSnapshotWithXminPartitionrequires a filenode. When filenode is null, the code threw an error. Now falls back to a newPostgresSourceJdbcUnsplittableSnapshotWithXminPartitionclass — consistent with how theUserDefinedCursorpath already handles null filenode (falls back toPostgresSourceJdbcUnsplittableSnapshotWithCursorPartition).Incremental ongoing path (line ~300-309):
PostgresSourceJdbcXminIncrementalPartitionwas guarded byfilenode?.let, but its constructor does not use filenode at all. Removed the unnecessary null guard so the partition is created directly.New class
PostgresSourceJdbcUnsplittableSnapshotWithXminPartitionis modeled afterPostgresSourceJdbcUnsplittableSnapshotWithCursorPartition, usingxminIncrementalCheckpointfor complete state andxminCursorUpperBoundQueryfor the cursor upper bound query.Review guide
PostgresSourceJdbcPartition.kt— NewPostgresSourceJdbcUnsplittableSnapshotWithXminPartitionclass (lines 115-129). VerifycompleteStateuses the correct checkpoint method andcursorUpperBoundQueryis appropriate for xmin-based snapshots. Compare with the splittable xmin partition and the unsplittable cursor partition for correctness.PostgresSourceJdbcPartitionFactory.kt— Two targeted fixes. Verify the cold start fallback and the removal of thefilenode?.letguard are both safe.PostgresSourceJdbcPartitionFactoryTest.kt— Two regression tests covering both null-filenode scenarios viaspykwithtidRangeScanCapableDBServer = false.Reviewer checklist
PostgresSourceJdbcXminIncrementalPartitionconstructor genuinely does not usefilenode(see line ~403-416 inPostgresSourceJdbcPartition.kt— it passesnullfor filenode to its superclass)completeStatein the new unsplittable xmin partition correctly produces an xmin-based checkpoint (should match behavior of the splittable variant'scompleteState)cursorUpperBoundQueryreturningxminCursorUpperBoundQueryis correct for unsplittable xmin snapshots (same query used by the splittable xmin partition)streamState.cursorUpperBound ?: Jsons.nullNode()in the new class'scompleteStateis safe — ifcursorUpperBoundis null at cold-start time, passingnullNodetoxminIncrementalCheckpointshould still produce valid statetidRangeScanCapableDBServer = falseUser Impact
Xmin incremental syncs on Postgres < 14 will work correctly instead of failing on all streams. No behavior change for Postgres >= 14 users.
Can this PR be safely reverted and rolled back?
Link to Devin session: https://app.devin.ai/sessions/8d4728a19baf4d1bb953974cc983e564