From 49f9413e3a1ab913f71e4799142106f981b5d54d Mon Sep 17 00:00:00 2001 From: HaoyuHuang Date: Mon, 27 Oct 2025 15:39:56 -0900 Subject: [PATCH 1/3] c --- contrib/pg_prewarm/pg_prewarm.c | 61 +++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/contrib/pg_prewarm/pg_prewarm.c b/contrib/pg_prewarm/pg_prewarm.c index fd139b2cd73..a7a3c15c66b 100644 --- a/contrib/pg_prewarm/pg_prewarm.c +++ b/contrib/pg_prewarm/pg_prewarm.c @@ -15,6 +15,7 @@ #include #include +#include "access/heapam.h" #include "access/relation.h" #include "fmgr.h" #include "miscadmin.h" @@ -185,29 +186,51 @@ pg_prewarm(PG_FUNCTION_ARGS) } else if (ptype == PREWARM_BUFFER) { - BlockNumber prefetch_block = first_block; - Oid nspOid; - int io_concurrency; + if (get_relkind_objtype(rel->rd_rel->relkind) == OBJECT_TABLE && forkNumber == MAIN_FORKNUM) + { + uint32 scan_flags = SO_TYPE_SEQSCAN | SO_TEMP_SNAPSHOT; + HeapTuple tuple; + Snapshot snapshot; + TableScanDesc scan; + + elog(LOG, "pg_prewarm: SeqScan relation \"%s\" starting %ld for %ld blocks", RelationGetRelationName(rel), first_block, last_block - first_block + 1); + // Use heap scan to set hint bits on every tuple. SO_ALLOW_PAGEMODE is intentionally NOT SET. + // Otherwise, when a page is all visible, tuple hint bits won't be set. + snapshot = RegisterSnapshot(GetTransactionSnapshot()); + scan = heap_beginscan(rel, snapshot, 0, NULL, NULL, scan_flags); + heap_setscanlimits(scan, first_block, last_block - first_block + 1); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + CHECK_FOR_INTERRUPTS(); + } + heap_endscan(scan); + } + else + { + BlockNumber prefetch_block = first_block; + Oid nspOid; + int io_concurrency; - nspOid = rel->rd_rel->reltablespace; - io_concurrency = get_tablespace_maintenance_io_concurrency(nspOid); + nspOid = rel->rd_rel->reltablespace; + io_concurrency = get_tablespace_maintenance_io_concurrency(nspOid); - /* - * In buffer mode, we actually pull the data into shared_buffers. - */ - for (block = first_block; block <= last_block; ++block) - { - Buffer buf; - BlockNumber prefetch_stop = block + Min(last_block - block + 1, - io_concurrency); - CHECK_FOR_INTERRUPTS(); - while (prefetch_block < prefetch_stop) + /* + * In buffer mode, we actually pull the data into shared_buffers. + */ + for (block = first_block; block <= last_block; ++block) { - PrefetchBuffer(rel, forkNumber, prefetch_block++); + Buffer buf; + BlockNumber prefetch_stop = block + Min(last_block - block + 1, + io_concurrency); + CHECK_FOR_INTERRUPTS(); + while (prefetch_block < prefetch_stop) + { + PrefetchBuffer(rel, forkNumber, prefetch_block++); + } + buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL); + ReleaseBuffer(buf); + ++blocks_done; } - buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL); - ReleaseBuffer(buf); - ++blocks_done; } } From e522e2d5bc25b83685740ffdd7c99dd0c4fdd2ea Mon Sep 17 00:00:00 2001 From: HaoyuHuang Date: Tue, 28 Oct 2025 10:18:18 -0900 Subject: [PATCH 2/3] c --- contrib/pg_prewarm/pg_prewarm.c | 62 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/contrib/pg_prewarm/pg_prewarm.c b/contrib/pg_prewarm/pg_prewarm.c index a7a3c15c66b..09f75ba5f7e 100644 --- a/contrib/pg_prewarm/pg_prewarm.c +++ b/contrib/pg_prewarm/pg_prewarm.c @@ -36,7 +36,8 @@ typedef enum { PREWARM_PREFETCH, PREWARM_READ, - PREWARM_BUFFER + PREWARM_BUFFER, + PREWARM_TUPLE, } PrewarmType; static PGIOAlignedBlock blockbuffer; @@ -89,6 +90,8 @@ pg_prewarm(PG_FUNCTION_ARGS) ptype = PREWARM_READ; else if (strcmp(ttype, "buffer") == 0) ptype = PREWARM_BUFFER; + else if (strcmp(ttype, "tuple") == 0) + ptype = PREWARM_TUPLE; else { ereport(ERROR, @@ -185,6 +188,33 @@ pg_prewarm(PG_FUNCTION_ARGS) } } else if (ptype == PREWARM_BUFFER) + { + BlockNumber prefetch_block = first_block; + Oid nspOid; + int io_concurrency; + + nspOid = rel->rd_rel->reltablespace; + io_concurrency = get_tablespace_maintenance_io_concurrency(nspOid); + + /* + * In buffer mode, we actually pull the data into shared_buffers. + */ + for (block = first_block; block <= last_block; ++block) + { + Buffer buf; + BlockNumber prefetch_stop = block + Min(last_block - block + 1, + io_concurrency); + CHECK_FOR_INTERRUPTS(); + while (prefetch_block < prefetch_stop) + { + PrefetchBuffer(rel, forkNumber, prefetch_block++); + } + buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL); + ReleaseBuffer(buf); + ++blocks_done; + } + } + else if (ptype == PREWARM_TUPLE) { if (get_relkind_objtype(rel->rd_rel->relkind) == OBJECT_TABLE && forkNumber == MAIN_FORKNUM) { @@ -204,33 +234,11 @@ pg_prewarm(PG_FUNCTION_ARGS) CHECK_FOR_INTERRUPTS(); } heap_endscan(scan); - } - else + } else { - BlockNumber prefetch_block = first_block; - Oid nspOid; - int io_concurrency; - - nspOid = rel->rd_rel->reltablespace; - io_concurrency = get_tablespace_maintenance_io_concurrency(nspOid); - - /* - * In buffer mode, we actually pull the data into shared_buffers. - */ - for (block = first_block; block <= last_block; ++block) - { - Buffer buf; - BlockNumber prefetch_stop = block + Min(last_block - block + 1, - io_concurrency); - CHECK_FOR_INTERRUPTS(); - while (prefetch_block < prefetch_stop) - { - PrefetchBuffer(rel, forkNumber, prefetch_block++); - } - buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL); - ReleaseBuffer(buf); - ++blocks_done; - } + ereport(INFO, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("tuple prewarm is only supported for heap relations on main fork"))); } } From 95abdee3240c4f6ec7c7c76864838f52add812c3 Mon Sep 17 00:00:00 2001 From: HaoyuHuang Date: Fri, 31 Oct 2025 13:55:08 -0900 Subject: [PATCH 3/3] c --- contrib/pg_prewarm/pg_prewarm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pg_prewarm/pg_prewarm.c b/contrib/pg_prewarm/pg_prewarm.c index 09f75ba5f7e..28ecee10ddc 100644 --- a/contrib/pg_prewarm/pg_prewarm.c +++ b/contrib/pg_prewarm/pg_prewarm.c @@ -197,8 +197,8 @@ pg_prewarm(PG_FUNCTION_ARGS) io_concurrency = get_tablespace_maintenance_io_concurrency(nspOid); /* - * In buffer mode, we actually pull the data into shared_buffers. - */ + * In buffer mode, we actually pull the data into shared_buffers. + */ for (block = first_block; block <= last_block; ++block) { Buffer buf;