pg_walinspect: add functions to locate and list WAL by time and LSN

From: Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com>
To: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: pg_walinspect: add functions to locate and list WAL by time and LSN
Date: 2026-09-18 04:05:24
Message-ID: 80E9F0AD-CFC5-4BE5-81DE-D8FE35E10A1C@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

I'd like to propose adding two new SQL functions to pg_walinspect.

# Motivation

During HOW2026 in Jinan this April, a DBA talked to me that PG lacks an easy way to locate WAL files by time.

He described a scenario where somebody had done something wrong in a production database, for example accidentally deleting important data. The administrator knew that the incident had happened at around 1am the last night and wanted to confirm what had happened by analyzing WAL.

The problem was that, with thousands of WAL files in pg_wal, where should he start?

# Analysis

After looking into this use case, I think the underlying requirement is slightly different from simply finding a WAL file by timestamp.

Given an approximate point in time, the user really wants an LSN range that is likely to contain the WAL records associated with the operation of interest, for example a DELETE.

PostgreSQL already provides several related facilities:

* pg_ls_waldir() lists WAL files and their modification times.
* pg_split_walfile_name() extracts the segment number and timeline ID from a WAL file name.
* pg_walfile_name() maps an LSN to a WAL file name.
* pg_walinspect can inspect WAL records by LSN.
* pg_waldump can dump WAL records by WAL file or LSN range.
* There are also a number of third-party WAL-mining tools.

However, WAL file modification times don't necessarily reflect the chronological position of the WAL segment. For example, on my local cluster:
```
% ls -l
total 65536
-rw-------@ 1 chaol staff 16777216 Sep 17 16:21 000000010000000000000001
-rw-------@ 1 chaol staff 16777216 Sep 16 13:24 000000010000000000000002
drwx------@ 2 chaol staff 64 Sep 16 13:17 archive_status
drwx------@ 2 chaol staff 64 Sep 16 13:17 summaries
```

Here, segment 1 has a later modification time than segment 2. Since modification times do not necessarily follow WAL segment order, using them to locate the WAL corresponding to a particular point in time can be difficult and sometimes confusing.

So fundamentally, what is missing is an easy way to map a wall-clock time to a useful WAL/LSN range.

# Design

I propose adding two SQL functions.

## pg_get_wal_location_at_time(target_time timestamptz, before interval DEFAULT '1 minute', after interval DEFAULT '1 minute’)

Given a target time and optional before and after intervals, it returns a WAL range around that time.

Not all WAL records contain timestamps, so the implementation reuses the existing timestamp-extraction logic from xlogrecovery.c, exposed by this patch as GetXLogRecordTimestamp(), to identify timestamp-bearing WAL records.

## pg_get_wal_files(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL)

Given a start LSN and an optional end LSN, it returns the WAL files covering that LSN range.

Unlike pg_walfile_name(), which returns only a WAL file name for a single LSN, this function returns each relevant WAL file together with that segment's start and end LSNs.

# Implementation

Both functions are added to the pg_walinspect extension.

There is already quite a bit of reusable WAL-reading infrastructure there, and semantically it also seems like the natural place for this functionality.

The implementation builds on the existing pg_walinspect and XLog reader infrastructure. It does not introduce a new WAL format or a separate WAL parser.

# Demo

For example, suppose I deleted a tuple at around 16:11 yesterday. I can first locate the WAL range around that time:
```
evantest=# SELECT * FROM pg_get_wal_location_at_time('2026-09-17 16:11:08.317383+08', before=>interval '5 minute', after=>interval '5 minute');
start_timestamp | start_lsn | end_timestamp | end_lsn
------------------------------+------------+-----------------------------+------------
2026-09-17 15:57:44.67121+08 | 0/01D05FA0 | 2026-09-18 10:56:13.9463+08 | 0/01D28900
(1 row)
```

Here, you may notice that end_lsn is much later than the specified timestamp plus 5 minutes. I chose this example intentionally.

As mentioned above, not all WAL records carry a timestamp. In this demo database, no timestamp-bearing WAL records were generated for quite some time after the target period, so the next usable timestamp did not appear until today. As a result, the returned WAL range is much wider than the requested five-minute window.

This also illustrates why the function returns an approximate WAL range based on timestamp-bearing records rather than an exact time-to-LSN mapping.

Then I can inspect that LSN range:
```
evantest=# SELECT start_lsn, end_lsn, xid, resource_manager, record_type FROM pg_get_wal_records_info('0/01D05FA0', '0/01D28900') WHERE record_type in ('DELETE', 'COMMIT');
start_lsn | end_lsn | xid | resource_manager | record_type
------------+------------+-----+------------------+-------------
0/01D05FA0 | 0/01D05FC8 | 681 | Transaction | COMMIT
0/01D0B530 | 0/01D0B558 | 682 | Transaction | COMMIT
0/01D0B590 | 0/01D0B5C8 | 683 | Heap | DELETE
0/01D0B5C8 | 0/01D0B5F0 | 683 | Transaction | COMMIT
(4 rows)
```

Now we know the LSN range for the DELETE. If we want to determine which WAL file contains it:
```
evantest=# SELECT * FROM pg_get_wal_files('0/01D0B590', '0/01D0B5C8');
wal_file | segment_start_lsn | segment_end_lsn
--------------------------+-------------------+-----------------
000000010000000000000001 | 0/01000000 | 0/02000000
(1 row)
```

From there, the WAL file can be examined further with pg_waldump or other WAL-mining tools.

PFA v1. Reviews and comments and suggestions are greatly appreciated.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

Attachment Content-Type Size
v1-0001-pg_walinspect-add-functions-to-locate-and-list-WA.patch application/octet-stream 59.5 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Amit Kapila 2026-09-18 04:13:31 Re: Distinguish publication exclusions in object addresses
Previous Message Haibo Yan 2026-09-18 03:57:15 Re: Global temporary tables