Skip to content

Commit 98d3496

Browse files
melanieplagemannbyavuz
authored and
Commitfest Bot
committed
Refactor autoprewarm_database_main() in preparation for read stream
Autoprewarm prewarms blocks from a dump file representing the contents of shared buffers at the time it was dumped. It uses a sorted array of BlockInfoRecords, each representing a block from one of the cluster's databases and tables. autoprewarm_database_main() prewarms all the blocks from a single database. It is optimized to ensure we don't try to open the same relation or fork over and over again if it has been dropped or is invalid. The main loop handled this by carefully setting various local variables to sentinel values when a run of blocks should be skipped. This method won't work with the read stream API. A read stream can only be created for a single relation and fork combination. The callback has to be able to advance the position in the array to allow for reading ahead additional blocks, however the callback cannot try to open another relation or close the current relation. So, the main loop in autoprewarm_database_main() must also advance the position in the array of BlockInfoRecords. To make it compatible with the read stream API, change autoprewarm_database_main() to explicitly fast-forward in the array past the blocks belonging to an invalid relation or fork. This commit only implements the new control flow -- it does not use the read stream API. Co-authored-by: Nazir Bilal Yavuz <[email protected]> Co-authored-by: Melanie Plageman <[email protected]> Reviewed-by: Heikki Linnakangas <[email protected]> Reviewed-by: Daniel Gustafsson <[email protected]> Discussion: https://postgr.es/m/flat/CAN55FZ3n8Gd%2BhajbL%3D5UkGzu_aHGRqnn%2BxktXq2fuds%3D1AOR6Q%40mail.gmail.com
1 parent a6c7f21 commit 98d3496

File tree

1 file changed

+94
-78
lines changed

1 file changed

+94
-78
lines changed

contrib/pg_prewarm/autoprewarm.c

+94-78
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,9 @@ apw_load_buffers(void)
429429
void
430430
autoprewarm_database_main(Datum main_arg)
431431
{
432-
int pos;
433432
BlockInfoRecord *block_info;
434-
Relation rel = NULL;
435-
BlockNumber nblocks = 0;
436-
BlockInfoRecord *old_blk = NULL;
433+
int i;
434+
BlockInfoRecord blk;
437435
dsm_segment *seg;
438436

439437
/* Establish signal handlers; once that's done, unblock signals. */
@@ -449,114 +447,132 @@ autoprewarm_database_main(Datum main_arg)
449447
errmsg("could not map dynamic shared memory segment")));
450448
BackgroundWorkerInitializeConnectionByOid(apw_state->database, InvalidOid, 0);
451449
block_info = (BlockInfoRecord *) dsm_segment_address(seg);
452-
pos = apw_state->prewarm_start_idx;
450+
451+
i = apw_state->prewarm_start_idx;
452+
blk = block_info[i];
453453

