Skip to content

fix(source-postgres): handle null filenode in Xmin incremental sync for Postgres < 14#76088

Draft
devin-ai-integration[bot] wants to merge 8 commits intomasterfrom
devin/1775345710-fix-xmin-null-filenode
Draft

fix(source-postgres): handle null filenode in Xmin incremental sync for Postgres < 14#76088
devin-ai-integration[bot] wants to merge 8 commits intomasterfrom
devin/1775345710-fix-xmin-null-filenode

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot commented Apr 4, 2026

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, so filenode is null — and two code paths in PostgresSourceJdbcPartitionFactory incorrectly 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:

  1. Cold start path (line ~93-109): PostgresSourceJdbcSplittableSnapshotWithXminPartition requires a filenode. When filenode is null, the code threw an error. Now falls back to a new PostgresSourceJdbcUnsplittableSnapshotWithXminPartition class — consistent with how the UserDefinedCursor path already handles null filenode (falls back to PostgresSourceJdbcUnsplittableSnapshotWithCursorPartition).

  2. Incremental ongoing path (line ~300-309): PostgresSourceJdbcXminIncrementalPartition was guarded by filenode?.let, but its constructor does not use filenode at all. Removed the unnecessary null guard so the partition is created directly.

New class PostgresSourceJdbcUnsplittableSnapshotWithXminPartition is modeled after PostgresSourceJdbcUnsplittableSnapshotWithCursorPartition, using xminIncrementalCheckpoint for complete state and xminCursorUpperBoundQuery for the cursor upper bound query.

Review guide

  1. PostgresSourceJdbcPartition.kt — New PostgresSourceJdbcUnsplittableSnapshotWithXminPartition class (lines 115-129). Verify completeState uses the correct checkpoint method and cursorUpperBoundQuery is appropriate for xmin-based snapshots. Compare with the splittable xmin partition and the unsplittable cursor partition for correctness.
  2. PostgresSourceJdbcPartitionFactory.kt — Two targeted fixes. Verify the cold start fallback and the removal of the filenode?.let guard are both safe.
  3. PostgresSourceJdbcPartitionFactoryTest.kt — Two regression tests covering both null-filenode scenarios via spyk with tidRangeScanCapableDBServer = false.

Reviewer checklist

  • Confirm PostgresSourceJdbcXminIncrementalPartition constructor genuinely does not use filenode (see line ~403-416 in PostgresSourceJdbcPartition.kt — it passes null for filenode to its superclass)
  • Verify completeState in the new unsplittable xmin partition correctly produces an xmin-based checkpoint (should match behavior of the splittable variant's completeState)
  • Verify cursorUpperBoundQuery returning xminCursorUpperBoundQuery is correct for unsplittable xmin snapshots (same query used by the splittable xmin partition)
  • Check that streamState.cursorUpperBound ?: Jsons.nullNode() in the new class's completeState is safe — if cursorUpperBound is null at cold-start time, passing nullNode to xminIncrementalCheckpoint should still produce valid state
  • This fix has not been tested against a real Postgres < 14 instance — only unit-tested with mocked tidRangeScanCapableDBServer = false

User 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?

  • YES 💚

Link to Devin session: https://app.devin.ai/sessions/8d4728a19baf4d1bb953974cc983e564

devin-ai-integration bot and others added 2 commits April 4, 2026 23:39
…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>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

PR Slash Commands

Airbyte Maintainers (that's you!) can execute the following slash commands on your PR:

  • 🛠️ Quick Fixes
    • /format-fix - Fixes most formatting issues.
    • /bump-version - Bumps connector versions, scraping changelog description from the PR title.
      • Bump types: patch (default), minor, major, major_rc, rc, promote.
      • The rc type is a smart default: applies minor_rc if stable, or bumps the RC number if already RC.
      • The promote type strips the RC suffix to finalize a release.
      • Example: /bump-version type=rc or /bump-version type=minor
    • /bump-progressive-rollout-version - Alias for /bump-version type=rc. Bumps with an RC suffix and enables progressive rollout.
  • ❇️ AI Testing and Review (internal link: AI-SDLC Docs):
    • /ai-prove-fix - Runs prerelease readiness checks, including testing against customer connections.
    • /ai-canary-prerelease - Rolls out prerelease to 5-10 connections for canary testing.
    • /ai-review - AI-powered PR review for connector safety and quality gates.
  • 🚀 Connector Releases:
    • /publish-connectors-prerelease - Publishes pre-release connector builds (tagged as {version}-preview.{git-sha}) for all modified connectors in the PR.
  • ☕️ JVM connectors:
    • /update-connector-cdk-version connector=<CONNECTOR_NAME> - Updates the specified connector to the latest CDK version.
      Example: /update-connector-cdk-version connector=destination-bigquery
  • 🐍 Python connectors:
    • /poe connector source-example lock - Run the Poe lock task on the source-example connector, committing the results back to the branch.
    • /poe source example lock - Alias for /poe connector source-example lock.
    • /poe source example use-cdk-branch my/branch - Pin the source-example CDK reference to the branch name specified.
    • /poe source example use-cdk-latest - Update the source-example CDK dependency to the latest available version.
  • ⚙️ Admin commands:
    • /force-merge reason="<REASON>" - Force merges the PR using admin privileges, bypassing CI checks. Requires a reason.
      Example: /force-merge reason="CI is flaky, tests pass locally"
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

devin-ai-integration bot and others added 2 commits April 4, 2026 23:42
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

Deploy preview for airbyte-docs ready!

✅ Preview
https://airbyte-docs-3dk97hjdj-airbyte-growth.vercel.app

Built with commit c1e8ab7.
This pull request is being automatically deployed with vercel-action

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

/format-fix

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

source-postgres Connector Test Results

 15 files   15 suites   14m 48s ⏱️
 94 tests  94 ✅ 0 💤 0 ❌
280 runs  280 ✅ 0 💤 0 ❌

Results for commit c1e8ab7.

♻️ This comment has been updated with latest results.

devin-ai-integration bot and others added 4 commits April 4, 2026 23:49
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

/format-fix

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

↪️ Triggering /ai-prove-fix per Hands-Free AI Triage Project triage next step.

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.
Linked issue: https://github.com/airbytehq/oncall/issues/11886

Devin session

@octavia-bot
Copy link
Copy Markdown
Contributor

octavia-bot bot commented Apr 5, 2026

🔍 AI Prove Fix session starting... Running readiness checks and testing against customer connections. View playbook

Devin AI session created successfully!

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

devin-ai-integration bot commented Apr 5, 2026

🔬 Prove-Fix Validation — source-postgres v3.8.0-rc.4

Status: 🔄 Evidence gathering in progress

Pre-flight Checks ✅

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:

  1. Cold start path (line ~94-109): When filenode is null, falls back to PostgresSourceJdbcUnsplittableSnapshotWithXminPartition instead of throwing
  2. Incremental ongoing path (line ~300-312): Removes the unnecessary filenode?.let guard since PostgresSourceJdbcXminIncrementalPartition doesn'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

  1. ⏳ Wait for pre-release publish to complete
  2. ⏳ Wait for regression tests to complete
  3. 🔒 Request human approval to pin pre-release to affected connection
  4. 🧪 Pin 3.8.0-preview.c1e8ab7 and trigger a sync
  5. 📊 Monitor sync results and document findings

Devin session

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 5, 2026

Pre-release Connector Publish Started

Publishing pre-release build for connector source-postgres.
PR: #76088

Pre-release versions will be tagged as {version}-preview.c1e8ab7
and are available for version pinning via the scoped_configuration API.

View workflow run
Pre-release Publish: SUCCESS

Docker image (pre-release):
airbyte/source-postgres:3.8.0-preview.c1e8ab7

Docker Hub: https://hub.docker.com/layers/airbyte/source-postgres/3.8.0-preview.c1e8ab7

Registry JSON:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant