/*
  * Build a pending-list page from the given array of tuples, and write it out.
+ *
+ * Returns amount of free space left on the page.
  */
 static int32
 writeListPage(Relation index, Buffer buffer,
                          IndexTuple *tuples, int32 ntuples, BlockNumber rightlink)
 {
        Page            page = BufferGetPage(buffer);
-       int                     i,
+       int32           i,
                                freesize,
                                size = 0;
        OffsetNumber l,
                GinPageGetOpaque(page)->maxoff = 0;
        }
 
-       freesize = PageGetFreeSpace(page);
-
        MarkBufferDirty(buffer);
 
        if (!index->rd_istemp)
                ginxlogInsertListPage data;
                XLogRecPtr      recptr;
 
-               rdata[0].buffer = buffer;
-               rdata[0].buffer_std = true;
+               data.node = index->rd_node;
+               data.blkno = BufferGetBlockNumber(buffer);
+               data.rightlink = rightlink;
+               data.ntuples = ntuples;
+
+               rdata[0].buffer = InvalidBuffer;
                rdata[0].data = (char *) &data;
                rdata[0].len = sizeof(ginxlogInsertListPage);
                rdata[0].next = rdata + 1;
 
-               rdata[1].buffer = InvalidBuffer;
+               rdata[1].buffer = buffer;
+               rdata[1].buffer_std = true;
                rdata[1].data = workspace;
                rdata[1].len = size;
                rdata[1].next = NULL;
 
-               data.blkno = BufferGetBlockNumber(buffer);
-               data.rightlink = rightlink;
-               data.ntuples = ntuples;
-
                recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT_LISTPAGE, rdata);
                PageSetLSN(page, recptr);
                PageSetTLI(page, ThisTimeLineID);
        }
 
+       /* get free space before releasing buffer */
+       freesize = PageGetExactFreeSpace(page);
+
        UnlockReleaseBuffer(buffer);
 
        END_CRIT_SECTION();
                        {
                                res->nPendingPages++;
                                writeListPage(index, prevBuffer,
-                                                         tuples + startTuple, i - startTuple,
+                                                         tuples + startTuple,
+                                                         i - startTuple,
                                                          BufferGetBlockNumber(curBuffer));
                        }
                        else
 
                tupsize = MAXALIGN(IndexTupleSize(tuples[i])) + sizeof(ItemIdData);
 
-               if (size + tupsize >= GinListPageSize)
+               if (size + tupsize > GinListPageSize)
                {
                        /* won't fit, force a new page and reprocess */
                        i--;
         */
        res->tail = BufferGetBlockNumber(curBuffer);
        res->tailFreeSize = writeListPage(index, curBuffer,
-                                                                  tuples + startTuple, ntuples - startTuple,
+                                                                         tuples + startTuple,
+                                                                         ntuples - startTuple,
                                                                          InvalidBlockNumber);
        res->nPendingPages++;
        /* that was only one heap tuple */
        metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
        metapage = BufferGetPage(metabuffer);
 
-       if (collector->sumsize + collector->ntuples * sizeof(ItemIdData) > GIN_PAGE_FREESIZE)
+       if (collector->sumsize + collector->ntuples * sizeof(ItemIdData) > GinListPageSize)
        {
                /*
                 * Total size is greater than one page => make sublist
 
        if (separateList)
        {
-               GinMetaPageData sublist;
-
                /*
                 * We should make sublist separately and append it to the tail
                 */
-               memset(&sublist, 0, sizeof(GinMetaPageData));
+               GinMetaPageData sublist;
 
+               memset(&sublist, 0, sizeof(GinMetaPageData));
                makeSublist(index, collector->tuples, collector->ntuples, &sublist);
 
                /*
                if (metadata->head == InvalidBlockNumber)
                {
                        /*
-                        * Sublist becomes main list
+                        * Main list is empty, so just copy sublist into main list
                         */
                        START_CRIT_SECTION();
+
                        memcpy(metadata, &sublist, sizeof(GinMetaPageData));
-                       memcpy(&data.metadata, &sublist, sizeof(GinMetaPageData));
                }
                else
                {
                        /*
-                        * merge lists
+                        * Merge lists
                         */
-
                        data.prevTail = metadata->tail;
+                       data.newRightlink = sublist.head;
+
                        buffer = ReadBuffer(index, metadata->tail);
                        LockBuffer(buffer, GIN_EXCLUSIVE);
                        page = BufferGetPage(buffer);
+
                        Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber);
 
                        START_CRIT_SECTION();
 
                        GinPageGetOpaque(page)->rightlink = sublist.head;
+
+                       MarkBufferDirty(buffer);
+
                        metadata->tail = sublist.tail;
                        metadata->tailFreeSize = sublist.tailFreeSize;
 
                        metadata->nPendingPages += sublist.nPendingPages;
                        metadata->nPendingHeapTuples += sublist.nPendingHeapTuples;
-
-                       memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
-                       data.newRightlink = sublist.head;
-
-                       MarkBufferDirty(buffer);
                }
        }
        else
        {
                /*
-                * Insert into tail page, metapage is already locked
+                * Insert into tail page.  Metapage is already locked
                 */
-
                OffsetNumber l,
                                        off;
                int                     i,
                buffer = ReadBuffer(index, metadata->tail);
                LockBuffer(buffer, GIN_EXCLUSIVE);
                page = BufferGetPage(buffer);
+
                off = (PageIsEmpty(page)) ? FirstOffsetNumber :
                        OffsetNumberNext(PageGetMaxOffsetNumber(page));
 
                        off++;
                }
 
-               metadata->tailFreeSize -= collector->sumsize + collector->ntuples * sizeof(ItemIdData);
-               memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
+               Assert((ptr - rdata[1].data) <= collector->sumsize);
+
+               metadata->tailFreeSize = PageGetExactFreeSpace(page);
+
                MarkBufferDirty(buffer);
        }
 
        /*
-        * Make real write
+        * Write metabuffer, make xlog entry
         */
-
        MarkBufferDirty(metabuffer);
+
        if (!index->rd_istemp)
        {
                XLogRecPtr      recptr;
 
+               memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
+
                recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, rdata);
                PageSetLSN(metapage, recptr);
                PageSetTLI(metapage, ThisTimeLineID);
                        metadata->nPendingPages = 0;
                        metadata->nPendingHeapTuples = 0;
                }
-               memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
 
                MarkBufferDirty(metabuffer);
 
                {
                        XLogRecPtr      recptr;
 
+                       memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
+
                        recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_LISTPAGE, rdata);
                        PageSetLSN(metapage, recptr);
                        PageSetTLI(metapage, ThisTimeLineID);