Skip to content

Commit b3888b6

Browse files
committed
Revert bogus fixes of HOT-freezing bug
It turns out we misdiagnosed what the real problem was. Revert the previous changes, because they may have worse consequences going forward. A better fix is forthcoming. The simplistic test case is kept, though disabled. Discussion: https://postgr.es/m/20171102112019.33wb7g5wp4zpjelu@alap3.anarazel.de
1 parent 7ae3951 commit b3888b6

File tree

6 files changed

+36
-99
lines changed

6 files changed

+36
-99
lines changed

src/backend/access/heap/heapam.c

Lines changed: 22 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,7 +1824,8 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
18241824
* broken.
18251825
*/
18261826
if (TransactionIdIsValid(prev_xmax) &&
1827-
!HeapTupleUpdateXmaxMatchesXmin(prev_xmax, heapTuple->t_data))
1827+
!TransactionIdEquals(prev_xmax,
1828+
HeapTupleHeaderGetXmin(heapTuple->t_data)))
18281829
break;
18291830

18301831
/*
@@ -2006,7 +2007,7 @@ heap_get_latest_tid(Relation relation,
20062007
* tuple. Check for XMIN match.
20072008
*/
20082009
if (TransactionIdIsValid(priorXmax) &&
2009-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tp.t_data))
2010+
!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
20102011
{
20112012
UnlockReleaseBuffer(buffer);
20122013
break;
@@ -2038,50 +2039,6 @@ heap_get_latest_tid(Relation relation,
20382039
} /* end of loop */
20392040
}
20402041

2041-
/*
2042-
* HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
2043-
*
2044-
* Given the new version of a tuple after some update, verify whether the
2045-
* given Xmax (corresponding to the previous version) matches the tuple's
2046-
* Xmin, taking into account that the Xmin might have been frozen after the
2047-
* update.
2048-
*/
2049-
bool
2050-
HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
2051-
{
2052-
TransactionId xmin = HeapTupleHeaderGetXmin(htup);
2053-
2054-
/*
2055-
* If the xmax of the old tuple is identical to the xmin of the new one,
2056-
* it's a match.
2057-
*/
2058-
if (TransactionIdEquals(xmax, xmin))
2059-
return true;
2060-
2061-
/*
2062-
* If the Xmin that was in effect prior to a freeze matches the Xmax,
2063-
* it's good too.
2064-
*/
2065-
if (HeapTupleHeaderXminFrozen(htup) &&
2066-
TransactionIdEquals(HeapTupleHeaderGetRawXmin(htup), xmax))
2067-
return true;
2068-
2069-
/*
2070-
* When a tuple is frozen, the original Xmin is lost, but we know it's a
2071-
* committed transaction. So unless the Xmax is InvalidXid, we don't know
2072-
* for certain that there is a match, but there may be one; and we must
2073-
* return true so that a HOT chain that is half-frozen can be walked
2074-
* correctly.
2075-
*
2076-
* We no longer freeze tuples this way, but we must keep this in order to
2077-
* interpret pre-pg_upgrade pages correctly.
2078-
*/
2079-
if (TransactionIdEquals(xmin, FrozenTransactionId) &&
2080-
TransactionIdIsValid(xmax))
2081-
return true;
2082-
2083-
return false;
2084-
}
20852042

20862043
/*
20872044
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5443,7 +5400,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
54435400
* end of the chain, we're done, so return success.
54445401
*/
54455402
if (TransactionIdIsValid(priorXmax) &&
5446-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, mytup.t_data))
5403+
!TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
5404+
priorXmax))
54475405
{
54485406
UnlockReleaseBuffer(buf);
54495407
return HeapTupleMayBeUpdated;
@@ -6122,23 +6080,14 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
61226080
Assert(TransactionIdIsValid(xid));
61236081

61246082
/*
6125-
* The updating transaction cannot possibly be still running, but
6126-
* verify whether it has committed, and request to set the
6127-
* COMMITTED flag if so. (We normally don't see any tuples in
6128-
* this state, because they are removed by page pruning before we
6129-
* try to freeze the page; but this can happen if the updating
6130-
* transaction commits after the page is pruned but before
6131-
* HeapTupleSatisfiesVacuum).
6083+
* If the xid is older than the cutoff, it has to have aborted,
6084+
* otherwise the tuple would have gotten pruned away.
61326085
*/
61336086
if (TransactionIdPrecedes(xid, cutoff_xid))
61346087
{
6135-
if (TransactionIdDidCommit(xid))
6136-
*flags = FRM_MARK_COMMITTED | FRM_RETURN_IS_XID;
6137-
else
6138-
{
6139-
*flags |= FRM_INVALIDATE_XMAX;
6140-
xid = InvalidTransactionId; /* not strictly necessary */
6141-
}
6088+
Assert(!TransactionIdDidCommit(xid));
6089+
*flags |= FRM_INVALIDATE_XMAX;
6090+
xid = InvalidTransactionId; /* not strictly necessary */
61426091
}
61436092
else
61446093
{
@@ -6211,16 +6160,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
62116160
/*
62126161
* It's an update; should we keep it? If the transaction is known
62136162
* aborted or crashed then it's okay to ignore it, otherwise not.
6163+
* Note that an updater older than cutoff_xid cannot possibly be
6164+
* committed, because HeapTupleSatisfiesVacuum would have returned
6165+
* HEAPTUPLE_DEAD and we would not be trying to freeze the tuple.
62146166
*
62156167
* As with all tuple visibility routines, it's critical to test
62166168
* TransactionIdIsInProgress before TransactionIdDidCommit,
62176169
* because of race conditions explained in detail in tqual.c.
6218-
*
6219-
* We normally don't see committed updating transactions earlier
6220-
* than the cutoff xid, because they are removed by page pruning
6221-
* before we try to freeze the page; but it can happen if the
6222-
* updating transaction commits after the page is pruned but
6223-
* before HeapTupleSatisfiesVacuum.
62246170
*/
62256171
if (TransactionIdIsCurrentTransactionId(xid) ||
62266172
TransactionIdIsInProgress(xid))
@@ -6245,6 +6191,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
62456191
* we can ignore it.
62466192
*/
62476193

6194+
/*
6195+
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6196+
* update Xid cannot possibly be older than the xid cutoff.
6197+
*/
6198+
Assert(!TransactionIdIsValid(update_xid) ||
6199+
!TransactionIdPrecedes(update_xid, cutoff_xid));
6200+
62486201
/*
62496202
* If we determined that it's an Xid corresponding to an update
62506203
* that must be retained, additionally add it to the list of
@@ -6321,10 +6274,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
63216274
*
63226275
* It is assumed that the caller has checked the tuple with
63236276
* HeapTupleSatisfiesVacuum() and determined that it is not HEAPTUPLE_DEAD
6324-
* (else we should be removing the tuple, not freezing it). However, note
6325-
* that we don't remove HOT tuples even if they are dead, and it'd be incorrect
6326-
* to freeze them (because that would make them visible), so we mark them as
6327-
* update-committed, and needing further freezing later on.
6277+
* (else we should be removing the tuple, not freezing it).
63286278
*
63296279
* NB: cutoff_xid *must* be <= the current global xmin, to ensure that any
63306280
* XID older than it could neither be running nor seen as running by any
@@ -6429,18 +6379,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
64296379
else if (TransactionIdIsNormal(xid) &&
64306380
TransactionIdPrecedes(xid, cutoff_xid))
64316381
{
6432-
/*
6433-
* Must freeze regular XIDs older than the cutoff. We must not freeze
6434-
* a HOT-updated tuple, though; doing so would bring it back to life.
6435-
*/
6436-
if (HeapTupleHeaderIsHotUpdated(tuple))
6437-
{
6438-
frz->t_infomask |= HEAP_XMAX_COMMITTED;
6439-
changed = true;
6440-
/* must not freeze */
6441-
}
6442-
else
6443-
freeze_xmax = true;
6382+
freeze_xmax = true;
64446383
}
64456384

64466385
if (freeze_xmax)

src/backend/access/heap/pruneheap.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
465465
* Check the tuple XMIN against prior XMAX, if any
466466
*/
467467
if (TransactionIdIsValid(priorXmax) &&
468-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, htup))
468+
!TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
469469
break;
470470

471471
/*
@@ -805,7 +805,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
805805
htup = (HeapTupleHeader) PageGetItem(page, lp);
806806

807807
if (TransactionIdIsValid(priorXmax) &&
808-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, htup))
808+
!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
809809
break;
810810

811811
/* Remember the root line pointer for this item */

src/backend/commands/vacuumlazy.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,15 +1722,15 @@ lazy_record_dead_tuple(LVRelStats *vacrelstats,
17221722
ItemPointer itemptr)
17231723
{
17241724
/*
1725-
* The array must never overflow, since we rely on all deletable tuples
1726-
* being removed; inability to remove a tuple might cause an old XID to
1727-
* persist beyond the freeze limit, which could be disastrous later on.
1725+
* The array shouldn't overflow under normal behavior, but perhaps it
1726+
* could if we are given a really small maintenance_work_mem. In that
1727+
* case, just forget the last few tuples (we'll get 'em next time).
17281728
*/
1729-
if (vacrelstats->num_dead_tuples >= vacrelstats->max_dead_tuples)
1730-
elog(ERROR, "dead tuple array overflow");
1731-
1732-
vacrelstats->dead_tuples[vacrelstats->num_dead_tuples] = *itemptr;
1733-
vacrelstats->num_dead_tuples++;
1729+
if (vacrelstats->num_dead_tuples < vacrelstats->max_dead_tuples)
1730+
{
1731+
vacrelstats->dead_tuples[vacrelstats->num_dead_tuples] = *itemptr;
1732+
vacrelstats->num_dead_tuples++;
1733+
}
17341734
}
17351735

17361736
/*

src/backend/executor/execMain.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,7 +2243,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
22432243
* atomic, and Xmin never changes in an existing tuple, except to
22442244
* invalid or frozen, and neither of those can match priorXmax.)
22452245
*/
2246-
if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
2246+
if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
2247+
priorXmax))
22472248
{
22482249
ReleaseBuffer(buffer);
22492250
return NULL;
@@ -2390,7 +2391,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
23902391
/*
23912392
* As above, if xmin isn't what we're expecting, do nothing.
23922393
*/
2393-
if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
2394+
if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
2395+
priorXmax))
23942396
{
23952397
ReleaseBuffer(buffer);
23962398
return NULL;

src/include/access/heapam.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,6 @@ extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
139139
ItemPointer tid);
140140
extern void setLastTid(const ItemPointer tid);
141141

142-
extern bool HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax,
143-
HeapTupleHeader htup);
144-
145142
extern BulkInsertState GetBulkInsertState(void);
146143
extern void FreeBulkInsertState(BulkInsertState);
147144

src/test/isolation/isolation_schedule

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ test: lock-committed-keyupdate
3535
test: update-locked-tuple
3636
test: propagate-lock-delete
3737
test: tuplelock-conflict
38-
test: freeze-the-dead
3938
test: nowait
4039
test: nowait-2
4140
test: nowait-3

0 commit comments

Comments
 (0)