Re: pg_waldump: support decoding of WAL inside tarfile
amul sul <sulamul@gmail.com>
Attachments
- v14-0001-Refactor-Move-tar-archive-parsing-into-a-common-.patch (application/octet-stream) patch v14-0001
- v14-0002-Refactor-pg_waldump-Move-some-declarations-to-ne.patch (application/octet-stream) patch v14-0002
- v14-0003-Refactor-pg_waldump-Separate-logic-used-to-calcu.patch (application/octet-stream) patch v14-0003
- v14-0004-Refactor-pg_waldump-Restructure-TAP-tests.patch (application/octet-stream) patch v14-0004
- v14-0005-Refactor-pg_waldump-Move-WAL-segment-size-to-XLo.patch (application/octet-stream) patch v14-0005
- v14-0006-pg_waldump-Add-support-for-archived-WAL-decoding.patch (application/octet-stream) patch v14-0006
- v14-0007-pg_waldump-Remove-the-restriction-on-the-order-o.patch (application/octet-stream) patch v14-0007
- v14-0008-pg_verifybackup-Delay-default-WAL-directory-prep.patch (application/octet-stream) patch v14-0008
- v14-0009-pg_verifybackup-Rename-the-wal-directory-switch-.patch (application/octet-stream) patch v14-0009
- v14-0010-pg_verifybackup-Enabled-WAL-parsing-for-tar-form.patch (application/octet-stream) patch v14-0010
On Wed, Feb 18, 2026 at 12:28 PM Amul Sul <sulamul@gmail.com> wrote:
>
> On Tue, Feb 10, 2026 at 3:06 PM Amul Sul <sulamul@gmail.com> wrote:
> >
> > On Wed, Feb 4, 2026 at 6:39 PM Amul Sul <sulamul@gmail.com> wrote:
> > >
> > > On Wed, Jan 28, 2026 at 2:41 AM Robert Haas <robertmhaas@gmail.com> wrote:
> > > >
> > > > On Tue, Jan 27, 2026 at 7:07 AM Amul Sul <sulamul@gmail.com> wrote:
> > > > > In the attached version, I am using the WAL segment name as the hash
> > > > > key, which is much more straightforward. I have rewritten
> > > > > read_archive_wal_page(), and it looks much cleaner than before. The
> > > > > logic to discard irrelevant WAL files is still within
> > > > > get_archive_wal_entry. I added an explanation for setting cur_wal to
> > > > > NULL, which is now handled in the separate function I mentioned
> > > > > previously.
> > > > >
> > > > > Kindly have a look at the attached version; let me know if you are
> > > > > still not happy with the current approach for filtering/discarding
> > > > > irrelevant WAL segments. It isn't much different from the previous
> > > > > version, but I have tried to keep it in a separate routine for better
> > > > > code readability, with comments to make it easier to understand. I
> > > > > also added a comment for ArchivedWALFile.
> > > >
> > > > I feel like the division of labor between get_archive_wal_entry() and
> > > > read_archive_wal_page() is odd. I noticed this in the last version,
> > > > too, and it still seems to be the case. get_archive_wal_entry() first
> > > > calls ArchivedWAL_lookup(). If that finds an entry, it just returns.
> > > > If it doesn't, it loops until an entry for the requested file shows up
> > > > and then returns it. Then control returns to read_archive_wal_page()
> > > > which loops some more until we have all the data we need for the
> > > > requested file. But it seems odd to me to have two separate loops
> > > > here. I think that the first loop is going to call read_archive_file()
> > > > until we find the beginning of the file that we care about and then
> > > > the second one is going to call read_archive_file() some more until we
> > > > have read enough of it to satisfy the request. It feels odd to me to
> > > > do it that way, as if we told somebody to first wait until 9 o'clock
> > > > and then wait another 30 minutes, instead of just telling them to wait
> > > > until 9:30. I realize it's not quite the same thing, because apart
> > > > from calling read_archive_file(), the two loops do different things,
> > > > but I still think it looks odd.
> > > >
> > > > + /*
> > > > + * Ignore if the timeline is different or the current segment is not
> > > > + * the desired one.
> > > > + */
> > > > + XLogFromFileName(entry->fname, &curSegTimeline, &curSegNo, WalSegSz);
> > > > + if (privateInfo->timeline != curSegTimeline ||
> > > > + privateInfo->startSegNo > curSegNo ||
> > > > + privateInfo->endSegNo < curSegNo ||
> > > > + segno > curSegNo)
> > > > + {
> > > > + free_archive_wal_entry(entry->fname, privateInfo);
> > > > + continue;
> > > > + }
> > > >
> > > > The comment doesn't match the code. If it did, the test would be
> > > > (privateInfo->timeline != curSegTimeline || segno != curSegno). But
> > > > instead the segno test is > rather than !=, and the checks against
> > > > startSegNo and endSegNo aren't explained at all. I think I understand
> > > > why the segno test uses > rather than !=, but it's the point of the
> > > > comment to explain things like that, rather than leaving the reader to
> > > > guess. And I don't know why we also need to test startSegNo and
> > > > endSegNo.
> > > >
> > > > I also wonder what the point is of doing XLogFromFileName() on the
> > > > fname provided by the caller and then again on entry->fname. Couldn't
> > > > you just compare the strings?
> > > >
> > > > Again, the division of labor is really odd here. It's the job of
> > > > astreamer_waldump_content() to skip things that aren't WAL files at
> > > > all, but it's the job of get_archive_wal_entry() to skip things that
> > > > are WAL files but not the one we want. I disagree with putting those
> > > > checks in completely separate parts of the code.
> > > >
> > >
> > > Keeping the timeline and segment start-end range checks inside the
> > > archive streamer creates a circular dependency that cannot be resolved
> > > without a 'dirty hack'. We must read the first available WAL file page
> > > to determine the wal_segment_size before it can calculate the target
> > > segment range. Moving the checks inside the streamer would make it
> > > impossible to process that initial file, as the necessary filtering
> > > parameters -- would still be unknown which would need to be skipped
> > > for the first read somehow. What if later we realized that the first
> > > WAL file which was allowed to be streamed by skipping that check is
> > > irrelevant and doesn't fall under the start-end segment range?
> > >
> >
> > Please have a look at the attached version, specifically patch 0005.
> > In astreamer_waldump_content(), I have moved the WAL file filtration
> > check from get_archive_wal_entry(). This check will be skipped during
> > the initial read in init_archive_reader(), which instead performs it
> > explicitly once it determines the WAL segment size and the start/end
> > segments.
> >
> > To access the WAL segment size inside astreamer_waldump_content(), I
> > have moved the WAL segment size variable into the XLogDumpPrivate
> > structure in the separate 0004 patch.
>
> Attached is an updated version including the aforesaid changes. It
> includes a new refactoring patch (0001) that moves the logic for
> identifying tar archives and their compression types from
> pg_basebackup and pg_verifybackup into a separate-reusable function,
> per a suggestion from Euler [1]. Additionally, I have added a test
> for the contrecord decoding to the main patch (now 0006).
>
> 1] http://postgr.es/m/3c8e7b02-2152-495a-a0b6-e37cf9286a70@app.fastmail.com
>
Rebased against the latest master, fixed typos in code comments, and
replaced palloc0 with palloc0_object.
Regards,
Amul
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Use size_t instead of Size in pg_waldump
- 3f8913f683ba 19 (unreleased) landed
-
More tar portability adjustments.
- 5079e420b92d 18.4 landed
- bab656bb87b0 19 (unreleased) landed
-
Further harden tests that might use not-so-compatible tar versions.
- ebba64c08d96 19 (unreleased) landed
- c4b7be4ecb12 18.4 landed
-
Harden astreamer tar parsing logic against archives it can't handle.
- bc30c704add5 19 (unreleased) landed
- 698eae7db7ab 18.4 landed
-
Fix pg_waldump/t/001_basic.pl with BSD tar on ZFS.
- 852de579a6ee 19 (unreleased) landed
-
Remove a low-value, high-risk optimization in pg_waldump.
- e9d723487b26 19 (unreleased) landed
-
Fix misuse of simplehash.h hash operations in pg_waldump.
- ff84efe4fdc0 19 (unreleased) landed
-
Fix file descriptor leakages in pg_waldump.
- 03b1e30e7aa5 19 (unreleased) landed
-
Fix poorly-sized buffers in astreamer compression modules.
- 6e243d81c549 19 (unreleased) landed
-
Remove read_archive_file()'s "count" parameter.
- ca1f1ade3f64 19 (unreleased) landed
-
Report detailed errors from XLogFindNextRecord() failures.
- 1c162c965a11 19 (unreleased) cited
-
Fix assorted bugs in archive_waldump.c.
- 860359ea029f 19 (unreleased) landed
-
Remove nonfunctional tar file trailer size check.
- 5868372bbfb9 19 (unreleased) landed
-
Fix finalization of decompressor astreamers.
- 9aa1fcc54702 19 (unreleased) landed
- 80785a527b9c 16.14 landed
- 6ccfc4492212 17.10 landed
- 5f9642614275 18.4 landed
- 5540f9c430f6 15.18 landed
-
Move tar detection and compression logic to common.
- c8a350a43982 19 (unreleased) landed
-
pg_verifybackup: Enable WAL parsing for tar-format backups
- b3cf461b3cf9 19 (unreleased) landed
-
pg_waldump: Add support for reading WAL from tar archives
- b15c1513984e 19 (unreleased) landed
-
pg_waldump: Preparatory refactoring for tar archive WAL decoding.
- f8a0cd267170 19 (unreleased) landed
-
pg_waldump: Remove file-level global WalSegSz.
- 9a446d0256dc 19 (unreleased) landed
-
pg_verifybackup: Verify tar-format backups.
- 8dfd31290279 18.0 cited