]> granicus.if.org Git - postgresql/commitdiff
hash: Immediately after a bucket split, try to clean the old bucket.
authorRobert Haas <rhaas@postgresql.org>
Fri, 4 Aug 2017 23:33:01 +0000 (19:33 -0400)
committerRobert Haas <rhaas@postgresql.org>
Fri, 4 Aug 2017 23:33:01 +0000 (19:33 -0400)
If it works, then we won't be storing two copies of all the tuples
that were just moved.  If not, VACUUM will still take care of it
eventually.  Per a report from AP and analysis from Amit Kapila, it
seems that a bulk load can cause splits fast enough that VACUUM won't
deal with the problem in time to prevent bloat.

Amit Kapila; I rewrote the comment.

Discussion: http://postgr.es/m/20170704105728.mwb72jebfmok2nm2@zip.com.au

src/backend/access/hash/hashpage.c

index d5b6502775197211d49b733235f5b44c27e00909..08eaf1d7bf444dfd01197a20d6b5a75dd0d8d54c 100644 (file)
@@ -956,9 +956,9 @@ restart_expand:
                                          buf_oblkno, buf_nblkno, NULL,
                                          maxbucket, highmask, lowmask);
 
-       /* all done, now release the locks and pins on primary buckets. */
-       _hash_relbuf(rel, buf_oblkno);
-       _hash_relbuf(rel, buf_nblkno);
+       /* all done, now release the pins on primary buckets. */
+       _hash_dropbuf(rel, buf_oblkno);
+       _hash_dropbuf(rel, buf_nblkno);
 
        return;
 
@@ -1068,10 +1068,11 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
  * while a split is in progress.
  *
  * In addition, the caller must have created the new bucket's base page,
- * which is passed in buffer nbuf, pinned and write-locked.  That lock and
- * pin are released here.  (The API is set up this way because we must do
- * _hash_getnewbuf() before releasing the metapage write lock.  So instead of
- * passing the new bucket's start block number, we pass an actual buffer.)
+ * which is passed in buffer nbuf, pinned and write-locked.  The lock will be
+ * released here and pin must be released by the caller.  (The API is set up
+ * this way because we must do _hash_getnewbuf() before releasing the metapage
+ * write lock.  So instead of passing the new bucket's start block number, we
+ * pass an actual buffer.)
  */
 static void
 _hash_splitbucket(Relation rel,
@@ -1280,8 +1281,9 @@ _hash_splitbucket(Relation rel,
 
        /*
         * After the split is finished, mark the old bucket to indicate that it
-        * contains deletable tuples.  Vacuum will clear split-cleanup flag after
-        * deleting such tuples.
+        * contains deletable tuples.  We will clear split-cleanup flag after
+        * deleting such tuples either at the end of split or at the next split
+        * from old bucket or at the time of vacuum.
         */
        oopaque->hasho_flag |= LH_BUCKET_NEEDS_SPLIT_CLEANUP;
 
@@ -1314,6 +1316,28 @@ _hash_splitbucket(Relation rel,
        }
 
        END_CRIT_SECTION();
+
+       /*
+        * If possible, clean up the old bucket.  We might not be able to do this
+        * if someone else has a pin on it, but if not then we can go ahead.  This
+        * isn't absolutely necessary, but it reduces bloat; if we don't do it now,
+        * VACUUM will do it eventually, but maybe not until new overflow pages
+        * have been allocated.  Note that there's no need to clean up the new
+        * bucket.
+        */
+       if (IsBufferCleanupOK(bucket_obuf))
+       {
+               LockBuffer(bucket_nbuf, BUFFER_LOCK_UNLOCK);
+               hashbucketcleanup(rel, obucket, bucket_obuf,
+                                                 BufferGetBlockNumber(bucket_obuf), NULL,
+                                                 maxbucket, highmask, lowmask, NULL, NULL, true,
+                                                 NULL, NULL);
+       }
+       else
+       {
+               LockBuffer(bucket_nbuf, BUFFER_LOCK_UNLOCK);
+               LockBuffer(bucket_obuf, BUFFER_LOCK_UNLOCK);
+       }
 }
 
 /*
@@ -1434,8 +1458,7 @@ _hash_finish_split(Relation rel, Buffer metabuf, Buffer obuf, Bucket obucket,
                                          nbucket, obuf, bucket_nbuf, tidhtab,
                                          maxbucket, highmask, lowmask);
 
-       _hash_relbuf(rel, bucket_nbuf);
-       LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
+       _hash_dropbuf(rel, bucket_nbuf);
        hash_destroy(tidhtab);
 }