Skip to content

Commit c5315f4

Browse files
committed
Cache smgrnblocks() results in recovery.
Avoid repeatedly calling lseek(SEEK_END) during recovery by caching the size of each fork. For now, we can't use the same technique in other processes, because we lack a shared invalidation mechanism. Do this by generalizing the pre-existing caching used by FSM and VM to support all forks. Discussion: https://postgr.es/m/CAEepm%3D3SSw-Ty1DFcK%3D1rU-K6GSzYzfdD4d%2BZwapdN7dTa6%3DnQ%40mail.gmail.com
1 parent e3931d0 commit c5315f4

File tree

6 files changed

+66
-46
lines changed

6 files changed

+66
-46
lines changed

contrib/pg_visibility/pg_visibility.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ pg_truncate_visibility_map(PG_FUNCTION_ARGS)
392392
check_relation_relkind(rel);
393393

394394
RelationOpenSmgr(rel);
395-
rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
395+
rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] = InvalidBlockNumber;
396396

397397
block = visibilitymap_prepare_truncate(rel, 0);
398398
if (BlockNumberIsValid(block))

src/backend/access/heap/visibilitymap.c

+8-10
Original file line numberDiff line numberDiff line change
@@ -561,17 +561,16 @@ vm_readbuf(Relation rel, BlockNumber blkno, bool extend)
561561
* If we haven't cached the size of the visibility map fork yet, check it
562562
* first.
563563
*/
564-
if (rel->rd_smgr->smgr_vm_nblocks == InvalidBlockNumber)
564+
if (rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] == InvalidBlockNumber)
565565
{
566566
if (smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM))
567-
rel->rd_smgr->smgr_vm_nblocks = smgrnblocks(rel->rd_smgr,
568-
VISIBILITYMAP_FORKNUM);
567+
smgrnblocks(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
569568
else
570-
rel->rd_smgr->smgr_vm_nblocks = 0;
569+
rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] = 0;
571570
}
572571

573572
/* Handle requests beyond EOF */
574-
if (blkno >= rel->rd_smgr->smgr_vm_nblocks)
573+
if (blkno >= rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM])
575574
{
576575
if (extend)
577576
vm_extend(rel, blkno + 1);
@@ -641,11 +640,13 @@ vm_extend(Relation rel, BlockNumber vm_nblocks)
641640
* Create the file first if it doesn't exist. If smgr_vm_nblocks is
642641
* positive then it must exist, no need for an smgrexists call.
643642
*/
644-
if ((rel->rd_smgr->smgr_vm_nblocks == 0 ||
645-
rel->rd_smgr->smgr_vm_nblocks == InvalidBlockNumber) &&
643+
if ((rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] == 0 ||
644+
rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] == InvalidBlockNumber) &&
646645
!smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM))
647646
smgrcreate(rel->rd_smgr, VISIBILITYMAP_FORKNUM, false);
648647

648+
/* Invalidate cache so that smgrnblocks() asks the kernel. */
649+
rel->rd_smgr->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] = InvalidBlockNumber;
649650
vm_nblocks_now = smgrnblocks(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
650651

651652
/* Now extend the file */
@@ -667,8 +668,5 @@ vm_extend(Relation rel, BlockNumber vm_nblocks)
667668
*/
668669
CacheInvalidateSmgr(rel->rd_smgr->smgr_rnode);
669670

670-
/* Update local cache with the up-to-date size */
671-
rel->rd_smgr->smgr_vm_nblocks = vm_nblocks_now;
672-
673671
UnlockRelationForExtension(rel, ExclusiveLock);
674672
}

src/backend/catalog/storage.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
290290
* Make sure smgr_targblock etc aren't pointing somewhere past new end
291291
*/
292292
rel->rd_smgr->smgr_targblock = InvalidBlockNumber;
293-
rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber;
294-
rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;
293+
for (int i = 0; i <= MAX_FORKNUM; ++i)
294+
rel->rd_smgr->smgr_cached_nblocks[i] = InvalidBlockNumber;
295295

296296
/* Prepare for truncation of MAIN fork of the relation */
297297
forks[nforks] = MAIN_FORKNUM;

src/backend/storage/freespace/freespace.c

+14-13
Original file line numberDiff line numberDiff line change
@@ -541,18 +541,19 @@ fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
541541
* value might be stale. (We send smgr inval messages on truncation, but
542542
* not on extension.)
543543
*/
544-
if (rel->rd_smgr->smgr_fsm_nblocks == InvalidBlockNumber ||
545-
blkno >= rel->rd_smgr->smgr_fsm_nblocks)
544+
if (rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] == InvalidBlockNumber ||
545+
blkno >= rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM])
546546
{
547+
/* Invalidate the cache so smgrnblocks asks the kernel. */
548+
rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] = InvalidBlockNumber;
547549
if (smgrexists(rel->rd_smgr, FSM_FORKNUM))
548-
rel->rd_smgr->smgr_fsm_nblocks = smgrnblocks(rel->rd_smgr,
549-
FSM_FORKNUM);
550+
smgrnblocks(rel->rd_smgr, FSM_FORKNUM);
550551
else
551-
rel->rd_smgr->smgr_fsm_nblocks = 0;
552+
rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] = 0;
552553
}
553554

554555
/* Handle requests beyond EOF */
555-
if (blkno >= rel->rd_smgr->smgr_fsm_nblocks)
556+
if (blkno >= rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM])
556557
{
557558
if (extend)
558559
fsm_extend(rel, blkno + 1);
@@ -621,14 +622,17 @@ fsm_extend(Relation rel, BlockNumber fsm_nblocks)
621622
RelationOpenSmgr(rel);
622623

623624
/*
624-
* Create the FSM file first if it doesn't exist. If smgr_fsm_nblocks is
625-
* positive then it must exist, no need for an smgrexists call.
625+
* Create the FSM file first if it doesn't exist. If
626+
* smgr_cached_nblocks[FSM_FORKNUM] is positive then it must exist, no
627+
* need for an smgrexists call.
626628
*/
627-
if ((rel->rd_smgr->smgr_fsm_nblocks == 0 ||
628-
rel->rd_smgr->smgr_fsm_nblocks == InvalidBlockNumber) &&
629+
if ((rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] == 0 ||
630+
rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] == InvalidBlockNumber) &&
629631
!smgrexists(rel->rd_smgr, FSM_FORKNUM))
630632
smgrcreate(rel->rd_smgr, FSM_FORKNUM, false);
631633

634+
/* Invalidate cache so that smgrnblocks() asks the kernel. */
635+
rel->rd_smgr->smgr_cached_nblocks[FSM_FORKNUM] = InvalidBlockNumber;
632636
fsm_nblocks_now = smgrnblocks(rel->rd_smgr, FSM_FORKNUM);
633637

634638
while (fsm_nblocks_now < fsm_nblocks)
@@ -640,9 +644,6 @@ fsm_extend(Relation rel, BlockNumber fsm_nblocks)
640644
fsm_nblocks_now++;
641645
}
642646

643-
/* Update local cache with the up-to-date size */
644-
rel->rd_smgr->smgr_fsm_nblocks = fsm_nblocks_now;
645-
646647
UnlockRelationForExtension(rel, ExclusiveLock);
647648
}
648649

src/backend/storage/smgr/smgr.c

+36-13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
#include "postgres.h"
1919

20+
#include "access/xlog.h"
2021
#include "lib/ilist.h"
2122
#include "storage/bufmgr.h"
2223
#include "storage/ipc.h"
@@ -174,8 +175,8 @@ smgropen(RelFileNode rnode, BackendId backend)
174175
/* hash_search already filled in the lookup key */
175176
reln->smgr_owner = NULL;
176177
reln->smgr_targblock = InvalidBlockNumber;
177-
reln->smgr_fsm_nblocks = InvalidBlockNumber;
178-
reln->smgr_vm_nblocks = InvalidBlockNumber;
178+
for (int i = 0; i <= MAX_FORKNUM; ++i)
179+
reln->smgr_cached_nblocks[i] = InvalidBlockNumber;
179180
reln->smgr_which = 0; /* we only have md.c at present */
180181

