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

Commit 699bf7d

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 9c2f0a6 commit 699bf7d

File tree

5 files changed

+114
-25
lines changed

5 files changed

+114
-25
lines changed

src/backend/access/heap/heapam.c

+92-20
Original file line numberDiff line numberDiff line change
@@ -6357,6 +6357,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
63576357
*/
63586358
static TransactionId
63596359
FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6360+
TransactionId relfrozenxid, TransactionId relminmxid,
63606361
TransactionId cutoff_xid, MultiXactId cutoff_multi,
63616362
uint16 *flags)
63626363
{
@@ -6383,16 +6384,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
63836384
*flags |= FRM_INVALIDATE_XMAX;
63846385
return InvalidTransactionId;
63856386
}
6387+
else if (MultiXactIdPrecedes(multi, relminmxid))
6388+
ereport(ERROR,
6389+
(errcode(ERRCODE_DATA_CORRUPTED),
6390+
errmsg_internal("found multixact %u from before relminmxid %u",
6391+
multi, relminmxid)));
63866392
else if (MultiXactIdPrecedes(multi, cutoff_multi))
63876393
{
63886394
/*
6389-
* This old multi cannot possibly have members still running. If it
6390-
* was a locker only, it can be removed without any further
6391-
* consideration; but if it contained an update, we might need to
6392-
* preserve it.
6395+
* This old multi cannot possibly have members still running, but
6396+
* verify just in case. If it was a locker only, it can be removed
6397+
* without any further consideration; but if it contained an update, we
6398+
* might need to preserve it.
63936399
*/
6394-
Assert(!MultiXactIdIsRunning(multi,
6395-
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)));
6400+
if (MultiXactIdIsRunning(multi,
6401+
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)))
6402+
ereport(ERROR,
6403+
(errcode(ERRCODE_DATA_CORRUPTED),
6404+
errmsg_internal("multixact %u from before cutoff %u found to be still running",
6405+
multi, cutoff_multi)));
6406+
63966407
if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
63976408
{
63986409
*flags |= FRM_INVALIDATE_XMAX;
@@ -6406,13 +6417,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64066417
/* wasn't only a lock, xid needs to be valid */
64076418
Assert(TransactionIdIsValid(xid));
64086419

6420+
if (TransactionIdPrecedes(xid, relfrozenxid))
6421+
ereport(ERROR,
6422+
(errcode(ERRCODE_DATA_CORRUPTED),
6423+
errmsg_internal("found update xid %u from before relfrozenxid %u",
6424+
xid, relfrozenxid)));
6425+
64096426
/*
64106427
* If the xid is older than the cutoff, it has to have aborted,
64116428
* otherwise the tuple would have gotten pruned away.
64126429
*/
64136430
if (TransactionIdPrecedes(xid, cutoff_xid))
64146431
{
6415-
Assert(!TransactionIdDidCommit(xid));
6432+
if (TransactionIdDidCommit(xid))
6433+
ereport(ERROR,
6434+
(errcode(ERRCODE_DATA_CORRUPTED),
6435+
errmsg_internal("cannot freeze committed update xid %u", xid)));
64166436
*flags |= FRM_INVALIDATE_XMAX;
64176437
xid = InvalidTransactionId; /* not strictly necessary */
64186438
}
@@ -6484,6 +6504,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64846504
{
64856505
TransactionId xid = members[i].xid;
64866506

6507+
Assert(TransactionIdIsValid(xid));
6508+
if (TransactionIdPrecedes(xid, relfrozenxid))
6509+
ereport(ERROR,
6510+
(errcode(ERRCODE_DATA_CORRUPTED),
6511+
errmsg_internal("found update xid %u from before relfrozenxid %u",
6512+
xid, relfrozenxid)));
6513+
64876514
/*
64886515
* It's an update; should we keep it? If the transaction is known
64896516
* aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6512,18 +6539,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
65126539
update_committed = true;
65136540
update_xid = xid;
65146541
}
6515-
6516-
/*
6517-
* Not in progress, not committed -- must be aborted or crashed;
6518-
* we can ignore it.
6519-
*/
6542+
else
6543+
{
6544+
/*
6545+
* Not in progress, not committed -- must be aborted or crashed;
6546+
* we can ignore it.
6547+
*/
6548+
}
65206549

65216550
/*
65226551
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6523-
* update Xid cannot possibly be older than the xid cutoff.
6552+
* update Xid cannot possibly be older than the xid cutoff. The
6553+
* presence of such a tuple would cause corruption, so be paranoid
6554+
* and check.
65246555
*/
6525-
Assert(!TransactionIdIsValid(update_xid) ||
6526-
!TransactionIdPrecedes(update_xid, cutoff_xid));
6556+
if (TransactionIdIsValid(update_xid) &&
6557+
TransactionIdPrecedes(update_xid, cutoff_xid))
6558+
ereport(ERROR,
6559+
(errcode(ERRCODE_DATA_CORRUPTED),
6560+
errmsg_internal("found update xid %u from before xid cutoff %u",
6561+
update_xid, cutoff_xid)));
65276562

65286563
/*
65296564
* If we determined that it's an Xid corresponding to an update
@@ -6620,8 +6655,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
66206655
* recovery. We really need to remove old xids.
66216656
*/
66226657
bool
6623-
heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6624-
TransactionId cutoff_multi,
6658+
heap_prepare_freeze_tuple(HeapTupleHeader tuple,
6659+
TransactionId relfrozenxid, TransactionId relminmxid,
6660+
TransactionId cutoff_xid, TransactionId cutoff_multi,
66256661
xl_heap_freeze_tuple *frz, bool *totally_frozen_p)
66266662
{
66276663
bool changed = false;
@@ -6638,8 +6674,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66386674
xid = HeapTupleHeaderGetXmin(tuple);
66396675
if (TransactionIdIsNormal(xid))
66406676
{
6677+
if (TransactionIdPrecedes(xid, relfrozenxid))
6678+
ereport(ERROR,
6679+
(errcode(ERRCODE_DATA_CORRUPTED),
6680+
errmsg_internal("found xmin %u from before relfrozenxid %u",
6681+
xid, relfrozenxid)));
6682+
66416683
if (TransactionIdPrecedes(xid, cutoff_xid))
66426684
{
6685+
if (!TransactionIdDidCommit(xid))
6686+
ereport(ERROR,
6687+
(errcode(ERRCODE_DATA_CORRUPTED),
6688+
errmsg_internal("uncommitted xmin %u from before xid cutoff %u needs to be frozen",
6689+
xid, cutoff_xid)));
6690+
66436691
frz->t_infomask |= HEAP_XMIN_FROZEN;
66446692
changed = true;
66456693
}
@@ -6664,6 +6712,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66646712
uint16 flags;
66656713

66666714
newxmax = FreezeMultiXactId(xid, tuple->t_infomask,
6715+
relfrozenxid, relminmxid,
66676716
cutoff_xid, cutoff_multi, &flags);
66686717

66696718
if (flags & FRM_INVALIDATE_XMAX)
@@ -6713,8 +6762,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
67136762
}
67146763
else if (TransactionIdIsNormal(xid))
67156764
{
6765+
if (TransactionIdPrecedes(xid, relfrozenxid))
6766+
ereport(ERROR,
6767+
(errcode(ERRCODE_DATA_CORRUPTED),
6768+
errmsg_internal("found xmax %u from before relfrozenxid %u",
6769+
xid, relfrozenxid)));
6770+
67166771
if (TransactionIdPrecedes(xid, cutoff_xid))
6772+
{
6773+
/*
6774+
* If we freeze xmax, make absolutely sure that it's not an XID
6775+
* that is important. (Note, a lock-only xmax can be removed
6776+
* independent of committedness, since a committed lock holder has
6777+
* released the lock).
6778+
*/
6779+
if (!(tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) &&
6780+
TransactionIdDidCommit(xid))
6781+
ereport(ERROR,
6782+
(errcode(ERRCODE_DATA_CORRUPTED),
6783+
errmsg_internal("cannot freeze committed xmax %u",
6784+
xid)));
67176785
freeze_xmax = true;
6786+
}
67186787
else
67196788
totally_frozen = false;
67206789
}
@@ -6819,14 +6888,17 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
68196888
* Useful for callers like CLUSTER that perform their own WAL logging.
68206889
*/
68216890
bool
6822-
heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6823-
TransactionId cutoff_multi)
6891+
heap_freeze_tuple(HeapTupleHeader tuple,
6892+
TransactionId relfrozenxid, TransactionId relminmxid,
6893+
TransactionId cutoff_xid, TransactionId cutoff_multi)
68246894
{
68256895
xl_heap_freeze_tuple frz;
68266896
bool do_freeze;
68276897
bool tuple_totally_frozen;
68286898

6829-
do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
6899+
do_freeze = heap_prepare_freeze_tuple(tuple,
6900+
relfrozenxid, relminmxid,
6901+
cutoff_xid, cutoff_multi,
68306902
&frz, &tuple_totally_frozen);
68316903

68326904
/*

src/backend/access/heap/rewriteheap.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,10 @@ rewrite_heap_tuple(RewriteState state,
407407
* While we have our hands on the tuple, we may as well freeze any
408408
* eligible xmin or xmax, so that future VACUUM effort can be saved.
409409
*/
410-
heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
410+
heap_freeze_tuple(new_tuple->t_data,
411+
state->rs_old_rel->rd_rel->relfrozenxid,
412+
state->rs_old_rel->rd_rel->relminmxid,
413+
state->rs_freeze_xid,
411414
state->rs_cutoff_multi);
412415

413416
/*

src/backend/commands/vacuumlazy.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
467467
blkno;
468468
HeapTupleData tuple;
469469
char *relname;
470+
TransactionId relfrozenxid = onerel->rd_rel->relfrozenxid;
471+
TransactionId relminmxid = onerel->rd_rel->relminmxid;
470472
BlockNumber empty_pages,
471473
vacuumed_pages;
472474
double num_tuples,
@@ -1004,6 +1006,13 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
10041006
* tuple, we choose to keep it, because it'll be a lot
10051007
* cheaper to get rid of it in the next pruning pass than
10061008
* to treat it like an indexed tuple.
1009+
*
1010+
* If this were to happen for a tuple that actually needed
1011+
* to be deleted, we'd be in trouble, because it'd
1012+
* possibly leave a tuple below the relation's xmin
1013+
* horizon alive. heap_prepare_freeze_tuple() is prepared
1014+
* to detect that case and abort the transaction,
1015+
* preventing corruption.
10071016
*/
10081017
if (HeapTupleIsHotUpdated(&tuple) ||
10091018
HeapTupleIsHeapOnly(&tuple))
@@ -1095,8 +1104,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
10951104
* Each non-removable tuple must be checked to see if it needs
10961105
* freezing. Note we already have exclusive buffer lock.
10971106
*/
1098-
if (heap_prepare_freeze_tuple(tuple.t_data, FreezeLimit,
1099-
MultiXactCutoff, &frozen[nfrozen],
1107+
if (heap_prepare_freeze_tuple(tuple.t_data,
1108+
relfrozenxid, relminmxid,
1109+
FreezeLimit, MultiXactCutoff,
1110+
&frozen[nfrozen],
11001111
&tuple_totally_frozen))
11011112
frozen[nfrozen++].offset = offnum;
11021113

src/include/access/heapam.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,9 @@ extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
168168
bool follow_update,
169169
Buffer *buffer, HeapUpdateFailureData *hufd);
170170
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
171-
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
172-
TransactionId cutoff_multi);
171+
extern bool heap_freeze_tuple(HeapTupleHeader tuple,
172+
TransactionId relfrozenxid, TransactionId relminmxid,
173+
TransactionId cutoff_xid, TransactionId cutoff_multi);
173174
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
174175
MultiXactId cutoff_multi, Buffer buf);
175176
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);

src/include/access/heapam_xlog.h

+2
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@ extern XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer,
384384
TransactionId cutoff_xid, xl_heap_freeze_tuple *tuples,
385385
int ntuples);
386386
extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple,
387+
TransactionId relfrozenxid,
388+
TransactionId relminmxid,
387389
TransactionId cutoff_xid,
388390
TransactionId cutoff_multi,
389391
xl_heap_freeze_tuple *frz,

0 commit comments

Comments
 (0)