From: Robert Haas Date: Fri, 15 Oct 2021 19:35:22 +0000 (-0400) Subject: require std pages, and that newly insert pages are visibly non-empty X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=5d0f1c3e49b880665ed2db44f19879c36b690105;p=users%2Frhaas%2Fpostgres.git require std pages, and that newly insert pages are visibly non-empty --- diff --git a/src/backend/access/conveyor/README b/src/backend/access/conveyor/README index 0314abc664..cd57b2164f 100644 --- a/src/backend/access/conveyor/README +++ b/src/backend/access/conveyor/README @@ -22,10 +22,10 @@ allocated to them can be reused for new logical pages. Conceptually, a relation fork organized as a conveyor belt has three parts: - Payload. The payload is whatever data the user of this module wishes - to store. The conveyor belt system avoids making any assumptions about how - payload pages are used, with one exception: it's expected that after - a payload page is initialized, PageIsNew() will return false. Apart from - that, this module doesn't know or care about their contents. + to store. The conveyor belt doesn't care what you store in a payload page, + but it does require that you store something: each time a payload page is + initialized, it must end up with either pd_lower > SizeOfPageHeaderData, + or pd_lower < BLCKSZ. - Index. The index translates logical page numbers to physical block numbers. The intention is that pages might be physically relocated - e.g. diff --git a/src/backend/access/conveyor/cbmodify.c b/src/backend/access/conveyor/cbmodify.c index 2905b4e7a5..95f1e8dca5 100644 --- a/src/backend/access/conveyor/cbmodify.c +++ b/src/backend/access/conveyor/cbmodify.c @@ -98,7 +98,7 @@ cb_create_fsmpage(RelFileNode *rnode, void cb_insert_payload_page(RelFileNode *rnode, ForkNumber fork, Buffer metabuffer, BlockNumber payloadblock, Buffer payloadbuffer, - bool needs_xlog, bool page_std) + bool needs_xlog) { Page metapage; Page payloadpage; @@ -117,16 +117,12 @@ cb_insert_payload_page(RelFileNode *rnode, ForkNumber fork, Buffer metabuffer, if (needs_xlog) { XLogRecPtr lsn; - int flags = REGBUF_FORCE_IMAGE; - - if (page_std) - flags |= REGBUF_STANDARD; XLogBeginInsert(); XLogRegisterBlock(0, rnode, fork, CONVEYOR_METAPAGE, metapage, REGBUF_STANDARD); XLogRegisterBlock(1, rnode, fork, payloadblock, - payloadpage, flags); + payloadpage, REGBUF_FORCE_IMAGE | REGBUF_STANDARD); lsn = XLogInsert(RM_CONVEYOR_ID, XLOG_CONVEYOR_INSERT_PAYLOAD_PAGE); diff --git a/src/backend/access/conveyor/conveyor.c b/src/backend/access/conveyor/conveyor.c index 395bd70ea6..70935cfd9f 100644 --- a/src/backend/access/conveyor/conveyor.c +++ b/src/backend/access/conveyor/conveyor.c @@ -30,6 +30,7 @@ static CBSegNo ConveyorSearchFSMPages(ConveyorBelt *cb, static Buffer ConveyorBeltExtend(ConveyorBelt *cb, BlockNumber blkno, BlockNumber *possibly_not_on_disk_blkno); static Buffer ConveyorBeltRead(ConveyorBelt *cb, BlockNumber blkno, int mode); +static Buffer ConveyorBeltPageIsUnused(Page page); /* * Handle used to mediate access to a conveyor belt. @@ -137,7 +138,7 @@ ConveyorBeltOpen(Relation rel, ForkNumber fork, MemoryContext mcxt) * page = BufferGetPage(buffer); * START_CRIT_SECTION(); * // set page contents - * ConveyorBeltPerformInsert(cb, buffer, page_std); + * ConveyorBeltPerformInsert(cb, buffer); * END_CRIT_SECTION(); * ConveyorBeltCleanupInsert(cb, buffer); * @@ -148,6 +149,11 @@ ConveyorBeltOpen(Relation rel, ForkNumber fork, MemoryContext mcxt) * so could result in undetected deadlock on the buffer LWLocks, or cause * a relcache flush that would break ConveyorBeltPerformInsert(). * + * Also note that the "set page contents" step must put some data in the + * page, so that either pd_lower is greater than the minimum value + * (SizeOfPageHeaderData) or pd_upper is less than the maximum value + * (BLCKSZ). + * * In future, we might want to provide the caller with an alternative to * calling ConveyorBeltPerformInsert, because that just logs an FPI for * the new page, and some callers might prefer to manage their own xlog @@ -488,11 +494,11 @@ ConveyorBeltGetNewPage(ConveyorBelt *cb, CBPageNo *pageno) ConveyorBeltRead(cb, next_blkno, BUFFER_LOCK_EXCLUSIVE); /* - * If the target buffer is still new, we're done. Otherwise, + * If the target buffer is still unused, we're done. Otherwise, * someone else grabbed that page before we did, so we must fall * through and retry. */ - if (PageIsNew(BufferGetPage(buffer))) + if (ConveyorBeltPageIsUnused(BufferGetPage(buffer))) { /* * Remember things that we'll need to know when the caller @@ -624,7 +630,7 @@ ConveyorBeltGetNewPage(ConveyorBelt *cb, CBPageNo *pageno) * See ConveyorBeltGetNewPage for the intended usage of this fucntion. */ void -ConveyorBeltPerformInsert(ConveyorBelt *cb, Buffer buffer, bool page_std) +ConveyorBeltPerformInsert(ConveyorBelt *cb, Buffer buffer) { bool needs_xlog; @@ -643,6 +649,16 @@ ConveyorBeltPerformInsert(ConveyorBelt *cb, Buffer buffer, bool page_std) cb->cb_insert_buffer, buffer); } + /* + * ConveyorBeltPageIsUnused is used by ConveyorBeltGetNewPage to figure + * out whether a concurrent inserter got there first. Here, we're the + * concurrent inserter, and must have initialized the page in a way that + * makes that function return false for the newly-inserted page, so that + * other backends can tell we got here first. + */ + if (ConveyorBeltPageIsUnused(BufferGetPage(buffer))) + elog(ERROR, "can't insert an unused page"); + /* Caller should be doing this inside a critical section. */ Assert(CritSectionCount > 0); @@ -657,7 +673,7 @@ ConveyorBeltPerformInsert(ConveyorBelt *cb, Buffer buffer, bool page_std) cb_insert_payload_page(cb->cb_insert_relfilenode, cb->cb_fork, cb->cb_insert_metabuffer, cb->cb_insert_block, buffer, - needs_xlog, page_std); + needs_xlog); /* * Buffer locks will be released by ConveyorBeltCleanupInsert, but we can @@ -1064,6 +1080,21 @@ ConveyorBeltRead(ConveyorBelt *cb, BlockNumber blkno, int mode) return buffer; } +/* + * We consider a page unused if it's either new (i.e. all zeroes) or if + * neither pd_lower nor pd_upper have moved. + */ +static Buffer +ConveyorBeltPageIsUnused(Page page) +{ + PageHeader ph = (PageHeader) page; + + if (PageIsNew(page)) + return true; + + return (ph->pd_lower <= SizeOfPageHeaderData && ph->pd_upper == BLCKSZ); +} + /* * Find a free segment by searching all of the FSM pages that currently exist, * and if that doesn't turn up anything, adding a new FSM page. diff --git a/src/include/access/cbmodify.h b/src/include/access/cbmodify.h index 0e11cef070..3cbbb80eca 100644 --- a/src/include/access/cbmodify.h +++ b/src/include/access/cbmodify.h @@ -44,8 +44,7 @@ extern void cb_insert_payload_page(RelFileNode *rnode, Buffer metabuffer, BlockNumber payloadblock, Buffer payloadbuffer, - bool needs_xlog, - bool page_std); + bool needs_xlog); extern void cb_allocate_payload_segment(RelFileNode *rnode, ForkNumber fork, diff --git a/src/include/access/conveyor.h b/src/include/access/conveyor.h index 548e09a22b..1b1ce5ba22 100644 --- a/src/include/access/conveyor.h +++ b/src/include/access/conveyor.h @@ -34,8 +34,7 @@ extern ConveyorBelt *ConveyorBeltOpen(Relation rel, /* Routines to inserting new data into a conveyor belt. */ extern Buffer ConveyorBeltGetNewPage(ConveyorBelt *cb, CBPageNo *pageno); -extern void ConveyorBeltPerformInsert(ConveyorBelt *cb, Buffer buffer, - bool page_std); +extern void ConveyorBeltPerformInsert(ConveyorBelt *cb, Buffer buffer); extern void ConveyorBeltCleanupInsert(ConveyorBelt *cb, Buffer buffer); /* Routines for reading data from a conveyor belt. */