Skip to content

Commit 08ba67d

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 1cf96d6 commit 08ba67d

File tree

6 files changed

+38
-105
lines changed

6 files changed

+38
-105
lines changed

src/backend/access/heap/heapam.c

Lines changed: 22 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,7 +2046,8 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
20462046
* broken.
20472047
*/
20482048
if (TransactionIdIsValid(prev_xmax) &&
2049-
!HeapTupleUpdateXmaxMatchesXmin(prev_xmax, heapTuple->t_data))
2049+
!TransactionIdEquals(prev_xmax,
2050+
HeapTupleHeaderGetXmin(heapTuple->t_data)))
20502051
break;
20512052

20522053
/*
@@ -2229,7 +2230,7 @@ heap_get_latest_tid(Relation relation,
22292230
* tuple. Check for XMIN match.
22302231
*/
22312232
if (TransactionIdIsValid(priorXmax) &&
2232-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tp.t_data))
2233+
!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
22332234
{
22342235
UnlockReleaseBuffer(buffer);
22352236
break;
@@ -2261,50 +2262,6 @@ heap_get_latest_tid(Relation relation,
22612262
} /* end of loop */
22622263
}
22632264

2264-
/*
2265-
* HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
2266-
*
2267-
* Given the new version of a tuple after some update, verify whether the
2268-
* given Xmax (corresponding to the previous version) matches the tuple's
2269-
* Xmin, taking into account that the Xmin might have been frozen after the
2270-
* update.
2271-
*/
2272-
bool
2273-
HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
2274-
{
2275-
TransactionId xmin = HeapTupleHeaderGetXmin(htup);
2276-
2277-
/*
2278-
* If the xmax of the old tuple is identical to the xmin of the new one,
2279-
* it's a match.
2280-
*/
2281-
if (TransactionIdEquals(xmax, xmin))
2282-
return true;
2283-
2284-
/*
2285-
* If the Xmin that was in effect prior to a freeze matches the Xmax,
2286-
* it's good too.
2287-
*/
2288-
if (HeapTupleHeaderXminFrozen(htup) &&
2289-
TransactionIdEquals(HeapTupleHeaderGetRawXmin(htup), xmax))
2290-
return true;
2291-
2292-
/*
2293-
* When a tuple is frozen, the original Xmin is lost, but we know it's a
2294-
* committed transaction. So unless the Xmax is InvalidXid, we don't know
2295-
* for certain that there is a match, but there may be one; and we must
2296-
* return true so that a HOT chain that is half-frozen can be walked
2297-
* correctly.
2298-
*
2299-
* We no longer freeze tuples this way, but we must keep this in order to
2300-
* interpret pre-pg_upgrade pages correctly.
2301-
*/
2302-
if (TransactionIdEquals(xmin, FrozenTransactionId) &&
2303-
TransactionIdIsValid(xmax))
2304-
return true;
2305-
2306-
return false;
2307-
}
23082265

23092266
/*
23102267
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5762,7 +5719,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
57625719
* end of the chain, we're done, so return success.
57635720
*/
57645721
if (TransactionIdIsValid(priorXmax) &&
5765-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, mytup.t_data))
5722+
!TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
5723+
priorXmax))
57665724
{
57675725
result = HeapTupleMayBeUpdated;
57685726
goto out_locked;
@@ -6456,23 +6414,14 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64566414
Assert(TransactionIdIsValid(xid));
64576415

64586416
/*
6459-
* The updating transaction cannot possibly be still running, but
6460-
* verify whether it has committed, and request to set the
6461-
* COMMITTED flag if so. (We normally don't see any tuples in
6462-
* this state, because they are removed by page pruning before we
6463-
* try to freeze the page; but this can happen if the updating
6464-
* transaction commits after the page is pruned but before
6465-
* HeapTupleSatisfiesVacuum).
6417+
* If the xid is older than the cutoff, it has to have aborted,
6418+
* otherwise the tuple would have gotten pruned away.
64666419
*/
64676420
if (TransactionIdPrecedes(xid, cutoff_xid))
64686421
{
6469-
if (TransactionIdDidCommit(xid))
6470-
*flags = FRM_MARK_COMMITTED | FRM_RETURN_IS_XID;
6471-
else
6472-
{
6473-
*flags |= FRM_INVALIDATE_XMAX;
6474-
xid = InvalidTransactionId; /* not strictly necessary */
6475-
}
6422+
Assert(!TransactionIdDidCommit(xid));
6423+
*flags |= FRM_INVALIDATE_XMAX;
6424+
xid = InvalidTransactionId; /* not strictly necessary */
64766425
}
64776426
else
64786427
{
@@ -6545,16 +6494,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
65456494
/*
65466495
* It's an update; should we keep it? If the transaction is known
65476496
* aborted or crashed then it's okay to ignore it, otherwise not.
6497+
* Note that an updater older than cutoff_xid cannot possibly be
6498+
* committed, because HeapTupleSatisfiesVacuum would have returned
6499+
* HEAPTUPLE_DEAD and we would not be trying to freeze the tuple.
65486500
*
65496501
* As with all tuple visibility routines, it's critical to test
65506502
* TransactionIdIsInProgress before TransactionIdDidCommit,
65516503
* because of race conditions explained in detail in tqual.c.
6552-
*
6553-
* We normally don't see committed updating transactions earlier
6554-
* than the cutoff xid, because they are removed by page pruning
6555-
* before we try to freeze the page; but it can happen if the
6556-
* updating transaction commits after the page is pruned but
6557-
* before HeapTupleSatisfiesVacuum.
65586504
*/
65596505
if (TransactionIdIsCurrentTransactionId(xid) ||
65606506
TransactionIdIsInProgress(xid))
@@ -6579,6 +6525,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
65796525
* we can ignore it.
65806526
*/
65816527

6528+
/*
6529+
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6530+
* update Xid cannot possibly be older than the xid cutoff.
6531+
*/
6532+
Assert(!TransactionIdIsValid(update_xid) ||
6533+
!TransactionIdPrecedes(update_xid, cutoff_xid));
6534+
65826535
/*
65836536
* If we determined that it's an Xid corresponding to an update
65846537
* that must be retained, additionally add it to the list of
@@ -6657,10 +6610,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
66576610
*
66586611
* It is assumed that the caller has checked the tuple with
66596612
* HeapTupleSatisfiesVacuum() and determined that it is not HEAPTUPLE_DEAD
6660-
* (else we should be removing the tuple, not freezing it). However, note
6661-
* that we don't remove HOT tuples even if they are dead, and it'd be incorrect
6662-
* to freeze them (because that would make them visible), so we mark them as
6663-
* update-committed, and needing further freezing later on.
6613+
* (else we should be removing the tuple, not freezing it).
66646614
*
66656615
* NB: cutoff_xid *must* be <= the current global xmin, to ensure that any
66666616
* XID older than it could neither be running nor seen as running by any
@@ -6771,22 +6721,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
67716721
else if (TransactionIdIsNormal(xid))
67726722
{
67736723
if (TransactionIdPrecedes(xid, cutoff_xid))
6774-
{
6775-
/*
6776-
* Must freeze regular XIDs older than the cutoff. We must not
6777-
* freeze a HOT-updated tuple, though; doing so would bring it
6778-
* back to life.
6779-
*/
6780-
if (HeapTupleHeaderIsHotUpdated(tuple))
6781-
{
6782-
frz->t_infomask |= HEAP_XMAX_COMMITTED;
6783-
totally_frozen = false;
6784-
changed = true;
6785-
/* must not freeze */
6786-
}
6787-
else
6788-
freeze_xmax = true;
6789-
}
6724+
freeze_xmax = true;
67906725
else
67916726
totally_frozen = false;
67926727
}

