Convert gist to compute page level xid horizon on primary.

Due to parallel development, gist added the missing conflict
information in c952eae52a, while 558a9165e0 moved that computation
to the primary for the index types that already had it.  Thus adapt
gist to also compute on the primary, using
index_compute_xid_horizon_for_tuples() instead of its own copy of the
logic.

This also adds pg_waldump support for XLOG_GIST_DELETE records, which
previously was not properly present.

Bumps WAL version.

Author: Andres Freund
Discussion: https://postgr.es/m/20190406050243.bszosdg4buvabfrt@alap3.anarazel.de
This commit is contained in:
Andres Freund 2019-04-22 14:28:30 -07:00
parent b804c1837b
commit b5f58cf213
6 changed files with 18 additions and 157 deletions

View File

@ -1616,6 +1616,7 @@ gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
int ndeletable = 0;
OffsetNumber offnum,
maxoff;
TransactionId latestRemovedXid = InvalidTransactionId;
Assert(GistPageIsLeaf(page));
@ -1634,6 +1635,11 @@ gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
deletable[ndeletable++] = offnum;
}
if (XLogStandbyInfoActive() && RelationNeedsWAL(rel))
latestRemovedXid =
index_compute_xid_horizon_for_tuples(rel, heapRel, buffer,
deletable, ndeletable);
if (ndeletable > 0)
{
START_CRIT_SECTION();
@ -1658,7 +1664,7 @@ gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
recptr = gistXLogDelete(buffer,
deletable, ndeletable,
heapRel->rd_node);
latestRemovedXid);
PageSetLSN(page, recptr);
}

View File

