From 3528976822bf6fdbe08b0cb34516e29568625f65 Mon Sep 17 00:00:00 2001 From: Gustavo William Date: Sat, 22 Aug 2026 16:47:55 -0400 Subject: [PATCH] Prefetch upcoming blocks during incremental base backups Incremental base backups read each changed block one at a time, synchronously. Under workloads scattered changed blocks throughout the relation each one requires its own independent random-access read, paid for serially, one block after another. Close some of that gap by issuing posix_fadvise(POSIX_FADV_WILLNEED) hints for upcoming blocks before the read loop reaches them, giving the kernel a chance to fetch them into page cache asynchronously while we're still busy handling the current block. Hints are kept a bounded number of blocks ahead of the current read position, rather than issued for the whole file up front, sized by maintenance_io_concurrency. Signed-off-by: Gustavo William --- src/backend/backup/basebackup.c | 74 ++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c index e3c04ecd810..3b9d882a2fa 100644 --- a/src/backend/backup/basebackup.c +++ b/src/backend/backup/basebackup.c @@ -12,6 +12,7 @@ */ #include "postgres.h" +#include #include #include #include @@ -38,6 +39,7 @@ #include "replication/slot.h" #include "replication/walsender.h" #include "replication/walsender_private.h" +#include "storage/bufmgr.h" #include "storage/bufpage.h" #include "storage/checksum.h" #include "storage/dsm_impl.h" @@ -106,6 +108,12 @@ static off_t read_file_data_into_buffer(bbsink *sink, BlockNumber blkno, bool verify_checksum, int *checksum_failures); +#if defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) +static void prefetch_next_incremental_run(int fd, + BlockNumber *incremental_blocks, + unsigned num_incremental_blocks, + unsigned *pf_index); +#endif static void push_to_sink(bbsink *sink, pg_checksum_context *checksum_ctx, size_t *bytes_done, void *data, size_t length); static bool backup_checksums_verifiable(XLogRecPtr start_lsn); @@ -1591,7 +1599,10 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename, pgoff_t bytes_done = 0; bool verify_checksum = false; pg_checksum_context checksum_ctx; - int ibindex = 0; + unsigned ibindex = 0; +#if defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) + unsigned pf_index = 0; +#endif if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0) elog(ERROR, "could not initialize checksum of file \"%s\"", @@ -1607,6 +1618,26 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename, errmsg("could not open file \"%s\": %m", readfilename))); } +#if defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) + + /* + * Get a head start on reading the blocks we're about to send: a + * bounded window of readahead hints, sized by the tablespace's + * maintenance_io_concurrency, rather than hinting the whole file at + * once. The window is topped up as we consume it further down. + */ + if (incremental_blocks != NULL) + { + int prefetch_target; + + prefetch_target = maintenance_io_concurrency; + + while (prefetch_target-- > 0 && pf_index < num_incremental_blocks) + prefetch_next_incremental_run(fd, incremental_blocks, + num_incremental_blocks, &pf_index); + } +#endif + _tarWriteHeader(sink, tarfilename, NULL, statbuf, false); /* @@ -1730,6 +1761,15 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename, * supposed to include. */ relative_blkno = incremental_blocks[ibindex++]; + +#if defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) + /* Keep the prefetch window topped up as we consume runs. */ + if (pf_index < num_incremental_blocks) + prefetch_next_incremental_run(fd, incremental_blocks, + num_incremental_blocks, + &pf_index); +#endif + cnt = read_file_data_into_buffer(sink, readfilename, fd, relative_blkno * BLCKSZ, BLCKSZ, @@ -1840,6 +1880,38 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename, return true; } +#if defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) + +/* + * Advance *pf_index past the next run of contiguous block numbers in + * incremental_blocks (which is sorted in ascending order, so a run can be + * found with a simple forward scan) and issue a single readahead hint + * covering that whole run. This is advisory only: posix_fadvise() failures + * are ignored, since the worst that happens is that we don't get the + * intended prefetching benefit. + */ +static void +prefetch_next_incremental_run(int fd, BlockNumber *incremental_blocks, + unsigned num_incremental_blocks, + unsigned *pf_index) +{ + BlockNumber run_start = incremental_blocks[(*pf_index)++]; + unsigned run_len = 1; + + /* Merge contiguous blocks into a single run. */ + while (*pf_index < num_incremental_blocks && + incremental_blocks[*pf_index] == run_start + run_len) + { + run_len++; + (*pf_index)++; + } + + (void) posix_fadvise(fd, (off_t) run_start * BLCKSZ, + (off_t) run_len * BLCKSZ, + POSIX_FADV_WILLNEED); +} +#endif + /* * Read some more data from the file into the bbsink's buffer, verifying * checksums as required. -- 2.43.5