require std pages, and that newly insert pages are visibly non-empty
authorRobert Haas <[email protected]>
Fri, 15 Oct 2021 19:35:22 +0000 (15:35 -0400)
committerRobert Haas <[email protected]>
Fri, 15 Oct 2021 19:35:22 +0000 (15:35 -0400)
src/backend/access/conveyor/README
src/backend/access/conveyor/cbmodify.c
src/backend/access/conveyor/conveyor.c
src/include/access/cbmodify.h
src/include/access/conveyor.h

index 0314abc66477ecd6c0ff90c03d854c867ba5afe9..cd57b2164ff3cdc08b7cf2237e1feeb9b1b383f4 100644 (file)
@@ -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.
index 2905b4e7a592ee84c98bbce110aa0e9b013eff17..95f1e8dca54e329e6fe7e93c084ae80cadfb7efc 100644 (file)
@@ -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);
 
index 395bd70ea633cdbc23f5fef4ceb8afec819941c9..70935cfd9f1be57ec663199d560e90742a8dfc62 100644 (file)
@@ -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.
index 0e11cef0703dc0dd2f002bbfa5fae5312b7072cf..3cbbb80eca7e4a7359556b2198c86d66d99a6310 100644 (file)
@@ -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,
index 548e09a22b4061af4500f3d9ef359fee3708942b..1b1ce5ba22799bd81070754310741de678262d25 100644 (file)
@@ -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. */