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

Commit e3ad3ff

Browse files
committed
Fix handling of multixacts predating pg_upgrade
After pg_upgrade, it is possible that some tuples' Xmax have multixacts corresponding to the old installation; such multixacts cannot have running members anymore. In many code sites we already know not to read them and clobber them silently, but at least when VACUUM tries to freeze a multixact or determine whether one needs freezing, there's an attempt to resolve it to its member transactions by calling GetMultiXactIdMembers, and if the multixact value is "in the future" with regards to the current valid multixact range, an error like this is raised: ERROR: MultiXactId 123 has not been created yet -- apparent wraparound and vacuuming fails. Per discussion with Andrew Gierth, it is completely bogus to try to resolve multixacts coming from before a pg_upgrade, regardless of where they stand with regards to the current valid multixact range. It's possible to get from under this problem by doing SELECT FOR UPDATE of the problem tuples, but if tables are large, this is slow and tedious, so a more thorough solution is desirable. To fix, we realize that multixacts in xmax created in 9.2 and previous have a specific bit pattern that is never used in 9.3 and later (we already knew this, per comments and infomask tests sprinkled in various places, but we weren't leveraging this knowledge appropriately). Whenever the infomask of the tuple matches that bit pattern, we just ignore the multixact completely as if Xmax wasn't set; or, in the case of tuple freezing, we act as if an unwanted value is set and clobber it without decoding. This guarantees that no errors will be raised, and that the values will be progressively removed until all tables are clean. Most callers of GetMultiXactIdMembers are patched to recognize directly that the value is a removable "empty" multixact and avoid calling GetMultiXactIdMembers altogether. To avoid changing the signature of GetMultiXactIdMembers() in back branches, we keep the "allow_old" boolean flag but rename it to "from_pgupgrade"; if the flag is true, we always return an empty set instead of looking up the multixact. (I suppose we could remove the argument in the master branch, but I chose not to do so in this commit). This was broken all along, but the error-facing message appeared first because of commit 8e9a16a and was partially fixed in a25c2b7. This fix, backpatched all the way back to 9.3, goes approximately in the same direction as a25c2b7 but should cover all cases. Bug analysis by Andrew Gierth and Álvaro Herrera. A number of public reports match this bug: https://www.postgresql.org/message-id/20140330040029.GY4582@tamriel.snowman.net https://www.postgresql.org/message-id/538F3D70.6080902@publicrelay.com https://www.postgresql.org/message-id/556439CF.7070109@pscs.co.uk https://www.postgresql.org/message-id/SG2PR06MB0760098A111C88E31BD4D96FB3540@SG2PR06MB0760.apcprd06.prod.outlook.com https://www.postgresql.org/message-id/20160615203829.5798.4594@wrigleys.postgresql.org
1 parent 8cf739d commit e3ad3ff

File tree

5 files changed

+88
-70
lines changed

5 files changed

+88
-70
lines changed

contrib/pgrowlocks/pgrowlocks.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
158158

159159
values[Atnum_ismulti] = pstrdup("true");
160160

161-
allow_old = !(infomask & HEAP_LOCK_MASK) &&
162-
(infomask & HEAP_XMAX_LOCK_ONLY);
161+
allow_old = HEAP_LOCKED_UPGRADED(infomask);
163162
nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
164163
false);
165164
if (nmembers == -1)