454454
/*
455455
* Loop until we run out of blocks to prewarm or until we run out of free
456456
* buffers.
457457
*/
458-
while (pos < apw_state->prewarm_stop_idx && have_free_buffer())
458+
while (i < apw_state->prewarm_stop_idx && have_free_buffer())
459459
{
460-
BlockInfoRecord *blk = &block_info[pos++];
461-
Buffer buf;
460+
Oid tablespace = blk.tablespace;
461+
RelFileNumber filenumber = blk.filenumber;
462+
Oid reloid;
463+
Relation rel;
462464

463465
CHECK_FOR_INTERRUPTS();
464466

465467
/*
466468
* All blocks between prewarm_start_idx and prewarm_stop_idx should
467469
* belong either to global objects or the same database.
468470
*/
469-
Assert(blk->database == apw_state->database || blk->database == 0);
471+
Assert(blk.database == apw_state->database || blk.database == 0);
470472

471-
/*
472-
* As soon as we encounter a block of a new relation, close the old
473-
* relation. RelFileNumbers are only guaranteed to be unique within a
474-
* tablespace, so check that too.
475-
*
476-
* Note that rel will be NULL if try_relation_open failed previously;
477-
* in that case, there is nothing to close.
478-
*/
479-
if (old_blk != NULL &&
480-
(old_blk->tablespace != blk->tablespace ||
481-
old_blk->filenumber != blk->filenumber) &&
482-
rel != NULL)
483-
{
484-
relation_close(rel, AccessShareLock);
485-
rel = NULL;
486-
CommitTransactionCommand();
487-
}
473+
StartTransactionCommand();
488474

489-
/*
490-
* Try to open each new relation, but only once, when we first
491-
* encounter it. If it's been dropped, skip the associated blocks.
492-
*/
493-
if (old_blk == NULL ||
494-
old_blk->tablespace != blk->tablespace ||
495-
old_blk->filenumber != blk->filenumber)
475+
reloid = RelidByRelfilenumber(blk.tablespace, blk.filenumber);
476+
if (!OidIsValid(reloid) ||
477+
(rel = try_relation_open(reloid, AccessShareLock)) == NULL)
496478
{
497-
Oid reloid;
479+
/* We failed to open the relation, so there is nothing to close. */
480+
CommitTransactionCommand();
498481

499-
Assert(rel == NULL);
500-
StartTransactionCommand();
501-
reloid = RelidByRelfilenumber(blk->tablespace, blk->filenumber);
502-
if (OidIsValid(reloid))
503-
rel = try_relation_open(reloid, AccessShareLock);
482+
/*
483+
* Fast-forward to the next relation. We want to skip all of the
484+
* other records referencing this relation since we know we can't
485+
* open it. That way, we avoid repeatedly trying and failing to
486+
* open the same relation.
487+
*/
488+
for (; i < apw_state->prewarm_stop_idx; i++)
489+
{
490+
blk = block_info[i];
491+
if (blk.tablespace != tablespace ||
492+
blk.filenumber != filenumber)
493+
break;
494+
}
504495

505-
if (!rel)
506-
CommitTransactionCommand();
507-
}
508-
if (!rel)
509-
{
510-
old_blk = blk;
496+
/* Time to try and open our newfound relation */
511497
continue;
512498
}
513499

514-
/* Once per fork, check for fork existence and size. */
515-
if (old_blk == NULL ||
516-
old_blk->tablespace != blk->tablespace ||
517-
old_blk->filenumber != blk->filenumber ||
518-
old_blk->forknum != blk->forknum)
500+
/*
501+
* We have a relation; now let's loop until we find a valid fork of
502+
* the relation or we run out of free buffers. Once we've read from
503+
* all valid forks or run out of options, we'll close the relation and
504+
* move on.
505+
*/
506+
while (i < apw_state->prewarm_stop_idx &&
507+
blk.tablespace == tablespace &&
508+
blk.filenumber == filenumber &&
509+
have_free_buffer())
519510
{
511+
ForkNumber forknum = blk.forknum;
512+
BlockNumber nblocks;
513+
Buffer buf;
514+
520515
/*
521516
* smgrexists is not safe for illegal forknum, hence check whether
522517
* the passed forknum is valid before using it in smgrexists.
523518
*/
524-
if (blk->forknum > InvalidForkNumber &&
525-
blk->forknum <= MAX_FORKNUM &&
526-
smgrexists(RelationGetSmgr(rel), blk->forknum))
527-
nblocks = RelationGetNumberOfBlocksInFork(rel, blk->forknum);
528-
else
529-
nblocks = 0;
530-
}
519+
if (blk.forknum <= InvalidForkNumber ||
520+
blk.forknum > MAX_FORKNUM ||
521+
!smgrexists(RelationGetSmgr(rel), blk.forknum))
522+
{
523+
/*
524+
* Fast-forward to the next fork. We want to skip all of the
525+
* other records referencing this fork since we already know
526+
* it's not valid.
527+
*/
528+
for (; i < apw_state->prewarm_stop_idx; i++)
529+
{
530+
blk = block_info[i];
531+
if (blk.tablespace != tablespace ||
532+
blk.filenumber != filenumber ||
533+
blk.forknum != forknum)
534+
break;
535+
}
536+
537+
/* Time to check if this newfound fork is valid */
538+
continue;
539+
}
531540

532-
/* Check whether blocknum is valid and within fork file size. */
533-
if (blk->blocknum >= nblocks)
534-
{
535-
/* Move to next forknum. */
536-
old_blk = blk;
537-
continue;
538-
}
541+
nblocks = RelationGetNumberOfBlocksInFork(rel, blk.forknum);
539542

540-
/* Prewarm buffer. */
541-
buf = ReadBufferExtended(rel, blk->forknum, blk->blocknum, RBM_NORMAL,
542-
NULL);
543-
if (BufferIsValid(buf))
544-
{
545-
apw_state->prewarmed_blocks++;
546-
ReleaseBuffer(buf);
547-
}
543+
/* Prewarm buffers. */
544+
while (i < apw_state->prewarm_stop_idx &&
545+
blk.tablespace == tablespace &&
546+
blk.filenumber == filenumber &&
547+
blk.forknum == forknum &&
548+
have_free_buffer())
549+
{
550+
CHECK_FOR_INTERRUPTS();
548551

549-
old_blk = blk;
550-
}
552+
/* Check whether blocknum is valid and within fork file size. */
553+
if (blk.blocknum >= nblocks)
554+
{
555+
blk = block_info[++i];
556+
continue;
557+
}
551558

552-
dsm_detach(seg);
559+
buf = ReadBufferExtended(rel, blk.forknum, blk.blocknum, RBM_NORMAL,
560+
NULL);
561+
562+
blk = block_info[++i];
563+
if (!BufferIsValid(buf))
564+
break;
565+
566+
apw_state->prewarmed_blocks++;
567+
ReleaseBuffer(buf);
568+
}
569+
}
553570

554-
/* Release lock on previous relation. */
555-
if (rel)
556-
{
557571
relation_close(rel, AccessShareLock);
558572
CommitTransactionCommand();
559573
}
574+
575+
dsm_detach(seg);
560576
}
561577

562578
/*

0 commit comments

Comments
 (0)