From b6e8454d632e22ed39db0b343e7be4e9b91900ea Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Wed, 2 Apr 2025 09:52:54 -0400 Subject: [PATCH v9 1/3] Autoprewarm global objects separately Autoprewarm previously prewarmed global objects while prewarming blocks from objects from the first valid database it encountered. This was because you can't read in buffers without being connected to a database. Prewarming global objects and objects from a single database in the autoprewarm_database_main() function required a special case. Once we convert autoprewarm to use a read stream, this special case will have to be duplicated in multiple places. Instead, modify apw_load_buffers() to prewarm the shared objects in one invocation of autoprewarm_database_main() while connected to the first valid database. It is a bit fiddly but seems better than the alternative. --- contrib/pg_prewarm/autoprewarm.c | 51 ++++++++++++++------------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c index 73485a2323c..172654fee25 100644 --- a/contrib/pg_prewarm/autoprewarm.c +++ b/contrib/pg_prewarm/autoprewarm.c @@ -347,44 +347,41 @@ apw_load_buffers(void) apw_state->prewarm_start_idx = apw_state->prewarm_stop_idx = 0; apw_state->prewarmed_blocks = 0; - /* Get the info position of the first block of the next database. */ + /* + * Loop through the records and launch a database worker to process + * objects in each database. We'll stop at the boundary of each new + * database and prewarm those blocks before moving to the next. + */ while (apw_state->prewarm_start_idx < num_elements) { int j = apw_state->prewarm_start_idx; Oid current_db = blkinfo[j].database; /* - * Advance the prewarm_stop_idx to the first BlockInfoRecord that does - * not belong to this database. + * Advance the position to the first BlockInfoRecord that does not + * belong to the current database. */ - j++; - while (j < num_elements) + for (; j < num_elements; j++) { - if (current_db != blkinfo[j].database) - { - /* - * Combine BlockInfoRecords for global objects with those of - * the database. - */ - if (current_db != InvalidOid) - break; - current_db = blkinfo[j].database; - } - - j++; + if (blkinfo[j].database != current_db) + break; } /* - * If we reach this point with current_db == InvalidOid, then only - * BlockInfoRecords belonging to global objects exist. We can't - * prewarm without a database connection, so just bail out. + * We can't prewarm without a database connection, so if all of the + * records belong to global objects, we have to bail out. */ - if (current_db == InvalidOid) + if (current_db == InvalidOid && blkinfo[j].database == InvalidOid) break; + /* Connect to the first valid db to prewarm global objects. */ + if (current_db == InvalidOid) + current_db = blkinfo[j].database; + /* Configure stop point and database for next per-database worker. */ apw_state->prewarm_stop_idx = j; apw_state->database = current_db; + Assert(apw_state->prewarm_start_idx < apw_state->prewarm_stop_idx); /* If we've run out of free buffers, don't launch another worker. */ @@ -423,8 +420,8 @@ apw_load_buffers(void) } /* - * Prewarm all blocks for one database (and possibly also global objects, if - * those got grouped with this database). + * Prewarm all blocks for one database or global objects (while connected to a + * valid database). */ void autoprewarm_database_main(Datum main_arg) @@ -462,12 +459,8 @@ autoprewarm_database_main(Datum main_arg) CHECK_FOR_INTERRUPTS(); - /* - * Quit if we've reached records for another database. If previous - * blocks are of some global objects, then continue pre-warming. - */ - if (old_blk != NULL && old_blk->database != blk->database && - old_blk->database != 0) + /* Quit if we've reached records for another database. */ + if (old_blk != NULL && old_blk->database != blk->database) break; /* -- 2.34.1