8000 Do not select new object OIDs that match recently-dead entries. · tomdcc/postgres@7448e7e · GitHub
[go: up one dir, main page]

Skip to content

Commit 7448e7e

Browse files
committed
Do not select new object OIDs that match recently-dead entries.
When selecting a new OID, we take care to avoid picking one that's already in use in the target table, so as not to create duplicates after the OID counter has wrapped around. However, up to now we used SnapshotDirty when scanning for pre-existing entries. That ignores committed-dead rows, so that we could select an OID matching a deleted-but-not-yet-vacuumed row. While that mostly worked, it has two problems: * If recently deleted, the dead row might still be visible to MVCC snapshots, creating a risk for duplicate OIDs when examining the catalogs within our own transaction. Such duplication couldn't be visible outside the object-creating transaction, though, and we've heard few if any field reports corresponding to such a symptom. * When selecting a TOAST OID, deleted toast rows definitely *are* visible to SnapshotToast, and will remain so until vacuumed away. This leads to a conflict that will manifest in errors like "unexpected chunk number 0 (expected 1) for toast value nnnnn". We've been seeing reports of such errors from the field for years, but the cause was unclear before. The fix is simple: just use SnapshotAny to search for conflicting rows. This results in a slightly longer window before object OIDs can be recycled, but that seems unlikely to create any large problems. Pavan Deolasee Discussion: https://postgr.es/m/CABOikdOgWT2hHkYG3Wwo2cyZJq2zfs1FH0FgX-=h4OLosXHf9w@mail.gmail.com
1 parent dfc383c commit 7448e7e

File tree

2 files changed

+12
-9
lines changed

2 files changed

+12
-9
lines changed

src/backend/access/heap/tuptoaster.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,9 @@ toast_delete_datum(Relation rel, Datum value)
15231523
/* ----------
15241524
* toastrel_valueid_exists -
15251525
*
1526-
* Test whether a toast value with the given ID exists in the toast relation
1526+
* Test whether a toast value with the given ID exists in the toast relation.
1527+
* For safety, we consider a value to exist if there are either live or dead
1528+
* toast rows with that ID; see notes for GetNewOid().
15271529
* ----------
15281530
*/
15291531
static bool
@@ -1545,7 +1547,7 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid)
15451547
* Is there any such chunk?
15461548
*/
15471549
toastscan = systable_beginscan(toastrel, toastrel->rd_rel->reltoastidxid,
1548-
true, SnapshotToast, 1, &toastkey);
1550+
true, SnapshotAny, 1, &toastkey);
15491551

15501552
if (systable_getnext(toastscan) != NULL)
15511553
result = true;

src/backend/catalog/catalog.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,12 @@ IsSharedRelation(Oid relationId)
292292
* managed to cycle through 2^32 OIDs and generate the same OID before we
293293
* finish inserting our row. This seems unlikely to be a problem. Note
294294
* that if we had to *commit* the row to end the race condition, the risk
295-
* would be rather higher; therefore we use SnapshotDirty in the test,
296-
* so that we will see uncommitted rows.
295+
* would be rather higher; therefore we use SnapshotAny in the test, so that
296+
* we will see uncommitted rows. (We used to use SnapshotDirty, but that has
297+
* the disadvantage that it ignores recently-deleted rows, creating a risk
298+
* of transient conflicts for as long as our own MVCC snapshots think a
299+
* recently-deleted row is live. The risk is far higher when selecting TOAST
300+
* OIDs, because SnapshotToast considers dead rows as active indefinitely.)
297301
*/
298302
Oid
299303
GetNewOid(Relation relation)
@@ -346,13 +350,10 @@ Oid
346350
GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
347351
{
348352
Oid newOid;
349-
SnapshotData SnapshotDirty;
350353
SysScanDesc scan;
351354
ScanKeyData key;
352355
bool collides;
353356

354-
InitDirtySnapshot(SnapshotDirty);
355-
356357
/* Generate new OIDs until we find one not in the table */
357358
do
358359
{
@@ -365,9 +366,9 @@ GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
365366
BTEqualStrategyNumber, F_OIDEQ,
366367
ObjectIdGetDatum(newOid));
367368

368-
/* see notes above about using SnapshotDirty */
369+
/* see notes above about using SnapshotAny */
369370
scan = systable_beginscan(relation, indexId, true,
370-
&SnapshotDirty, 1, &key);
371+
SnapshotAny, 1, &key);
371372

372373
collides = HeapTupleIsValid(systable_getnext(scan));
373374

0 commit comments

Comments
 (0)
0