Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 986a915

Browse files
committed
Perform a lot more sanity checks when freezing tuples.
The previous commit has shown that the sanity checks around freezing aren't strong enough. Strengthening them seems especially important because the existance of the bug has caused corruption that we don't want to make even worse during future vacuum cycles. The errors are emitted with ereport rather than elog, despite being "should never happen" messages, so a proper error code is emitted. To avoid superflous translations, mark messages as internal. Author: Andres Freund and Alvaro Herrera Reviewed-By: Alvaro Herrera, Michael Paquier Discussion: https://postgr.es/m/20171102112019.33wb7g5wp4zpjelu@alap3.anarazel.de Backpatch: 9.3-
1 parent 937494c commit 986a915

File tree

5 files changed

+114
-25
lines changed

5 files changed

+114
-25
lines changed

src/backend/access/heap/heapam.c

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6364,6 +6364,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
63646364
*/
63656365
static TransactionId
63666366
FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6367+
TransactionId relfrozenxid, TransactionId relminmxid,
63676368
TransactionId cutoff_xid, MultiXactId cutoff_multi,
63686369
uint16 *flags)
63696370
{
@@ -6390,16 +6391,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
63906391
*flags |= FRM_INVALIDATE_XMAX;
63916392
return InvalidTransactionId;
63926393
}
6394+
else if (MultiXactIdPrecedes(multi, relminmxid))
6395+
ereport(ERROR,
6396+
(errcode(ERRCODE_DATA_CORRUPTED),
6397+
errmsg_internal("found multixact %u from before relminmxid %u",
6398+
multi, relminmxid)));
63936399
else if (MultiXactIdPrecedes(multi, cutoff_multi))
63946400
{
63956401
/*
6396-
* This old multi cannot possibly have members still running. If it
6397-
* was a locker only, it can be removed without any further
6398-
* consideration; but if it contained an update, we might need to
6399-
* preserve it.
6402+
* This old multi cannot possibly have members still running, but
6403+
* verify just in case. If it was a locker only, it can be removed
6404+
* without any further consideration; but if it contained an update, we
6405+
* might need to preserve it.
64006406
*/
6401-
Assert(!MultiXactIdIsRunning(multi,
6402-
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)));
6407+
if (MultiXactIdIsRunning(multi,
6408+
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)))
6409+
ereport(ERROR,
6410+
(errcode(ERRCODE_DATA_CORRUPTED),
6411+
errmsg_internal("multixact %u from before cutoff %u found to be still running",
6412+
multi, cutoff_multi)));
6413+
64036414
if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
64046415
{
64056416
*flags |= FRM_INVALIDATE_XMAX;
@@ -6413,13 +6424,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64136424
/* wasn't only a lock, xid needs to be valid */
64146425
Assert(TransactionIdIsValid(xid));
64156426

6427+
if (TransactionIdPrecedes(xid, relfrozenxid))
6428+
ereport(ERROR,
6429+
(errcode(ERRCODE_DATA_CORRUPTED),
6430+
errmsg_internal("found update xid %u from before relfrozenxid %u",
6431+
xid, relfrozenxid)));
6432+
64166433
/*
64176434
* If the xid is older than the cutoff, it has to have aborted,
64186435
* otherwise the tuple would have gotten pruned away.
64196436
*/
64206437
if (TransactionIdPrecedes(xid, cutoff_xid))
64216438
{
6422-
Assert(!TransactionIdDidCommit(xid));
6439+
if (TransactionIdDidCommit(xid))
6440+
ereport(ERROR,
6441+
(errcode(ERRCODE_DATA_CORRUPTED),
6442+
errmsg_internal("cannot freeze committed update xid %u", xid)));
64236443
*flags |= FRM_INVALIDATE_XMAX;
64246444
xid = InvalidTransactionId; /* not strictly necessary */
64256445
}
@@ -6491,6 +6511,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64916511
{
64926512
TransactionId xid = members[i].xid;
64936513

6514+
Assert(TransactionIdIsValid(xid));
6515+
if (TransactionIdPrecedes(xid, relfrozenxid))
6516+
ereport(ERROR,
6517+
(errcode(ERRCODE_DATA_CORRUPTED),
6518+
errmsg_internal("found update xid %u from before relfrozenxid %u",
6519+
xid, relfrozenxid)));
6520+
64946521
/*
64956522
* It's an update; should we keep it? If the transaction is known
64966523
* aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6519,18 +6546,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
65196546
update_committed = true;
65206547
update_xid = xid;
65216548
}
6522-
6523-
/*
6524-
* Not in progress, not committed -- must be aborted or crashed;
6525-
* we can ignore it.
6526-
*/
6549+
else
6550+
{
6551+
/*
6552+
* Not in progress, not committed -- must be aborted or crashed;
6553+
* we can ignore it.
6554+
*/
6555+
}
65276556

65286557
/*
65296558
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6530-
* update Xid cannot possibly be older than the xid cutoff.
6559+
* update Xid cannot possibly be older than the xid cutoff. The
6560+
* presence of such a tuple would cause corruption, so be paranoid
6561+
* and check.
65316562
*/
6532-
Assert(!TransactionIdIsValid(update_xid) ||
6533-
!TransactionIdPrecedes(update_xid, cutoff_xid));
6563+
if (TransactionIdIsValid(update_xid) &&
6564+
TransactionIdPrecedes(update_xid, cutoff_xid))
6565+
ereport(ERROR,
6566+
(errcode(ERRCODE_DATA_CORRUPTED),
6567+
errmsg_internal("found update xid %u from before xid cutoff %u",
6568+
update_xid, cutoff_xid)));
65346569

65356570
/*
65366571
* If we determined that it's an Xid corresponding to an update
@@ -6627,8 +6662,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
66276662
* recovery. We really need to remove old xids.
66286663
*/
66296664
bool
6630-
heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6631-
TransactionId cutoff_multi,
6665+
heap_prepare_freeze_tuple(HeapTupleHeader tuple,
6666+
TransactionId relfrozenxid, TransactionId relminmxid,
6667+
TransactionId cutoff_xid, TransactionId cutoff_multi,
66326668
xl_heap_freeze_tuple *frz, bool *totally_frozen_p)
66336669
{
66346670
bool changed = false;
@@ -6645,8 +6681,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66456681
xid = HeapTupleHeaderGetXmin(tuple);
66466682
if (TransactionIdIsNormal(xid))
66476683
{
6684+
if (TransactionIdPrecedes(xid, relfrozenxid))
6685+
ereport(ERROR,
6686+
(errcode(ERRCODE_DATA_CORRUPTED),
6687+
errmsg_internal("found xmin %u from before relfrozenxid %u",
6688+
xid, relfrozenxid)));
6689+
66486690
if (TransactionIdPrecedes(xid, cutoff_xid))
66496691
{
6692+
if (!TransactionIdDidCommit(xid))
6693+
ereport(ERROR,
6694+
(errcode(ERRCODE_DATA_CORRUPTED),
6695+
errmsg_internal("uncommitted xmin %u from before xid cutoff %u needs to be frozen",
6696+
xid, cutoff_xid)));
6697+
66506698
frz->t_infomask |= HEAP_XMIN_FROZEN;
66516699
changed = true;
66526700
}
@@ -6671,6 +6719,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66716719
uint16 flags;
66726720

66736721
newxmax = FreezeMultiXactId(xid, tuple->t_infomask,
6722+
relfrozenxid, relminmxid,
66746723
cutoff_xid, cutoff_multi, &flags);
66756724

66766725
if (flags & FRM_INVALIDATE_XMAX)
@@ -6720,8 +6769,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
67206769
}
67216770
else if (TransactionIdIsNormal(xid))
67226771
{
6772+
if (TransactionIdPrecedes(xid, relfrozenxid))
6773+
ereport(ERROR,
6774+
(errcode(ERRCODE_DATA_CORRUPTED),
6775+
errmsg_internal("found xmax %u from before relfrozenxid %u",
6776+
xid, relfrozenxid)));
6777+
67236778
if (TransactionIdPrecedes(xid, cutoff_xid))
6779+
{
6780+
/*
6781+
* If we freeze xmax, make absolutely sure that it's not an XID
6782+
* that is important. (Note, a lock-only xmax can be removed
6783+
* independent of committedness, since a committed lock holder has
6784+
* released the lock).
6785+
*/
6786+
if (!(tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) &&
6787+
TransactionIdDidCommit(xid))
6788+
ereport(ERROR,
6789+
(errcode(ERRCODE_DATA_CORRUPTED),
6790+
errmsg_internal("cannot freeze committed xmax %u",
6791+
xid)));
67246792
freeze_xmax = true;
6793+
}
67256794
else
67266795
totally_frozen = false;
67276796
}
@@ -6826,14 +6895,17 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
68266895
* Useful for callers like CLUSTER that perform their own WAL logging.
68276896
*/
68286897
bool
6829-
heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6830-
TransactionId cutoff_multi)
6898+
heap_freeze_tuple(HeapTupleHeader tuple,
6899+
TransactionId relfrozenxid, TransactionId relminmxid,
6900+
TransactionId cutoff_xid, TransactionId cutoff_multi)
68316901
{
68326902
xl_heap_freeze_tuple frz;
68336903
bool do_freeze;
68346904
bool tuple_totally_frozen;
68356905

6836-
do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
6906+
do_freeze = heap_prepare_freeze_tuple(tuple,
6907+
relfrozenxid, relminmxid,
6908+
cutoff_xid, cutoff_multi,
68376909
&frz, &tuple_totally_frozen);
68386910

68396911
/*

src/backend/access/heap/rewriteheap.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,10 @@ rewrite_heap_tuple(RewriteState state,
405405
* While we have our hands on the tuple, we may as well freeze any
406406
* eligible xmin or xmax, so that future VACUUM effort can be saved.
407407
*/
408-
heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
408+
heap_freeze_tuple(new_tuple->t_data,
409+
state->rs_old_rel->rd_rel->relfrozenxid,
410+
state->rs_old_rel->rd_rel->relminmxid,
411+
state->rs_freeze_xid,
409412
state->rs_cutoff_multi);
410413

411414
/*

src/backend/commands/vacuumlazy.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
456456
blkno;
457457
HeapTupleData tuple;
458458
char *relname;
459+
TransactionId relfrozenxid = onerel->rd_rel->relfrozenxid;
460+
TransactionId relminmxid = onerel->rd_rel->relminmxid;
459461
BlockNumber empty_pages,
460462
vacuumed_pages;
461463
double num_tuples,
@@ -987,6 +989,13 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
987989
* tuple, we choose to keep it, because it'll be a lot
988990
* cheaper to get rid of it in the next pruning pass than
989991
* to treat it like an indexed tuple.
992+
*
993+
* If this were to happen for a tuple that actually needed
994+
* to be deleted, we'd be in trouble, because it'd
995+
* possibly leave a tuple below the relation's xmin
996+
* horizon alive. heap_prepare_freeze_tuple() is prepared
997+
* to detect that case and abort the transaction,
998+
* preventing corruption.
990999
*/
9911000
if (HeapTupleIsHotUpdated(&tuple) ||
9921001
HeapTupleIsHeapOnly(&tuple))
@@ -1078,8 +1087,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
10781087
* Each non-removable tuple must be checked to see if it needs
10791088
* freezing. Note we already have exclusive buffer lock.
10801089
*/
1081-
if (heap_prepare_freeze_tuple(tuple.t_data, FreezeLimit,
1082-
MultiXactCutoff, &frozen[nfrozen],
1090+
if (heap_prepare_freeze_tuple(tuple.t_data,
1091+
relfrozenxid, relminmxid,
1092+
FreezeLimit, MultiXactCutoff,
1093+
&frozen[nfrozen],
10831094
&tuple_totally_frozen))
10841095
frozen[nfrozen++].offset = offnum;
10851096

src/include/access/heapam.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,9 @@ extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
166166
bool follow_update,
167167
Buffer *buffer, HeapUpdateFailureData *hufd);
168168
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
169-
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
170-
TransactionId cutoff_multi);
169+
extern bool heap_freeze_tuple(HeapTupleHeader tuple,
170+
TransactionId relfrozenxid, TransactionId relminmxid,
171+
TransactionId cutoff_xid, TransactionId cutoff_multi);
171172
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
172173
MultiXactId cutoff_multi, Buffer buf);
173174
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);

src/include/access/heapam_xlog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ extern XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer,
383383
TransactionId cutoff_xid, xl_heap_freeze_tuple *tuples,
384384
int ntuples);
385385
extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple,
386+
TransactionId relfrozenxid,
387+
TransactionId relminmxid,
386388
TransactionId cutoff_xid,
387389
TransactionId cutoff_multi,
388390
xl_heap_freeze_tuple *frz,

0 commit comments

Comments
 (0)