src/backend/access/heap/heapam.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3844,6 +3844,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38443844
* HEAP_XMAX_INVALID bit set; that's fine.)
38453845
*/
38463846
if ((oldtup.t_data->t_infomask & HEAP_XMAX_INVALID) ||
3847+
HEAP_LOCKED_UPGRADED(oldtup.t_data->t_infomask) ||
38473848
(checked_lockers && !locker_remains))
38483849
xmax_new_tuple = InvalidTransactionId;
38493850
else
@@ -5225,8 +5226,7 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
52255226
* pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
52265227
* that such multis are never passed.
52275228
*/
5228-
if (!(old_infomask & HEAP_LOCK_MASK) &&
5229-
HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
5229+
if (HEAP_LOCKED_UPGRADED(old_infomask))
52305230
{
52315231
old_infomask &= ~HEAP_XMAX_IS_MULTI;
52325232
old_infomask |= HEAP_XMAX_INVALID;
@@ -5586,6 +5586,17 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
55865586
int i;
55875587
MultiXactMember *members;
55885588

5589+
/*
5590+
* We don't need a test for pg_upgrade'd tuples: this is only
5591+
* applied to tuples after the first in an update chain. Said
5592+
* first tuple in the chain may well be locked-in-9.2-and-
5593+
* pg_upgraded, but that one was already locked by our caller,
5594+
* not us; and any subsequent ones cannot be because our
5595+
* caller must necessarily have obtained a snapshot later than
5596+
* the pg_upgrade itself.
5597+
*/
5598+
Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
5599+
55895600
nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
55905601
HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
55915602
for (i = 0; i < nmembers; i++)
@@ -6144,14 +6155,14 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
61446155
bool has_lockers;
61456156
TransactionId update_xid;
61466157
bool update_committed;
6147-
bool allow_old;
61486158

61496159
*flags = 0;
61506160

61516161
/* We should only be called in Multis */
61526162
Assert(t_infomask & HEAP_XMAX_IS_MULTI);
61536163

6154-
if (!MultiXactIdIsValid(multi))
6164+
if (!MultiXactIdIsValid(multi) ||
6165+
HEAP_LOCKED_UPGRADED(t_infomask))
61556166
{
61566167
/* Ensure infomask bits are appropriately set/reset */
61576168
*flags |= FRM_INVALIDATE_XMAX;
@@ -6164,14 +6175,8 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
61646175
* was a locker only, it can be removed without any further
61656176
* consideration; but if it contained an update, we might need to
61666177
* preserve it.
6167-
*
6168-
* Don't assert MultiXactIdIsRunning if the multi came from a
6169-
* pg_upgrade'd share-locked tuple, though, as doing that causes an
6170-
* error to be raised unnecessarily.
61716178
*/
6172-
Assert((!(t_infomask & HEAP_LOCK_MASK) &&
6173-
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)) ||
6174-
!MultiXactIdIsRunning(multi,
6179+
Assert(!MultiXactIdIsRunning(multi,
61756180
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)));
61766181
if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
61776182
{
@@ -6213,10 +6218,8 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
62136218
* anything.
62146219
*/
62156220

6216-
allow_old = !(t_infomask & HEAP_LOCK_MASK) &&
6217-
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask);
62186221
nmembers =
6219-
GetMultiXactIdMembers(multi, &members, allow_old,
6222+
GetMultiXactIdMembers(multi, &members, false,
62206223
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask));
62216224
if (nmembers <= 0)
62226225
{
@@ -6777,14 +6780,15 @@ static bool
67776780
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
67786781
LockTupleMode lockmode)
67796782
{
6780-
bool allow_old;
67816783
int nmembers;
67826784
MultiXactMember *members;
67836785
bool result = false;
67846786
LOCKMODE wanted = tupleLockExtraInfo[lockmode].hwlock;
67856787

6786-
allow_old = !(infomask & HEAP_LOCK_MASK) && HEAP_XMAX_IS_LOCKED_ONLY(infomask);
6787-
nmembers = GetMultiXactIdMembers(multi, &members, allow_old,
6788+
if (HEAP_LOCKED_UPGRADED(infomask))
6789+
return false;
6790+
6791+
nmembers = GetMultiXactIdMembers(multi, &members, false,
67886792
HEAP_XMAX_IS_LOCKED_ONLY(infomask));
67896793
if (nmembers >= 0)
67906794
{
@@ -6867,15 +6871,15 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
68676871
Relation rel, ItemPointer ctid, XLTW_Oper oper,
68686872
int *remaining)
68696873
{
6870-
bool allow_old;
68716874
bool result = true;
68726875
MultiXactMember *members;
68736876
int nmembers;
68746877
int remain = 0;
68756878

6876-
allow_old = !(infomask & HEAP_LOCK_MASK) && HEAP_XMAX_IS_LOCKED_ONLY(infomask);
6877-
nmembers = GetMultiXactIdMembers(multi, &members, allow_old,
6878-
HEAP_XMAX_IS_LOCKED_ONLY(infomask));
6879+
/* for pre-pg_upgrade tuples, no need to sleep at all */
6880+
nmembers = HEAP_LOCKED_UPGRADED(infomask) ? -1 :
6881+
GetMultiXactIdMembers(multi, &members, false,
6882+
HEAP_XMAX_IS_LOCKED_ONLY(infomask));
68796883

68806884
if (nmembers >= 0)
68816885
{
@@ -7056,20 +7060,19 @@ heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
70567060
/* no xmax set, ignore */
70577061
;
70587062
}
7063+
else if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
7064+
return true;
70597065
else if (MultiXactIdPrecedes(multi, cutoff_multi))
70607066
return true;
70617067
else
70627068
{
70637069
MultiXactMember *members;
70647070
int nmembers;
70657071
int i;
7066-
bool allow_old;
70677072

70687073
/* need to check whether any member of the mxact is too old */
70697074

7070-
allow_old = !(tuple->t_infomask & HEAP_LOCK_MASK) &&
7071-
HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask);
7072-
nmembers = GetMultiXactIdMembers(multi, &members, allow_old,
7075+
nmembers = GetMultiXactIdMembers(multi, &members, false,
70737076
HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
70747077

70757078
for (i = 0; i < nmembers; i++)

src/backend/access/transam/multixact.c

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,19 +1173,25 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
11731173

11741174
/*
11751175
* GetMultiXactIdMembers
1176-
* Returns the set of MultiXactMembers that make up a MultiXactId
1177-
*
1178-
* If the given MultiXactId is older than the value we know to be oldest, we
1179-
* return -1. The caller is expected to allow that only in permissible cases,
1180-
* i.e. when the infomask lets it presuppose that the tuple had been
1181-
* share-locked before a pg_upgrade; this means that the HEAP_XMAX_LOCK_ONLY
1182-
* needs to be set, but HEAP_XMAX_KEYSHR_LOCK and HEAP_XMAX_EXCL_LOCK are not
1183-
* set.
1184-
*
1185-
* Other border conditions, such as trying to read a value that's larger than
1186-
* the value currently known as the next to assign, raise an error. Previously
1187-
* these also returned -1, but since this can lead to the wrong visibility
1188-
* results, it is dangerous to do that.
1176+
* Return the set of MultiXactMembers that make up a MultiXactId
1177+
*
1178+
* Return value is the number of members found, or -1 if there are none,
1179+
* and *members is set to a newly palloc'ed array of members. It's the
1180+
* caller's responsibility to free it when done with it.
1181+
*
1182+
* from_pgupgrade must be passed as true if and only if only the multixact
1183+
* corresponds to a value from a tuple that was locked in a 9.2-or-older
1184+
* installation and later pg_upgrade'd (that is, the infomask is
1185+
* HEAP_LOCKED_UPGRADED). In this case, we know for certain that no members
1186+
* can still be running, so we return -1 just like for an empty multixact
1187+
* without any further checking. It would be wrong to try to resolve such a
1188+
* multixact: either the multixact is within the current valid multixact
1189+
* range, in which case the returned result would be bogus, or outside that
1190+
* range, in which case an error would be raised.
1191+
*
1192+
* In all other cases, the passed multixact must be within the known valid
1193+
* range, that is, greater to or equal than oldestMultiXactId, and less than
1194+
* nextMXact. Otherwise, an error is raised.
11891195
*
11901196
* onlyLock must be set to true if caller is certain that the given multi
11911197
* is used only to lock tuples; can be false without loss of correctness,
@@ -1194,7 +1200,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
11941200
*/
11951201
int
11961202
GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
1197-
bool allow_old, bool onlyLock)
1203+
bool from_pgupgrade, bool onlyLock)
11981204
{
11991205
int pageno;
12001206
int prev_pageno;
@@ -1213,7 +1219,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
12131219

12141220
debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
12151221

1216-
if (!MultiXactIdIsValid(multi))
1222+
if (!MultiXactIdIsValid(multi) || from_pgupgrade)
12171223
return -1;
12181224

12191225
/* See if the MultiXactId is in the local cache */
@@ -1246,18 +1252,11 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
12461252
*
12471253
* An ID older than MultiXactState->oldestMultiXactId cannot possibly be
12481254
* useful; it has already been removed, or will be removed shortly, by
1249-
* truncation. Returning the wrong values could lead to an incorrect
1250-
* visibility result. However, to support pg_upgrade we need to allow an
1251-
* empty set to be returned regardless, if the caller is willing to accept
1252-
* it; the caller is expected to check that it's an allowed condition
1253-
* (such as ensuring that the infomask bits set on the tuple are
1254-
* consistent with the pg_upgrade scenario). If the caller is expecting
1255-
* this to be called only on recently created multis, then we raise an
1256-
* error.
1255+
* truncation. If one is passed, an error is raised.
12571256
*
1258-
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
1259-
* seen, it implies undetected ID wraparound has occurred. This raises a
1260-
* hard error.
1257+
* Also, an ID >= nextMXact shouldn't ever be seen here; if it is seen, it
1258+
* implies undetected ID wraparound has occurred. This raises a hard
1259+
* error.
12611260
*
12621261
* Shared lock is enough here since we aren't modifying any global state.
12631262
* Acquire it just long enough to grab the current counter values. We may
@@ -1273,7 +1272,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
12731272

12741273
if (MultiXactIdPrecedes(multi, oldestMXact))
12751274
{
1276-
ereport(allow_old ? DEBUG1 : ERROR,
1275+
ereport(ERROR,
12771276
(errcode(ERRCODE_INTERNAL_ERROR),
12781277
errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
12791278
multi)));

src/backend/utils/time/tqual.c

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -620,15 +620,12 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
620620
{
621621
TransactionId xmax;
622622

623+
if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
624+
return HeapTupleMayBeUpdated;
625+
623626
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
624627
{
625-
/*
626-
* If it's only locked but neither EXCL_LOCK nor KEYSHR_LOCK is
627-
* set, it cannot possibly be running. Otherwise need to check.
628-
*/
629-
if ((tuple->t_infomask & (HEAP_XMAX_EXCL_LOCK |
630-
HEAP_XMAX_KEYSHR_LOCK)) &&
631-
MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
628+
if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
632629
return HeapTupleBeingUpdated;
633630

634631
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
@@ -1279,26 +1276,21 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
12791276
* "Deleting" xact really only locked it, so the tuple is live in any
12801277
* case. However, we should make sure that either XMAX_COMMITTED or
12811278
* XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1282-
* examining the tuple for future xacts. Also, marking dead
1283-
* MultiXacts as invalid here provides defense against MultiXactId
1284-
* wraparound (see also comments in heap_freeze_tuple()).
1279+
* examining the tuple for future xacts.
12851280
*/
12861281
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
12871282
{
12881283
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
12891284
{
12901285
/*
1291-
* If it's only locked but neither EXCL_LOCK nor KEYSHR_LOCK
1292-
* are set, it cannot possibly be running; otherwise have to
1293-
* check.
1286+
* If it's a pre-pg_upgrade tuple, the multixact cannot
1287+
* possibly be running; otherwise have to check.
12941288
*/
1295-
if ((tuple->t_infomask & (HEAP_XMAX_EXCL_LOCK |
1296-
HEAP_XMAX_KEYSHR_LOCK)) &&
1289+
if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
12971290
MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
12981291
true))
12991292
return HEAPTUPLE_LIVE;
13001293
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
1301-
13021294
}
13031295
else
13041296
{

src/include/access/htup_details.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,31 @@ struct HeapTupleHeaderData
217217
(((infomask) & HEAP_XMAX_LOCK_ONLY) || \
218218
(((infomask) & (HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)) == HEAP_XMAX_EXCL_LOCK))
219219

220+
/*
221+
* A tuple that has HEAP_XMAX_IS_MULTI and HEAP_XMAX_LOCK_ONLY but neither of
222+
* XMAX_EXCL_LOCK and XMAX_KEYSHR_LOCK must come from a tuple that was
223+
* share-locked in 9.2 or earlier and then pg_upgrade'd.
224+
*
225+
* In 9.2 and prior, HEAP_XMAX_IS_MULTI was only set when there were multiple
226+
* FOR SHARE lockers of that tuple. That set HEAP_XMAX_LOCK_ONLY (with a
227+
* different name back then) but neither of HEAP_XMAX_EXCL_LOCK and
228+
* HEAP_XMAX_KEYSHR_LOCK. That combination is no longer possible in 9.3 and
229+
* up, so if we see that combination we know for certain that the tuple was
230+
* locked in an earlier release; since all such lockers are gone (they cannot
231+
* survive through pg_upgrade), such tuples can safely be considered not
232+
* locked.
233+
*
234+
* We must not resolve such multixacts locally, because the result would be
235+
* bogus, regardless of where they stand with respect to the current valid
236+
* multixact range.
237+
*/
238+
#define HEAP_LOCKED_UPGRADED(infomask) \
239+
( \
240+
((infomask) & HEAP_XMAX_IS_MULTI) && \
241+
((infomask) & HEAP_XMAX_LOCK_ONLY) && \
242+
(((infomask) & (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)) == 0) \
243+
)
244+
220245
/*
221246
* Use these to test whether a particular lock is applied to a tuple
222247
*/

0 commit comments

Comments
 (0)