Skip to content

Commit 00d7fb5

Browse files
committed
Assert that buffers are marked dirty before XLogRegisterBuffer().
Enforce the rule from transam/README in XLogRegisterBuffer(), and update callers to follow the rule. Hash indexes sometimes register clean pages as a part of the locking protocol, so provide a REGBUF_NO_CHANGE flag to support that use. Discussion: https://postgr.es/m/c84114f8-c7f1-5b57-f85a-3adc31e1a904@iki.fi Reviewed-by: Heikki Linnakangas
1 parent befe945 commit 00d7fb5

File tree

10 files changed

+118
-20
lines changed

10 files changed

+118
-20
lines changed

src/backend/access/gin/ginbtree.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -387,24 +387,22 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
387387
START_CRIT_SECTION();
388388

389389
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
390-
{
391390
XLogBeginInsert();
392-
XLogRegisterBuffer(0, stack->buffer, REGBUF_STANDARD);
393-
if (BufferIsValid(childbuf))
394-
XLogRegisterBuffer(1, childbuf, REGBUF_STANDARD);
395-
}
396391

397-
/* Perform the page update, and register any extra WAL data */
392+
/*
393+
* Perform the page update, dirty and register stack->buffer, and
394+
* register any extra WAL data.
395+
*/
398396
btree->execPlaceToPage(btree, stack->buffer, stack,
399397
insertdata, updateblkno, ptp_workspace);
400398

401-
MarkBufferDirty(stack->buffer);
402-
403399
/* An insert to an internal page finishes the split of the child. */
404400
if (BufferIsValid(childbuf))
405401
{
406402
GinPageGetOpaque(childpage)->flags &= ~GIN_INCOMPLETE_SPLIT;
407403
MarkBufferDirty(childbuf);
404+
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
405+
XLogRegisterBuffer(1, childbuf, REGBUF_STANDARD);
408406
}
409407

410408
if (RelationNeedsWAL(btree->index) && !btree->isBuild)

src/backend/access/gin/gindatapage.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,9 +721,12 @@ dataExecPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
721721
/* Apply changes to page */
722722
dataPlaceToPageLeafRecompress(buf, leaf);
723723

724+
MarkBufferDirty(buf);
725+
724726
/* If needed, register WAL data built by computeLeafRecompressWALData */
725727
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
726728
{
729+
XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
727730
XLogRegisterBufData(0, leaf->walinfo, leaf->walinfolen);
728731
}
729732
}
@@ -1155,6 +1158,8 @@ dataExecPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
11551158
pitem = (PostingItem *) insertdata;
11561159
GinDataPageAddPostingItem(page, pitem, off);
11571160

