Skip to content

Commit 3e00251

Browse files
author
Alexander Korotkov
committed
Fix wraparound logic.
1 parent ffb949b commit 3e00251

File tree

9 files changed

+72
-276
lines changed

9 files changed

+72
-276
lines changed

src/backend/access/heap/rewriteheap.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,8 +1238,6 @@ CheckPointLogicalRewriteHeap(void)
12381238
Oid dboid;
12391239
Oid relid;
12401240
XLogRecPtr lsn;
1241-
TransactionId rewrite_xid;
1242-
TransactionId create_xid;
12431241
uint32 lsn_hi,
12441242
lsn_lo,
12451243
rewrite_xid_hi,
@@ -1266,8 +1264,6 @@ CheckPointLogicalRewriteHeap(void)
12661264
elog(ERROR, "could not parse filename \"%s\"", mapping_de->d_name);
12671265

12681266
lsn = ((uint64) lsn_hi) << 32 | lsn_lo;
1269-
rewrite_xid = ((uint64) rewrite_xid_hi) << 32 | rewrite_xid_lo;
1270-
create_xid = ((uint64) create_xid_hi) << 32 | create_xid_lo;
12711267

12721268
if (lsn < cutoff || cutoff == InvalidXLogRecPtr)
12731269
{

src/backend/access/transam/multixact.c

Lines changed: 36 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -203,22 +203,8 @@ typedef struct MultiXactStateData
203203
MultiXactId oldestMultiXactId;
204204
Oid oldestMultiXactDB;
205205

206-
/*
207-
* Oldest multixact offset that is potentially referenced by a multixact
208-
* referenced by a relation. We don't always know this value, so there's
209-
* a flag here to indicate whether or not we currently do.
210-
*/
211-
MultiXactOffset oldestOffset;
212-
bool oldestOffsetKnown;
213-
214206
/* support for anti-wraparound measures */
215207
MultiXactId multiVacLimit;
216-
MultiXactId multiWarnLimit;
217-
MultiXactId multiStopLimit;
218-
MultiXactId multiWrapLimit;
219-
220-
/* support for members anti-wraparound measures */
221-
MultiXactOffset offsetStopLimit; /* known if oldestOffsetKnown */
222208

223209
/*
224210
* Per-backend data starts here. We have two arrays stored in the area
@@ -707,9 +693,6 @@ ReadNextMultiXactId(void)
707693
mxid = MultiXactState->nextMXact;
708694
LWLockRelease(MultiXactGenLock);
709695

710-
if (mxid < FirstMultiXactId)
711-
mxid = FirstMultiXactId;
712-
713696
return mxid;
714697
}
715698

@@ -933,6 +916,42 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
933916
/* Assign the MXID */
934917
result = MultiXactState->nextMXact;
935918

919+
/*----------
920+
* Check to see if it's safe to assign another MultiXactId. This protects
921+
* against catastrophic data loss due to multixact wraparound. The basic
922+
* rules are:
923+
*
924+
* If we're past multiVacLimit or the safe threshold for member storage
925+
* space, or we don't know what the safe threshold for member storage is,
926+
* start trying to force autovacuum cycles.
927+
*
928+
* Note these are pretty much the same protections in GetNewTransactionId.
929+
*----------
930+
*/
931+
if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit))
932+
{
933+
/*
934+
* For safety's sake, we release MultiXactGenLock while sending
935+
* signals, warnings, etc. This is not so much because we care about
936+
* preserving concurrency in this situation, as to avoid any
937+
* possibility of deadlock while doing get_database_name(). First,
938+
* copy all the shared values we'll need in this path.
939+
*/
940+
LWLockRelease(MultiXactGenLock);
941+
942+
/*
943+
* To avoid swamping the postmaster with signals, we issue the autovac
944+
* request only once per 64K multis generated. This still gives
945+
* plenty of chances before we get into real trouble.
946+
*/
947+
if (IsUnderPostmaster && (result % 65536) == 0)
948+
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
949+
950+
/* Re-acquire lock and start over */
951+
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
952+
result = MultiXactState->nextMXact;
953+
}
954+
936955
/* Make sure there is room for the MXID in the file. */
937956
ExtendMultiXactOffset(result);
938957

@@ -1016,7 +1035,6 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
10161035
int length;
10171036
int truelength;
10181037
int i;
1019-
MultiXactId oldestMXact;
10201038
MultiXactId nextMXact;
10211039
MultiXactId tmpMXact;
10221040
MultiXactOffset nextOffset;
@@ -1069,7 +1087,6 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
10691087
*/
10701088
LWLockAcquire(MultiXactGenLock, LW_SHARED);
10711089

1072-
oldestMXact = MultiXactState->oldestMultiXactId;
10731090
nextMXact = MultiXactState->nextMXact;
10741091
nextOffset = MultiXactState->nextOffset;
10751092

@@ -1974,50 +1991,9 @@ void
19741991
SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
19751992
{
19761993
MultiXactId multiVacLimit;
1977-
MultiXactId multiWarnLimit;
1978-
MultiXactId multiStopLimit;
1979-
MultiXactId multiWrapLimit;
1980-
MultiXactId curMulti;
19811994

19821995
Assert(MultiXactIdIsValid(oldest_datminmxid));
19831996

1984-
/*
1985-
* We pretend that a wrap will happen halfway through the multixact ID
1986-
* space, but that's not really true, because multixacts wrap differently
1987-
* from transaction IDs. Note that, separately from any concern about
1988-
* multixact IDs wrapping, we must ensure that multixact members do not
1989-
* wrap. Limits for that are set in DetermineSafeOldestOffset, not here.
1990-
*/
1991-
multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
1992-
if (multiWrapLimit < FirstMultiXactId)
1993-
multiWrapLimit += FirstMultiXactId;
1994-
1995-
/*
1996-
* We'll refuse to continue assigning MultiXactIds once we get within 100
1997-
* multi of data loss.
1998-
*
1999-
* Note: This differs from the magic number used in
2000-
* SetTransactionIdLimit() since vacuum itself will never generate new
2001-
* multis. XXX actually it does, if it needs to freeze old multis.
2002-
*/
2003-
multiStopLimit = multiWrapLimit - 100;
2004-
if (multiStopLimit < FirstMultiXactId)
2005-
multiStopLimit -= FirstMultiXactId;
2006-
2007-
/*
2008-
* We'll start complaining loudly when we get within 10M multis of the
2009-
* stop point. This is kind of arbitrary, but if you let your gas gauge
2010-
* get down to 1% of full, would you be looking for the next gas station?
2011-
* We need to be fairly liberal about this number because there are lots
2012-
* of scenarios where most transactions are done by automatic clients that
2013-
* won't pay attention to warnings. (No, we're not gonna make this
2014-
* configurable. If you know enough to configure it, you know enough to
2015-
* not get in this kind of trouble in the first place.)
2016-
*/
2017-
multiWarnLimit = multiStopLimit - 10000000;
2018-
if (multiWarnLimit < FirstMultiXactId)
2019-
multiWarnLimit -= FirstMultiXactId;
2020-
20211997
/*
20221998
* We'll start trying to force autovacuums when oldest_datminmxid gets to
20231999
* be more than autovacuum_multixact_freeze_max_age mxids old.
@@ -2027,25 +2003,14 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
20272003
* its value. See SetTransactionIdLimit.
20282004
*/
20292005
multiVacLimit = oldest_datminmxid + autovacuum_multixact_freeze_max_age;
2030-
if (multiVacLimit < FirstMultiXactId)
2031-
multiVacLimit += FirstMultiXactId;
20322006

20332007
/* Grab lock for just long enough to set the new limit values */
20342008
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
20352009
MultiXactState->oldestMultiXactId = oldest_datminmxid;
20362010
MultiXactState->oldestMultiXactDB = oldest_datoid;
20372011
MultiXactState->multiVacLimit = multiVacLimit;
2038-
MultiXactState->multiWarnLimit = multiWarnLimit;
2039-
MultiXactState->multiStopLimit = multiStopLimit;
2040-
MultiXactState->multiWrapLimit = multiWrapLimit;
2041-
curMulti = MultiXactState->nextMXact;
20422012
LWLockRelease(MultiXactGenLock);
20432013

2044-
/* Log the info */
2045-
ereport(DEBUG1,
2046-
(errmsg("MultiXactId wrap limit is " XID_FMT ", limited by database with OID %u",
2047-
multiWrapLimit, oldest_datoid)));
2048-
20492014
/*
20502015
* Computing the actual limits is only possible once the data directory is
20512016
* in a consistent state. There's no need to compute the limits while
@@ -2285,35 +2250,6 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
22852250
return true;
22862251
}
22872252

2288-
/*
2289-
* Determine how many multixacts, and how many multixact members, currently
2290-
* exist. Return false if unable to determine.
2291-
*/
2292-
static bool
2293-
ReadMultiXactCounts(uint64 *multixacts, MultiXactOffset *members)
2294-
{
2295-
MultiXactOffset nextOffset;
2296-
MultiXactOffset oldestOffset;
2297-
MultiXactId oldestMultiXactId;
2298-
MultiXactId nextMultiXactId;
2299-
bool oldestOffsetKnown;
2300-
2301-
LWLockAcquire(MultiXactGenLock, LW_SHARED);
2302-
nextOffset = MultiXactState->nextOffset;
2303-
oldestMultiXactId = MultiXactState->oldestMultiXactId;
2304-
nextMultiXactId = MultiXactState->nextMXact;
2305-
oldestOffset = MultiXactState->oldestOffset;
2306-
oldestOffsetKnown = MultiXactState->oldestOffsetKnown;
2307-
LWLockRelease(MultiXactGenLock);
2308-
2309-
if (!oldestOffsetKnown)
2310-
return false;
2311-
2312-
*members = nextOffset - oldestOffset;
2313-
*multixacts = nextMultiXactId - oldestMultiXactId;
2314-
return true;
2315-
}
2316-
23172253
typedef struct mxtruncinfo
23182254
{
23192255
int earliestExistingPage;

src/backend/access/transam/varsup.c

Lines changed: 0 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,6 @@ GetNewTransactionId(bool isSubXact)
9797
* possibility of deadlock while doing get_database_name(). First,
9898
* copy all the shared values we'll need in this path.
9999
*/
100-
TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit;
101-
TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit;
102-
TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit;
103-
Oid oldest_datoid = ShmemVariableCache->oldestXidDB;
104-
105100
LWLockRelease(XidGenLock);
106101

107102
/*
@@ -112,48 +107,6 @@ GetNewTransactionId(bool isSubXact)
112107
if (IsUnderPostmaster && (xid % 65536) == 0)
113108
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
114109

115-
if (IsUnderPostmaster &&
116-
TransactionIdFollowsOrEquals(xid, xidStopLimit))
117-
{
118-
char *oldest_datname = get_database_name(oldest_datoid);
119-
120-
/* complain even if that DB has disappeared */
121-
if (oldest_datname)
122-
ereport(ERROR,
123-
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
124-
errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
125-
oldest_datname),
126-
errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
127-
"You might also need to commit or roll back old prepared transactions.")));
128-
else
129-
ereport(ERROR,
130-
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
131-
errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
132-
oldest_datoid),
133-
errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
134-
"You might also need to commit or roll back old prepared transactions.")));
135-
}
136-
else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
137-
{
138-
char *oldest_datname = get_database_name(oldest_datoid);
139-
140-
/* complain even if that DB has disappeared */
141-
if (oldest_datname)
142-
ereport(WARNING,
143-
(errmsg("database \"%s\" must be vacuumed within " XID_FMT " transactions",
144-
oldest_datname,
145-
xidWrapLimit - xid),
146-
errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
147-
"You might also need to commit or roll back old prepared transactions.")));
148-
else
149-
ereport(WARNING,
150-
(errmsg("database with OID %u must be vacuumed within " XID_FMT " transactions",
151-
oldest_datoid,
152-
xidWrapLimit - xid),
153-
errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
154-
"You might also need to commit or roll back old prepared transactions.")));
155-
}
156-
157110
/* Re-acquire lock and start over */
158111
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
159112
xid = ShmemVariableCache->nextXid;
@@ -267,50 +220,10 @@ void
267220
SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
268221
{
269222
TransactionId xidVacLimit;
270-
TransactionId xidWarnLimit;
271-
TransactionId xidStopLimit;
272-
TransactionId xidWrapLimit;
273223
TransactionId curXid;
274224

275225
Assert(TransactionIdIsNormal(oldest_datfrozenxid));
276226

277-
/*
278-
* The place where we actually get into deep trouble is halfway around
279-
* from the oldest potentially-existing XID. (This calculation is
280-
* probably off by one or two counts, because the special XIDs reduce the
281-
* size of the loop a little bit. But we throw in plenty of slop below,
282-
* so it doesn't matter.)
283-
*/
284-
xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
285-
if (xidWrapLimit < FirstNormalTransactionId)
286-
xidWrapLimit += FirstNormalTransactionId;
287-
288-
/*
289-
* We'll refuse to continue assigning XIDs in interactive mode once we get
290-
* within 1M transactions of data loss. This leaves lots of room for the
291-
* DBA to fool around fixing things in a standalone backend, while not
292-
* being significant compared to total XID space. (Note that since
293-
* vacuuming requires one transaction per table cleaned, we had better be
294-
* sure there's lots of XIDs left...)
295-
*/
296-
xidStopLimit = xidWrapLimit - 1000000;
297-
if (xidStopLimit < FirstNormalTransactionId)
298-
xidStopLimit -= FirstNormalTransactionId;
299-
300-
/*
301-
* We'll start complaining loudly when we get within 10M transactions of
302-
* the stop point. This is kind of arbitrary, but if you let your gas
303-
* gauge get down to 1% of full, would you be looking for the next gas
304-
* station? We need to be fairly liberal about this number because there
305-
* are lots of scenarios where most transactions are done by automatic
306-
* clients that won't pay attention to warnings. (No, we're not gonna make
307-
* this configurable. If you know enough to configure it, you know enough
308-
* to not get in this kind of trouble in the first place.)
309-
*/
310-
xidWarnLimit = xidStopLimit - 10000000;
311-
if (xidWarnLimit < FirstNormalTransactionId)
312-
xidWarnLimit -= FirstNormalTransactionId;
313-
314227
/*
315228
* We'll start trying to force autovacuums when oldest_datfrozenxid gets
316229
* to be more than autovacuum_freeze_max_age transactions old.
@@ -334,18 +247,10 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
334247
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
335248
ShmemVariableCache->oldestXid = oldest_datfrozenxid;
336249
ShmemVariableCache->xidVacLimit = xidVacLimit;
337-
ShmemVariableCache->xidWarnLimit = xidWarnLimit;
338-
ShmemVariableCache->xidStopLimit = xidStopLimit;
339-
ShmemVariableCache->xidWrapLimit = xidWrapLimit;
340250
ShmemVariableCache->oldestXidDB = oldest_datoid;
341251
curXid = ShmemVariableCache->nextXid;
342252
LWLockRelease(XidGenLock);
343253

344-
/* Log the info */
345-
ereport(DEBUG1,
346-
(errmsg("transaction ID wrap limit is " XID_FMT ", limited by database with OID %u",
347-
xidWrapLimit, oldest_datoid)));
348-
349254
/*
350255
* If past the autovacuum force point, immediately signal an autovac
351256
* request. The reason for this is that autovac only processes one
@@ -356,41 +261,6 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
356261
if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
357262
IsUnderPostmaster && !InRecovery)
358263
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
359-
360-
/* Give an immediate warning if past the wrap warn point */
361-
if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
362-
{
363-
char *oldest_datname;
364-
365-
/*
366-
* We can be called when not inside a transaction, for example during
367-
* StartupXLOG(). In such a case we cannot do database access, so we
368-
* must just report the oldest DB's OID.
369-
*
370-
* Note: it's also possible that get_database_name fails and returns
371-
* NULL, for example because the database just got dropped. We'll
372-
* still warn, even though the warning might now be unnecessary.
373-
*/
374-
if (IsTransactionState())
375-
oldest_datname = get_database_name(oldest_datoid);
376-
else
377-
oldest_datname = NULL;
378-
379-
if (oldest_datname)
380-
ereport(WARNING,
381-
(errmsg("database \"%s\" must be vacuumed within " XID_FMT " transactions",
382-
oldest_datname,
383-
xidWrapLimit - curXid),
384-
errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
385-
"You might also need to commit or roll back old prepared transactions.")));
386-
else
387-
ereport(WARNING,
388-
(errmsg("database with OID %u must be vacuumed within " XID_FMT " transactions",
389-
oldest_datoid,
390-
xidWrapLimit - curXid),
391-
errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
392-
"You might also need to commit or roll back old prepared transactions.")));
393-
}
394264
}
395265

396266

0 commit comments

Comments
 (0)