@ -165,152 +165,6 @@ gistRedoPageUpdateRecord(XLogReaderState *record)
UnlockReleaseBuffer(buffer);
}
/*
* Get the latestRemovedXid from the heap pages pointed at by the index
* tuples being deleted. See also btree_xlog_delete_get_latestRemovedXid,
* on which this function is based.
*/
static TransactionId
gistRedoDeleteRecordGetLatestRemovedXid(XLogReaderState *record)
{
gistxlogDelete *xlrec = (gistxlogDelete *) XLogRecGetData(record);
OffsetNumber *todelete;
Buffer ibuffer,
hbuffer;
Page ipage,
hpage;
RelFileNode rnode;
BlockNumber blkno;
ItemId iitemid,
hitemid;
IndexTuple itup;
HeapTupleHeader htuphdr;
BlockNumber hblkno;
OffsetNumber hoffnum;
TransactionId latestRemovedXid = InvalidTransactionId;
int i;
/*
* If there's nothing running on the standby we don't need to derive a
* full latestRemovedXid value, so use a fast path out of here. This
* returns InvalidTransactionId, and so will conflict with all HS
* transactions; but since we just worked out that that's zero people,
* it's OK.
*
* XXX There is a race condition here, which is that a new backend might
* start just after we look. If so, it cannot need to conflict, but this
* coding will result in throwing a conflict anyway.
*/
if (CountDBBackends(InvalidOid) == 0)
return latestRemovedXid;
/*
* In what follows, we have to examine the previous state of the index
* page, as well as the heap page(s) it points to. This is only valid if
* WAL replay has reached a consistent database state; which means that
* the preceding check is not just an optimization, but is *necessary*. We
* won't have let in any user sessions before we reach consistency.
*/
if (!reachedConsistency)
elog(PANIC, "gistRedoDeleteRecordGetLatestRemovedXid: cannot operate with inconsistent data");
/*
* Get index page. If the DB is consistent, this should not fail, nor
* should any of the heap page fetches below. If one does, we return
* InvalidTransactionId to cancel all HS transactions. That's probably
* overkill, but it's safe, and certainly better than panicking here.
*/
XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
ibuffer = XLogReadBufferExtended(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL);
if (!BufferIsValid(ibuffer))
return InvalidTransactionId;
LockBuffer(ibuffer, BUFFER_LOCK_EXCLUSIVE);
ipage = (Page) BufferGetPage(ibuffer);
/*
* Loop through the deleted index items to obtain the TransactionId from
* the heap items they point to.
*/
todelete = (OffsetNumber *) ((char *) xlrec + SizeOfGistxlogDelete);
for (i = 0; i < xlrec->ntodelete; i++)
{
/*
* Identify the index tuple about to be deleted
*/
iitemid = PageGetItemId(ipage, todelete[i]);
itup = (IndexTuple) PageGetItem(ipage, iitemid);
/*
* Locate the heap page that the index tuple points at
*/
hblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
hbuffer = XLogReadBufferExtended(xlrec->hnode, MAIN_FORKNUM, hblkno, RBM_NORMAL);
if (!BufferIsValid(hbuffer))
{
UnlockReleaseBuffer(ibuffer);
return InvalidTransactionId;
}
LockBuffer(hbuffer, BUFFER_LOCK_SHARE);
hpage = (Page) BufferGetPage(hbuffer);
/*
* Look up the heap tuple header that the index tuple points at by
* using the heap node supplied with the xlrec. We can't use
* heap_fetch, since it uses ReadBuffer rather than XLogReadBuffer.
* Note that we are not looking at tuple data here, just headers.
*/
hoffnum = ItemPointerGetOffsetNumber(&(itup->t_tid));
hitemid = PageGetItemId(hpage, hoffnum);
/*
* Follow any redirections until we find something useful.
*/
while (ItemIdIsRedirected(hitemid))
{
hoffnum = ItemIdGetRedirect(hitemid);
hitemid = PageGetItemId(hpage, hoffnum);
CHECK_FOR_INTERRUPTS();
}
/*
* If the heap item has storage, then read the header and use that to
* set latestRemovedXid.
*
* Some LP_DEAD items may not be accessible, so we ignore them.
*/
if (ItemIdHasStorage(hitemid))
{
htuphdr = (HeapTupleHeader) PageGetItem(hpage, hitemid);
HeapTupleHeaderAdvanceLatestRemovedXid(htuphdr, &latestRemovedXid);
}
else if (ItemIdIsDead(hitemid))
{
/*
* Conjecture: if hitemid is dead then it had xids before the xids
* marked on LP_NORMAL items. So we just ignore this item and move
* onto the next, for the purposes of calculating
* latestRemovedxids.
*/
}
else
Assert(!ItemIdIsUsed(hitemid));
UnlockReleaseBuffer(hbuffer);
}
UnlockReleaseBuffer(ibuffer);
/*
* If all heap tuples were LP_DEAD then we will be returning
* InvalidTransactionId here, which avoids conflicts. This matches
* existing logic which assumes that LP_DEAD tuples must already be older
* than the latestRemovedXid on the cleanup record that set them as
* LP_DEAD, hence must already have generated a conflict.
*/
return latestRemovedXid;
}
/*
* redo delete on gist index page to remove tuples marked as DEAD during index
@ -337,12 +191,11 @@ gistRedoDeleteRecord(XLogReaderState *record)
*/
if (InHotStandby)
{
TransactionId latestRemovedXid = gistRedoDeleteRecordGetLatestRemovedXid(record);
RelFileNode rnode;
XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
ResolveRecoveryConflictWithSnapshot(latestRemovedXid, rnode);
ResolveRecoveryConflictWithSnapshot(xldata->latestRemovedXid, rnode);
}
if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
@ -800,12 +653,12 @@ gistXLogUpdate(Buffer buffer,
*/
XLogRecPtr
gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete,
RelFileNode hnode)
TransactionId latestRemovedXid)
{
gistxlogDelete xlrec;
XLogRecPtr recptr;
xlrec.hnode = hnode;
xlrec.latestRemovedXid = latestRemovedXid;
xlrec.ntodelete = ntodelete;
XLogBeginInsert();

View File

@ -33,8 +33,11 @@ out_gistxlogPageReuse(StringInfo buf, gistxlogPageReuse *xlrec)
}
static void
out_gistxlogDelete(StringInfo buf, gistxlogPageUpdate *xlrec)
out_gistxlogDelete(StringInfo buf, gistxlogDelete *xlrec)
{
appendStringInfo(buf, "delete: latestRemovedXid %u, nitems: %u",
xlrec->latestRemovedXid, xlrec->ntodelete);
}
static void
@ -66,7 +69,7 @@ gist_desc(StringInfo buf, XLogReaderState *record)
out_gistxlogPageReuse(buf, (gistxlogPageReuse *) rec);
break;
case XLOG_GIST_DELETE:
out_gistxlogDelete(buf, (gistxlogPageUpdate *) rec);
out_gistxlogDelete(buf, (gistxlogDelete *) rec);
break;
case XLOG_GIST_PAGE_SPLIT:
out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec);

View File

@ -431,7 +431,7 @@ extern XLogRecPtr gistXLogUpdate(Buffer buffer,
Buffer leftchild);
extern XLogRecPtr gistXLogDelete(Buffer buffer, OffsetNumber *todelete,
int ntodelete, RelFileNode hnode);
int ntodelete, TransactionId latestRemovedXid);
extern XLogRecPtr gistXLogSplit(bool page_is_leaf,
SplitedPageLayout *dist,

View File

@ -47,8 +47,7 @@ typedef struct gistxlogPageUpdate
*/
typedef struct gistxlogDelete
{
RelFileNode hnode; /* RelFileNode of the heap the index currently
* points at */
TransactionId latestRemovedXid;
uint16 ntodelete; /* number of deleted offsets */
/*

View File

@ -31,7 +31,7 @@
/*
* Each page of XLOG file has a header like this:
*/
#define XLOG_PAGE_MAGIC 0xD100 /* can be used as WAL version indicator */
#define XLOG_PAGE_MAGIC 0xD101 /* can be used as WAL version indicator */
typedef struct XLogPageHeaderData
{