src/backend/access/heap/pruneheap.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
474474
* Check the tuple XMIN against prior XMAX, if any
475475
*/
476476
if (TransactionIdIsValid(priorXmax) &&
477-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, htup))
477+
!TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
478478
break;
479479

480480
/*
@@ -814,7 +814,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
814814
htup = (HeapTupleHeader) PageGetItem(page, lp);
815815

816816
if (TransactionIdIsValid(priorXmax) &&
817-
!HeapTupleUpdateXmaxMatchesXmin(priorXmax, htup))
817+
!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
818818
break;
819819

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

src/backend/commands/vacuumlazy.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,17 +1984,17 @@ lazy_record_dead_tuple(LVRelStats *vacrelstats,
19841984
ItemPointer itemptr)
19851985
{
19861986
/*
1987-
* The array must never overflow, since we rely on all deletable tuples
1988-
* being removed; inability to remove a tuple might cause an old XID to
1989-
* persist beyond the freeze limit, which could be disastrous later on.
1987+
* The array shouldn't overflow under normal behavior, but perhaps it
1988+
* could if we are given a really small maintenance_work_mem. In that
1989+
* case, just forget the last few tuples (we'll get 'em next time).
19901990
*/
1991-
if (vacrelstats->num_dead_tuples >= vacrelstats->max_dead_tuples)
1992-
elog(ERROR, "dead tuple array overflow");
1993-
1994-
vacrelstats->dead_tuples[vacrelstats->num_dead_tuples] = *itemptr;
1995-
vacrelstats->num_dead_tuples++;
1996-
pgstat_progress_update_param(PROGRESS_VACUUM_NUM_DEAD_TUPLES,
1997-
vacrelstats->num_dead_tuples);
1991+
if (vacrelstats->num_dead_tuples < vacrelstats->max_dead_tuples)
1992+
{
1993+
vacrelstats->dead_tuples[vacrelstats->num_dead_tuples] = *itemptr;
1994+
vacrelstats->num_dead_tuples++;
1995+
pgstat_progress_update_param(PROGRESS_VACUUM_NUM_DEAD_TUPLES,
1996+
vacrelstats->num_dead_tuples);
1997+
}
19981998
}
19991999

20002000
/*

src/backend/executor/execMain.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,7 +2275,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
22752275
* atomic, and Xmin never changes in an existing tuple, except to
22762276
* invalid or frozen, and neither of those can match priorXmax.)
22772277
*/
2278-
if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
2278+
if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
2279+
priorXmax))
22792280
{
22802281
ReleaseBuffer(buffer);
22812282
return NULL;
@@ -2422,7 +2423,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
24222423
/*
24232424
* As above, if xmin isn't what we're expecting, do nothing.
24242425
*/
2425-
if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
2426+
if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
2427+
priorXmax))
24262428
{
24272429
ReleaseBuffer(buffer);
24282430
return NULL;

src/include/access/heapam.h

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

148-
extern bool HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax,
149-
HeapTupleHeader htup);
150-
151148
extern BulkInsertState GetBulkInsertState(void);
152149
extern void FreeBulkInsertState(BulkInsertState);
153150

src/test/isolation/isolation_schedule

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ test: update-locked-tuple
4141
test: propagate-lock-delete
4242
test: tuplelock-conflict
4343
test: tuplelock-update
44-
test: freeze-the-dead
4544
test: nowait
4645
test: nowait-2
4746
test: nowait-3

0 commit comments

Comments
 (0)