Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita5736bf

Browse files
committed
Fix traversal of half-frozen update chains
When some tuple versions in an update chain are frozen due to them beingolder than freeze_min_age, the xmax/xmin trail can become broken. Thisbreaks HOT (and probably other things). A subsequent VACUUM can breakthings in more serious ways, such as leaving orphan heap-only tupleswhose root HOT redirect items were removed. This can be seen becauseindex creation (or REINDEX) complain like ERROR: XX000: failed to find parent tuple for heap-only tuple at (0,7) in table "t"Because of relfrozenxid contraints, we cannot avoid the freezing of theearly tuples, so we must cope with the results: whenever we see an Xminof FrozenTransactionId, consider it a match for whatever the previousXmax value was.This problem seems to have appeared in 9.3 with multixact changes,though strictly speaking it seems unrelated.Since 9.4 we have commit37484ad "Change the way we mark tuples asfrozen", so the fix is simple: just compare the raw Xmin (still storedin the tuple header, since freezing merely set an infomask bit) to theXmax. But in 9.3 we rewrite the Xmin value to FrozenTransactionId, sothe original value is lost and we have nothing to compare the Xmax with.To cope with that case we need to compare the Xmin with FrozenXid,assume it's a match, and hope for the best. Sadly, since you canpg_upgrade a 9.3 instance containing half-frozen pages to newerreleases, we need to keep the old check in newer versions too, whichseems a bit brittle; I hope we can somehow get rid of that.I didn't optimize the new function for performance. The new coding isprobably a bit slower than before, since there is a function call ratherthan a straight comparison, but I'd rather have it work correctly thanbe fast but wrong.This is a followup after20b6552 fixed a few related problems.Apparently, in 9.6 and up there are more ways to get into trouble, butin 9.3 - 9.5 I cannot reproduce a problem anymore with this patch, sothere must be a separate bug.Reported-by: Peter GeogheganDiagnosed-by: Peter Geoghegan, Michael Paquier, Daniel Wood,Yi Wen Wong, ÁlvaroDiscussion:https://postgr.es/m/CAH2-Wznm4rCrhFAiwKPWTpEw2bXDtgROZK7jWWGucXeH3D1fmA@mail.gmail.com
1 parentf49842d commita5736bf

File tree

4 files changed

+54
-11
lines changed

4 files changed

+54
-11
lines changed

‎src/backend/access/heap/heapam.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,8 +2074,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
20742074
* broken.
20752075
*/
20762076
if (TransactionIdIsValid(prev_xmax)&&
2077-
!TransactionIdEquals(prev_xmax,
2078-
HeapTupleHeaderGetXmin(heapTuple->t_data)))
2077+
!HeapTupleUpdateXmaxMatchesXmin(prev_xmax,heapTuple->t_data))
20792078
break;
20802079

20812080
/*
@@ -2261,7 +2260,7 @@ heap_get_latest_tid(Relation relation,
22612260
* tuple. Check for XMIN match.
22622261
*/
22632262
if (TransactionIdIsValid(priorXmax)&&
2264-
!TransactionIdEquals(priorXmax,HeapTupleHeaderGetXmin(tp.t_data)))
2263+
!HeapTupleUpdateXmaxMatchesXmin(priorXmax,tp.t_data))
22652264
{
22662265
UnlockReleaseBuffer(buffer);
22672266
break;
@@ -2293,6 +2292,50 @@ heap_get_latest_tid(Relation relation,
22932292
}/* end of loop */
22942293
}
22952294

2295+
/*
2296+
* HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
2297+
*
2298+
* Given the new version of a tuple after some update, verify whether the
2299+
* given Xmax (corresponding to the previous version) matches the tuple's
2300+
* Xmin, taking into account that the Xmin might have been frozen after the
2301+
* update.
2302+
*/
2303+
bool
2304+
HeapTupleUpdateXmaxMatchesXmin(TransactionIdxmax,HeapTupleHeaderhtup)
2305+
{
2306+
TransactionIdxmin=HeapTupleHeaderGetXmin(htup);
2307+
2308+
/*
2309+
* If the xmax of the old tuple is identical to the xmin of the new one,
2310+
* it's a match.
2311+
*/
2312+
if (TransactionIdEquals(xmax,xmin))
2313+
return true;
2314+
2315+
/*
2316+
* If the Xmin that was in effect prior to a freeze matches the Xmax,
2317+
* it's good too.
2318+
*/
2319+
if (HeapTupleHeaderXminFrozen(htup)&&
2320+
TransactionIdEquals(HeapTupleHeaderGetRawXmin(htup),xmax))
2321+
return true;
2322+
2323+
/*
2324+
* When a tuple is frozen, the original Xmin is lost, but we know it's a
2325+
* committed transaction. So unless the Xmax is InvalidXid, we don't know
2326+
* for certain that there is a match, but there may be one; and we must
2327+
* return true so that a HOT chain that is half-frozen can be walked
2328+
* correctly.
2329+
*
2330+
* We no longer freeze tuples this way, but we must keep this in order to
2331+
* interpret pre-pg_upgrade pages correctly.
2332+
*/
2333+
if (TransactionIdEquals(xmin,FrozenTransactionId)&&
2334+
TransactionIdIsValid(xmax))
2335+
return true;
2336+
2337+
return false;
2338+
}
22962339

22972340
/*
22982341
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -5712,8 +5755,7 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
57125755
* end of the chain, we're done, so return success.
57135756
*/
57145757
if (TransactionIdIsValid(priorXmax)&&
5715-
!TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
5716-
priorXmax))
5758+
!HeapTupleUpdateXmaxMatchesXmin(priorXmax,mytup.t_data))
57175759
{
57185760
result=HeapTupleMayBeUpdated;
57195761
gotoout_locked;

‎src/backend/access/heap/pruneheap.c

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

479479
/*
@@ -813,7 +813,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
813813
htup= (HeapTupleHeader)PageGetItem(page,lp);
814814

815815
if (TransactionIdIsValid(priorXmax)&&
816-
!TransactionIdEquals(priorXmax,HeapTupleHeaderGetXmin(htup)))
816+
!HeapTupleUpdateXmaxMatchesXmin(priorXmax,htup))
817817
break;
818818

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

‎src/backend/executor/execMain.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2594,8 +2594,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
25942594
* atomic, and Xmin never changes in an existing tuple, except to
25952595
* invalid or frozen, and neither of those can match priorXmax.)
25962596
*/
2597-
if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
2598-
priorXmax))
2597+
if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax,tuple.t_data))
25992598
{
26002599
ReleaseBuffer(buffer);
26012600
returnNULL;
@@ -2742,8 +2741,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
27422741
/*
27432742
* As above, if xmin isn't what we're expecting, do nothing.
27442743
*/
2745-
if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
2746-
priorXmax))
2744+
if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax,tuple.t_data))
27472745
{
27482746
ReleaseBuffer(buffer);
27492747
returnNULL;

‎src/include/access/heapam.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
146146
ItemPointertid);
147147
externvoidsetLastTid(constItemPointertid);
148148

149+
externboolHeapTupleUpdateXmaxMatchesXmin(TransactionIdxmax,
150+
HeapTupleHeaderhtup);
151+
149152
externBulkInsertStateGetBulkInsertState(void);
150153
externvoidFreeBulkInsertState(BulkInsertState);
151154
externvoidReleaseBulkInsertStatePin(BulkInsertStatebistate);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp