Re: [BUG] [PATCH] pg_basebackup produces wrong incremental files after relation truncation in segmented tables
Oleg Tkachenko <oatkachenko@gmail.com>
From: Oleg Tkachenko <oatkachenko@gmail.com>
To: pgsql-hackers@lists.postgresql.org
Date: 2025-12-15T11:09:03Z
Lists: pgsql-hackers
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Don't set the truncation block length greater than RELSEG_SIZE.
- ad569b54a106 17.8 landed
- c80b0c9d63b2 18.2 landed
- ecd275718be0 19 (unreleased) landed
Hello,
I am following up on my report with a patch, which did not receive any responses. I suspect the issue only manifests under specific conditions, so I am sending additional details along with a reliable reproducer.
For context:
Incremental backups cannot be restored when a relation larger than 1 GB (multiple segments) is vacuum-truncated between the base backup and the incremental backup (pg_basebackup itself completes successfully, but the resulting incremental backup is not restorable)
For segmented relations, the WAL summarizer records limit_block in the WAL summary. During incremental backup, the truncation length is computed incorrectly because a relation-wide limit is compared against a segment-local size.
Reproducer
I am attaching a bash script that reliably reproduces the issue on my system. The script:
Creates a table large enough to span multiple segments.
Takes a full base backup.
Deletes rows at the end of the relation.
Runs VACUUM (TRUNCATE) to remove blocks.
Takes an incremental backup.
Fails during pg_combinebackup.
The script is fully automated and intended to be run as-is.
A patch itself in the previous message.
I would appreciate feedback on the approach and am happy to revise it if needed.
Best regards,
Oleg Tkachenko

> On Nov 14, 2025, at 20:43, Oleg Tkachenko <oatkachenko@gmail.com> wrote:
>
> Hello PostgreSQL developers,
>
> I’ve encountered a bug in the incremental backup feature that prevents restoration of backups containing relations larger than 1 GB that were vacuum-truncated.
>
>
>
> Problem Description
>
> When taking incremental backups of relations that span multiple segments, if the relation is truncated during VACUUM (after the base backup but before the incremental one), pg_combinebackup fails with:
>
> ```
>
> file "%s" has truncation block length %u in excess of segment size %u
>
> ```
>
> pg_basebackup itself completes without errors, but the resulting incremental backup cannot be restored.
>
>
>
> Root Cause
>
> In segmented relations, a VACUUM that truncates blocks sets a limit_block in the WAL summary. The incremental restore logic miscalculates truncation_block_length when processing segment 0…N, because it compares the segment-local size with a relation-wide limit.
>
> In src/backend/backup/basebackup_incremental.c:
>
> ```
>
> *truncation_block_length = size / BLCKSZ;
>
> if (BlockNumberIsValid(limit_block))
>
> {
>
> unsigned relative_limit = limit_block - segno * RELSEG_SIZE;
>
> if (*truncation_block_length < relative_limit) /* ← problematic */
>
> *truncation_block_length = relative_limit;
>
> }
>
> ```
>
> For example, if limit_block lies in segment 10, then relative_limit will be roughly 9 * RELSEG_SIZE while processing segment 0. This forces truncation_block_length far beyond the actual segment size, leading to a segment length larger than RELSEG_SIZE and eventually the restore error.
>
>
>
> Reproduction Steps
>
>
> Create a table larger than 1 GB (multiple segments).
>
> Take a full base backup.
>
> Delete rows that occupy the end of the relation.
>
> Run VACUUM (VERBOSE, TRUNCATE) to ensure blocks are removed.
>
> (optional) Confirm that the WAL summary includes a limit entry for the relation.
>
> Take an incremental backup with pg_basebackup.
>
> Attempt to restore using pg_combinebackup.
>
> Observe the truncation block length error.
>
>
>
>
> Patch
>
> A patch correcting this logic is attached, and I’m happy to provide additional details or revisions if helpful.
>
>
>
> Best regards,
>
> Oleg Tkachenko
>
>
> <bug_truncation_block_length.patch>
>
>
>
>
>