Skip to content

Commit bbbe2c9

Browse files
committed
Fetch XIDs atomically during vac_truncate_clog().
Because vac_update_datfrozenxid() updates datfrozenxid and datminmxid in-place, it's unsafe to assume that successive reads of those values will give consistent results. Fetch each one just once to ensure sane behavior in the minimum calculation. Noted while reviewing Alexander Korotkov's patch in the same area. Discussion: <8564.1464116473@sss.pgh.pa.us>
1 parent a34c3dd commit bbbe2c9

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

src/backend/commands/vacuum.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,12 @@ vac_truncate_clog(TransactionId frozenXID,
10661066
/*
10671067
* Scan pg_database to compute the minimum datfrozenxid/datminmxid
10681068
*
1069+
* Since vac_update_datfrozenxid updates datfrozenxid/datminmxid in-place,
1070+
* the values could change while we look at them. Fetch each one just
1071+
* once to ensure sane behavior of the comparison logic. (Here, as in
1072+
* many other places, we assume that fetching or updating an XID in shared
1073+
* storage is atomic.)
1074+
*
10691075
* Note: we need not worry about a race condition with new entries being
10701076
* inserted by CREATE DATABASE. Any such entry will have a copy of some
10711077
* existing DB's datfrozenxid, and that source DB cannot be ours because
@@ -1081,10 +1087,12 @@ vac_truncate_clog(TransactionId frozenXID,
10811087

10821088
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
10831089
{
1084-
Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
1090+
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
1091+
TransactionId datfrozenxid = dbform->datfrozenxid;
1092+
TransactionId datminmxid = dbform->datminmxid;
10851093

1086-
Assert(TransactionIdIsNormal(dbform->datfrozenxid));
1087-
Assert(MultiXactIdIsValid(dbform->datminmxid));
1094+
Assert(TransactionIdIsNormal(datfrozenxid));
1095+
Assert(MultiXactIdIsValid(datminmxid));
10881096

10891097
/*
10901098
* If things are working properly, no database should have a
@@ -1095,21 +1103,21 @@ vac_truncate_clog(TransactionId frozenXID,
10951103
* databases have been scanned and cleaned up. (We will issue the
10961104
* "already wrapped" warning if appropriate, though.)
10971105
*/
1098-
if (TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid) ||
1099-
MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid))
1106+
if (TransactionIdPrecedes(lastSaneFrozenXid, datfrozenxid) ||
1107+
MultiXactIdPrecedes(lastSaneMinMulti, datminmxid))
11001108
bogus = true;
11011109

1102-
if (TransactionIdPrecedes(nextXID, dbform->datfrozenxid))
1110+
if (TransactionIdPrecedes(nextXID, datfrozenxid))
11031111
frozenAlreadyWrapped = true;
1104-
else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
1112+
else if (TransactionIdPrecedes(datfrozenxid, frozenXID))
11051113
{
1106-
frozenXID = dbform->datfrozenxid;
1114+
frozenXID = datfrozenxid;
11071115
oldestxid_datoid = HeapTupleGetOid(tuple);
11081116
}
11091117

1110-
if (MultiXactIdPrecedes(dbform->datminmxid, minMulti))
1118+
if (MultiXactIdPrecedes(datminmxid, minMulti))
11111119
{
1112-
minMulti = dbform->datminmxid;
1120+
minMulti = datminmxid;
11131121
minmulti_datoid = HeapTupleGetOid(tuple);
11141122
}
11151123
}

0 commit comments

Comments
 (0)