181182
/* implementation-specific initialization */
@@ -464,6 +465,16 @@ smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
464465
{
465466
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
466467
buffer, skipFsync);
468+
469+
/*
470+
* Normally we expect this to increase nblocks by one, but if the cached
471+
* value isn't as expected, just invalidate it so the next call asks the
472+
* kernel.
473+
*/
474+
if (reln->smgr_cached_nblocks[forknum] == blocknum)
475+
reln->smgr_cached_nblocks[forknum] = blocknum + 1;
476+
else
477+
reln->smgr_cached_nblocks[forknum] = InvalidBlockNumber;
467478
}
468479

469480
/*
@@ -537,7 +548,20 @@ smgrwriteback(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
537548
BlockNumber
538549
smgrnblocks(SMgrRelation reln, ForkNumber forknum)
539550
{
540-
return smgrsw[reln->smgr_which].smgr_nblocks(reln, forknum);
551+
BlockNumber result;
552+
553+
/*
554+
* For now, we only use cached values in recovery due to lack of a shared
555+
* invalidation mechanism for changes in file size.
556+
*/
557+
if (InRecovery && reln->smgr_cached_nblocks[forknum] != InvalidBlockNumber)
558+
return reln->smgr_cached_nblocks[forknum];
559+
560+
result = smgrsw[reln->smgr_which].smgr_nblocks(reln, forknum);
561+
562+
reln->smgr_cached_nblocks[forknum] = result;
563+
564+
return result;
541565
}
542566

543567
/*
@@ -576,20 +600,19 @@ smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nb
576600
/* Do the truncation */
577601
for (i = 0; i < nforks; i++)
578602
{
603+
/* Make the cached size is invalid if we encounter an error. */
604+
reln->smgr_cached_nblocks[forknum[i]] = InvalidBlockNumber;
605+
579606
smgrsw[reln->smgr_which].smgr_truncate(reln, forknum[i], nblocks[i]);
580607

581608
/*
582-
* We might as well update the local smgr_fsm_nblocks and
583-
* smgr_vm_nblocks settings. The smgr cache inval message that this
584-
* function sent will cause other backends to invalidate their copies
585-
* of smgr_fsm_nblocks and smgr_vm_nblocks, and these ones too at the
586-
* next command boundary. But these ensure they aren't outright wrong
587-
* until then.
609+
* We might as well update the local smgr_cached_nblocks values. The
610+
* smgr cache inval message that this function sent will cause other
611+
* backends to invalidate their copies of smgr_fsm_nblocks and
612+
* smgr_vm_nblocks, and these ones too at the next command boundary.
613+
* But these ensure they aren't outright wrong until then.
588614
*/
589-
if (forknum[i] == FSM_FORKNUM)
590-
reln->smgr_fsm_nblocks = nblocks[i];
591-
if (forknum[i] == VISIBILITYMAP_FORKNUM)
592-
reln->smgr_vm_nblocks = nblocks[i];
615+
reln->smgr_cached_nblocks[forknum[i]] = nblocks[i];
593616
}
594617
}
595618

src/include/storage/smgr.h

+5-7
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,13 @@ typedef struct SMgrRelationData
4545
struct SMgrRelationData **smgr_owner;
4646

4747
/*
48-
* These next three fields are not actually used or manipulated by smgr,
49-
* except that they are reset to InvalidBlockNumber upon a cache flush
50-
* event (in particular, upon truncation of the relation). Higher levels
51-
* store cached state here so that it will be reset when truncation
52-
* happens. In all three cases, InvalidBlockNumber means "unknown".
48+
* The following fields are reset to InvalidBlockNumber upon a cache flush
49+
* event, and hold the last known size for each fork. This information is
50+
* currently only reliable during recovery, since there is no cache
51+
* invalidation for fork extension.
5352
*/
5453
BlockNumber smgr_targblock; /* current insertion target block */
55-
BlockNumber smgr_fsm_nblocks; /* last known size of fsm fork */
56-
BlockNumber smgr_vm_nblocks; /* last known size of vm fork */
54+
BlockNumber smgr_cached_nblocks[MAX_FORKNUM + 1]; /* last known size */
5755

5856
/* additional public fields may someday exist here */
5957

0 commit comments

Comments
 (0)