1161+
MarkBufferDirty(buf);
1162+
11581163
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
11591164
{
11601165
/*
@@ -1167,6 +1172,7 @@ dataExecPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
11671172
data.offset = off;
11681173
data.newitem = *pitem;
11691174

1175+
XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
11701176
XLogRegisterBufData(0, (char *) &data,
11711177
sizeof(ginxlogInsertDataInternal));
11721178
}

src/backend/access/gin/ginentrypage.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
571571
elog(ERROR, "failed to add item to index page in \"%s\"",
572572
RelationGetRelationName(btree->index));
573573

574+
MarkBufferDirty(buf);
575+
574576
if (RelationNeedsWAL(btree->index) && !btree->isBuild)
575577
{
576578
/*
@@ -583,6 +585,7 @@ entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
583585
data.isDelete = insertData->isDelete;
584586
data.offset = off;
585587

588+
XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
586589
XLogRegisterBufData(0, (char *) &data,
587590
offsetof(ginxlogInsertEntry, tuple));
588591
XLogRegisterBufData(0, (char *) insertData->entry,

src/backend/access/gin/ginfast.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,15 +397,16 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
397397
}
398398

399399
Assert((ptr - collectordata) <= collector->sumsize);
400+
401+
MarkBufferDirty(buffer);
402+
400403
if (needWal)
401404
{
402405
XLogRegisterBuffer(1, buffer, REGBUF_STANDARD);
403406
XLogRegisterBufData(1, collectordata, collector->sumsize);
404407
}
405408

406409
metadata->tailFreeSize = PageGetExactFreeSpace(page);
407-
408-
MarkBufferDirty(buffer);
409410
}
410411

411412
/*

src/backend/access/hash/hash.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -824,11 +824,16 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
824824
XLogRegisterData((char *) &xlrec, SizeOfHashDelete);
825825

826826
/*
827-
* bucket buffer needs to be registered to ensure that we can
828-
* acquire a cleanup lock on it during replay.
827+
* bucket buffer was not changed, but still needs to be
828+
* registered to ensure that we can acquire a cleanup lock on
829+
* it during replay.
829830
*/
830831
if (!xlrec.is_primary_bucket_page)
831-
XLogRegisterBuffer(0, bucket_buf, REGBUF_STANDARD | REGBUF_NO_IMAGE);
832+
{
833+
uint8 flags = REGBUF_STANDARD | REGBUF_NO_IMAGE | REGBUF_NO_CHANGE;
834+
835+
XLogRegisterBuffer(0, bucket_buf, flags);
836+
}
832837

833838
XLogRegisterBuffer(1, buf, REGBUF_STANDARD);
834839
XLogRegisterBufData(1, (char *) deletable,

src/backend/access/hash/hashovfl.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -658,11 +658,15 @@ _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
658658
XLogRegisterData((char *) &xlrec, SizeOfHashSqueezePage);
659659

660660
/*
661-
* bucket buffer needs to be registered to ensure that we can acquire
662-
* a cleanup lock on it during replay.
661+
* bucket buffer was not changed, but still needs to be registered to
662+
* ensure that we can acquire a cleanup lock on it during replay.
663663
*/
664664
if (!xlrec.is_prim_bucket_same_wrt)
665-
XLogRegisterBuffer(0, bucketbuf, REGBUF_STANDARD | REGBUF_NO_IMAGE);
665+
{
666+
uint8 flags = REGBUF_STANDARD | REGBUF_NO_IMAGE | REGBUF_NO_CHANGE;
667+
668+
XLogRegisterBuffer(0, bucketbuf, flags);
669+
}
666670

667671
XLogRegisterBuffer(1, wbuf, REGBUF_STANDARD);
668672
if (xlrec.ntups > 0)
@@ -960,11 +964,16 @@ _hash_squeezebucket(Relation rel,
960964
XLogRegisterData((char *) &xlrec, SizeOfHashMovePageContents);
961965

962966
/*
963-
* bucket buffer needs to be registered to ensure that
964-
* we can acquire a cleanup lock on it during replay.
967+
* bucket buffer was not changed, but still needs to
968+
* be registered to ensure that we can acquire a
969+
* cleanup lock on it during replay.
965970
*/
966971
if (!xlrec.is_prim_bucket_same_wrt)
967-
XLogRegisterBuffer(0, bucket_buf, REGBUF_STANDARD | REGBUF_NO_IMAGE);
972+
{
973+
int flags = REGBUF_STANDARD | REGBUF_NO_IMAGE | REGBUF_NO_CHANGE;
974+
975+
XLogRegisterBuffer(0, bucket_buf, flags);
976+
}
968977

969978
XLogRegisterBuffer(1, wbuf, REGBUF_STANDARD);
970979
XLogRegisterBufData(1, (char *) itup_offsets,

src/backend/access/transam/xloginsert.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,20 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
248248
Assert(!((flags & REGBUF_FORCE_IMAGE) && (flags & (REGBUF_NO_IMAGE))));
249249
Assert(begininsert_called);
250250

251+
/*
252+
* Ordinarily, buffer should be exclusive-locked and marked dirty before
253+
* we get here, otherwise we could end up violating one of the rules in
254+
* access/transam/README.
255+
*
256+
* Some callers intentionally register a clean page and never update that
257+
* page's LSN; in that case they can pass the flag REGBUF_NO_CHANGE to
258+
* bypass these checks.
259+
*/
260+
#ifdef USE_ASSERT_CHECKING
261+
if (!(flags & REGBUF_NO_CHANGE))
262+
Assert(BufferIsExclusiveLocked(buffer) && BufferIsDirty(buffer));
263+
#endif
264+
251265
if (block_id >= max_registered_block_id)
252266
{
253267
if (block_id >= max_registered_buffers)
@@ -1313,8 +1327,8 @@ log_newpage_range(Relation rel, ForkNumber forknum,
13131327
START_CRIT_SECTION();
13141328
for (i = 0; i < nbufs; i++)
13151329
{
1316-
XLogRegisterBuffer(i, bufpack[i], flags);
13171330
MarkBufferDirty(bufpack[i]);
1331+
XLogRegisterBuffer(i, bufpack[i], flags);
13181332
}
13191333

13201334
recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);

src/backend/storage/buffer/bufmgr.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,6 +2098,65 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
20982098
return first_block;
20992099
}
21002100

2101+
/*
2102+
* BufferIsExclusiveLocked
2103+
*
2104+
* Checks if buffer is exclusive-locked.
2105+
*
2106+
* Buffer must be pinned.
2107+
*/
2108+
bool
2109+
BufferIsExclusiveLocked(Buffer buffer)
2110+
{
2111+
BufferDesc *bufHdr;
2112+
2113+
if (BufferIsLocal(buffer))
2114+
{
2115+
int bufid = -buffer - 1;
2116+
2117+
bufHdr = GetLocalBufferDescriptor(bufid);
2118+
}
2119+
else
2120+
{
2121+
bufHdr = GetBufferDescriptor(buffer - 1);
2122+
}
2123+
2124+
Assert(BufferIsPinned(buffer));
2125+
return LWLockHeldByMeInMode(BufferDescriptorGetContentLock(bufHdr),
2126+
LW_EXCLUSIVE);
2127+
}
2128+
2129+
/*
2130+
* BufferIsDirty
2131+
*
2132+
* Checks if buffer is already dirty.
2133+
*
2134+
* Buffer must be pinned and exclusive-locked. (Without an exclusive lock,
2135+
* the result may be stale before it's returned.)
2136+
*/
2137+
bool
2138+
BufferIsDirty(Buffer buffer)
2139+
{
2140+
BufferDesc *bufHdr;
2141+
2142+
if (BufferIsLocal(buffer))
2143+
{
2144+
int bufid = -buffer - 1;
2145+
2146+
bufHdr = GetLocalBufferDescriptor(bufid);
2147+
}
2148+
else
2149+
{
2150+
bufHdr = GetBufferDescriptor(buffer - 1);
2151+
}
2152+
2153+
Assert(BufferIsPinned(buffer));
2154+
Assert(LWLockHeldByMeInMode(BufferDescriptorGetContentLock(bufHdr),
2155+
LW_EXCLUSIVE));
2156+
2157+
return pg_atomic_read_u32(&bufHdr->state) & BM_DIRTY;
2158+
}
2159+
21012160
/*
21022161
* MarkBufferDirty
21032162
*

src/include/access/xloginsert.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* will be skipped) */
3838
#define REGBUF_KEEP_DATA 0x10 /* include data even if a full-page image
3939
* is taken */
40+
#define REGBUF_NO_CHANGE 0x20 /* intentionally register clean buffer */
4041

4142
/* prototypes for public functions in xloginsert.c: */
4243
extern void XLogBeginInsert(void);

src/include/storage/bufmgr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ extern Buffer ReadBufferWithoutRelcache(RelFileLocator rlocator,
179179
bool permanent);
180180
extern void ReleaseBuffer(Buffer buffer);
181181
extern void UnlockReleaseBuffer(Buffer buffer);
182+
extern bool BufferIsExclusiveLocked(Buffer buffer);
183+
extern bool BufferIsDirty(Buffer buffer);
182184
extern void MarkBufferDirty(Buffer buffer);
183185
extern void IncrBufferRefCount(Buffer buffer);
184186
extern void CheckBufferIsPinnedOnce(Buffer buffer);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy