heapam: Pass number of required pages to RelationGetBufferForTuple()
authorAndres Freund <andres@anarazel.de>
Thu, 6 Apr 2023 21:18:24 +0000 (14:18 -0700)
committerAndres Freund <andres@anarazel.de>
Thu, 6 Apr 2023 23:17:16 +0000 (16:17 -0700)
A future commit will use this information to determine how aggressively to
extend the relation by. In heap_multi_insert() we know accurately how many
pages we need once we need to extend the relation, providing an accurate lower
bound for how much to extend.

Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/20221029025420.eplyow6k7tgu6he3@awork3.anarazel.de

src/backend/access/heap/heapam.c
src/backend/access/heap/hio.c
src/include/access/hio.h

index f7d9ce59a4790aef587beef073f37fdea8c8b449..57083a33ee5da33ea2797a4cc36ee98314d8da94 100644 (file)
@@ -1847,7 +1847,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
         */
        buffer = RelationGetBufferForTuple(relation, heaptup->t_len,
                                                                           InvalidBuffer, options, bistate,
-                                                                          &vmbuffer, NULL);
+                                                                          &vmbuffer, NULL,
+                                                                          0);
 
        /*
         * We're about to do the actual insert -- but check for conflict first, to
@@ -2050,6 +2051,32 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
                return tup;
 }
 
+/*
+ * Helper for heap_multi_insert() that computes the number of entire pages
+ * that inserting the remaining heaptuples requires. Used to determine how
+ * much the relation needs to be extended by.
+ */
+static int
+heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace)
+{
+       size_t          page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+       int                     npages = 1;
+
+       for (int i = done; i < ntuples; i++)
+       {
+               size_t          tup_sz = sizeof(ItemIdData) + MAXALIGN(heaptuples[i]->t_len);
+
+               if (page_avail < tup_sz)
+               {
+                       npages++;
+                       page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+               }
+               page_avail -= tup_sz;
+       }
+
+       return npages;
+}
+
 /*
  *     heap_multi_insert       - insert multiple tuples into a heap
  *
@@ -2076,6 +2103,9 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
        Size            saveFreeSpace;
        bool            need_tuple_data = RelationIsLogicallyLogged(relation);
        bool            need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+       bool            starting_with_empty_page = false;
+       int                     npages = 0;
+       int                     npages_used = 0;
 
        /* currently not needed (thus unsupported) for heap_multi_insert() */
        Assert(!(options & HEAP_INSERT_NO_LOGICAL));
@@ -2126,13 +2156,31 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
        while (ndone < ntuples)
        {
                Buffer          buffer;
-               bool            starting_with_empty_page;
                bool            all_visible_cleared = false;
                bool            all_frozen_set = false;
                int                     nthispage;
 
                CHECK_FOR_INTERRUPTS();
 
+               /*
+                * Compute number of pages needed to fit the to-be-inserted tuples in
+                * the worst case.  This will be used to determine how much to extend
+                * the relation by in RelationGetBufferForTuple(), if needed.  If we
+                * filled a prior page from scratch, we can just update our last
+                * computation, but if we started with a partially filled page,
+                * recompute from scratch, the number of potentially required pages
+                * can vary due to tuples needing to fit onto the page, page headers
+                * etc.
+                */
+               if (ndone == 0 || !starting_with_empty_page)
+               {
+                       npages = heap_multi_insert_pages(heaptuples, ndone, ntuples,
+                                                                                        saveFreeSpace);
+                       npages_used = 0;
+               }
+               else
+                       npages_used++;
+
                /*
                 * Find buffer where at least the next tuple will fit.  If the page is
                 * all-visible, this will also pin the requisite visibility map page.
@@ -2142,7 +2190,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
                 */
                buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
                                                                                   InvalidBuffer, options, bistate,
-                                                                                  &vmbuffer, NULL);
+                                                                                  &vmbuffer, NULL,
+                                                                                  npages - npages_used);
                page = BufferGetPage(buffer);
 
                starting_with_empty_page = PageGetMaxOffsetNumber(page) == 0;
@@ -3576,7 +3625,8 @@ l2:
                                /* It doesn't fit, must use RelationGetBufferForTuple. */
                                newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
                                                                                                   buffer, 0, NULL,
-                                                                                                  &vmbuffer_new, &vmbuffer);
+                                                                                                  &vmbuffer_new, &vmbuffer,
+                                                                                                  0);
                                /* We're all done. */
                                break;
                        }
index a0713c178ac31f9fc1e466d51740489c70f26a0f..f24e3d96eb9722f482b42bed7c60ab395365a8f2 100644 (file)
@@ -301,6 +301,11 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
  *     Returns pinned and exclusive-locked buffer of a page in given relation
  *     with free space >= given len.
  *
+ *     If num_pages is > 1, we will try to extend the relation by at least that
+ *     many pages when we decide to extend the relation. This is more efficient
+ *     for callers that know they will need multiple pages
+ *     (e.g. heap_multi_insert()).
+ *
  *     If otherBuffer is not InvalidBuffer, then it references a previously
  *     pinned buffer of another page in the same relation; on return, this
  *     buffer will also be exclusive-locked.  (This case is used by heap_update;
@@ -359,7 +364,8 @@ Buffer
 RelationGetBufferForTuple(Relation relation, Size len,
                                                  Buffer otherBuffer, int options,
                                                  BulkInsertState bistate,
-                                                 Buffer *vmbuffer, Buffer *vmbuffer_other)
+                                                 Buffer *vmbuffer, Buffer *vmbuffer_other,
+                                                 int num_pages)
 {
        bool            use_fsm = !(options & HEAP_INSERT_SKIP_FSM);
        Buffer          buffer = InvalidBuffer;
index 3f20b585326a2934b4a3af398548f69c42af4b6b..b665de7d41eaedfb180068d0cdaee0bf39af5115 100644 (file)
@@ -38,6 +38,7 @@ extern void RelationPutHeapTuple(Relation relation, Buffer buffer,
 extern Buffer RelationGetBufferForTuple(Relation relation, Size len,
                                                                                Buffer otherBuffer, int options,
                                                                                BulkInsertStateData *bistate,
-                                                                               Buffer *vmbuffer, Buffer *vmbuffer_other);
+                                                                               Buffer *vmbuffer, Buffer *vmbuffer_other,
+                                                                               int num_pages);
 
 #endif                                                 /* HIO_H */