Skip to content

skip TIMELINE_HISTORY on the logical replication connection - #64

Merged
teknogeek0 merged 2 commits into
mainfrom
fix/skip-timeline-history
Sep 28, 2026
Merged

teknogeek0 merged 2 commits into
mainfrom
fix/skip-timeline-history

Conversation

@DmitriiAn

Copy link
Copy Markdown

Problem

On some Managed PostgreSQL (incl. Azure), CDC fails whenever the source is on a timeline > 1. That happens after an HA failover, a point-in-time restore, or a major-version upgrade.

pgsql_identify_system() follows IDENTIFY_SYSTEM with TIMELINE_HISTORY <tli> on the logical replication connection. Azure rejects it:

ERROR:  TIMELINE_HISTORY cannot be executed on a logical replication connection

Stock PostgreSQL allows this command in logical walsender mode; Azure restricts it. Prefetch keeps reconnecting and failing the same way, transform gives up after 10s with Failed to read stream context file: retry policy expired, and the follow pipeline exits with code 12. The initial copy completes, but no changes are replicated.

Fix

Stop sending TIMELINE_HISTORY, and remove the matching parse_timeline_history_file() call in stream_write_context(). Without the history filename, that call would fail on its own.

This is safe because the history is never used:

  • The timeline number, which CDC file names depend on, still comes from IDENTIFY_SYSTEM.
  • The history rows only fill system->currentTimeline, and nothing reads that.
  • catalog_lookup_timeline_history() already handles the empty table, which is also the normal case for timeline-1 sources.

The helper functions and the timeline_history catalog table stay in place, so existing work directories and --resume are unaffected. The now-unused static parseTimelineHistoryResult() produces one -Wunused-function warning.

Upstream issue: dimitri#1039 (open, not fixed upstream).

Nothing read the timeline history. The rows filled
IdentifySystem.currentTimeline, which no code reads, so the parser, the
catalog table and the cdcPathDir plumbing that fed them are all removed.
pgsql_identify_system loses its third parameter.

In place of the collection, stream_write_context now compares the source
timeline against the one recorded for this migration and stops when they
differ. A promotion or a point-in-time restore forks the source history,
and changes already applied to the target may belong to a branch the new
source does not have. The check reads the timeline that IDENTIFY_SYSTEM
already returns, so it needs no extra replication command and works on
managed providers that restrict TIMELINE_HISTORY.

The recorded timeline is left in place on failure so the operator can see
both values. The cdc-low-level suite covers the new path.
@teknogeek0

Copy link
Copy Markdown
Collaborator

This most recent commit finishes the removal (the parser, the catalog table and the cdcPathDir plumbing were all unreachable once the command goes, and parseTimelineHistoryResult was emitting an unused-function warning), and replaces the collection with a check: stream_write_context now compares the source timeline against the one recorded for the migration and stops when they differ, which catches a promoted or restored source without needing TIMELINE_HISTORY.

Covered by a new case in cdc-low-level — no new containers, and I confirmed it fails without the check. Full battery green at PG18, style and banned-API checks pass.

@teknogeek0 teknogeek0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed in depth and approved from my side.

@teknogeek0
teknogeek0 merged commit 0fc529c into main Sep 28, 2026
111 checks passed
@teknogeek0
teknogeek0 deleted the fix/skip-timeline-history branch September 28, 2026 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants