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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy