Revert changes to CONCURRENTLY that "sped up" Xmin advance
authorAlvaro Herrera
Tue, 31 May 2022 19:24:59 +0000 (21:24 +0200)
committerAlvaro Herrera
Tue, 31 May 2022 19:24:59 +0000 (21:24 +0200)
This reverts commit d9d076222f5b "VACUUM: ignore indexing operations
with CONCURRENTLY".

These changes caused indexes created with the CONCURRENTLY option to
miss heap tuples that were HOT-updated and HOT-pruned during the index
creation.  Before these changes, HOT pruning would have been prevented
by the Xmin of the transaction creating the index, but because this
change was precisely to allow the Xmin to move forward ignoring that
backend, now other backends scanning the table can prune them.  This is
not a problem for VACUUM (which requires a lock that conflicts with a
CREATE INDEX CONCURRENTLY operation), but HOT-prune can definitely
occur.  In other words, Xmin advancement was sped up, but at the cost of
corrupting the resulting index.

Regrettably, this means that the new feature in PG14 that RIC/CIC on
very large tables no longer force VACUUM to retain very old tuples goes
away.  We might try to implement it again in a later release, but for
now the risk of indexes missing tuples is too high and there's no easy
fix.

Backpatch to 14, where this change appeared.

Reported-by: Peter Slavov
Diagnosys-by: Andrey Borodin
Diagnosys-by: Michael Paquier
Diagnosys-by: Andres Freund
Discussion: https://postgr.es/m/17485-396609c6925b982d%40postgresql.org

doc/src/sgml/ref/create_index.sgml
doc/src/sgml/ref/reindex.sgml
src/backend/storage/ipc/procarray.c

index 11179f0b9d9d5cdb947b862af24fc068b1e2d75e..df45bc97466c82e86566aa0f634c6578431f373b 100644 (file)
@@ -845,8 +845,6 @@ Indexes:
    Like any long-running transaction, CREATE INDEX on a
    table can affect which tuples can be removed by concurrent
    VACUUM on any other table.
-   Excepted from this are operations with the CONCURRENTLY
-   option for indexes that are not partial and do not index any expressions.
   
 
   
index e6b25ee670f7376560b701eabe10e96352f439a1..22dbb520d520a0a5fc4c70d00334fbb6233a39bc 100644 (file)
@@ -470,8 +470,6 @@ Indexes:
     Like any long-running transaction, REINDEX on a table
     can affect which tuples can be removed by concurrent
     VACUUM on any other table.
-    Excepted from this are operations with the CONCURRENTLY
-    option for indexes that are not partial and do not index any expressions.
    
 
    
index 08053a7e8f19fbe347c4a05dad324f7b54aac587..33a890736a574f0c268b4832569610593d24d233 100644 (file)
@@ -1667,13 +1667,7 @@ TransactionIdIsActive(TransactionId xid)
  * relations that's not required, since only backends in my own database could
  * ever see the tuples in them. Also, we can ignore concurrently running lazy
  * VACUUMs because (a) they must be working on other tables, and (b) they
- * don't need to do snapshot-based lookups.  Similarly, for the non-catalog
- * horizon, we can ignore CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY
- * when they are working on non-partial, non-expressional indexes, for the
- * same reasons and because they can't run in transaction blocks.  (They are
- * not possible to ignore for catalogs, because CIC and RC do some catalog
- * operations.)  Do note that this means that CIC and RC must use a lock level
- * that conflicts with VACUUM.
+ * don't need to do snapshot-based lookups.
  *
  * This also computes a horizon used to truncate pg_subtrans. For that
  * backends in all databases have to be considered, and concurrently running
@@ -1723,6 +1717,9 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
    bool        in_recovery = RecoveryInProgress();
    TransactionId *other_xids = ProcGlobal->xids;
 
+   /* inferred after ProcArrayLock is released */
+   h->catalog_oldest_nonremovable = InvalidTransactionId;
+
    LWLockAcquire(ProcArrayLock, LW_SHARED);
 
    h->latest_completed = ShmemVariableCache->latestCompletedXid;
@@ -1742,7 +1739,6 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
 
        h->oldest_considered_running = initial;
        h->shared_oldest_nonremovable = initial;
-       h->catalog_oldest_nonremovable = initial;
        h->data_oldest_nonremovable = initial;
 
        /*
@@ -1834,26 +1830,11 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
            MyDatabaseId == InvalidOid || proc->databaseId == MyDatabaseId ||
            proc->databaseId == 0)  /* always include WalSender */
        {
-           /*
-            * We can ignore this backend if it's running CREATE INDEX
-            * CONCURRENTLY or REINDEX CONCURRENTLY on a "safe" index -- but
-            * only on vacuums of user-defined tables.
-            */
-           if (!(statusFlags & PROC_IN_SAFE_IC))
-               h->data_oldest_nonremovable =
-                   TransactionIdOlder(h->data_oldest_nonremovable, xmin);
-
-           /* Catalog tables need to consider all backends in this db */
-           h->catalog_oldest_nonremovable =
-               TransactionIdOlder(h->catalog_oldest_nonremovable, xmin);
-
+           h->data_oldest_nonremovable =
+               TransactionIdOlder(h->data_oldest_nonremovable, xmin);
        }
    }
 
-   /* catalog horizon should never be later than data */
-   Assert(TransactionIdPrecedesOrEquals(h->catalog_oldest_nonremovable,
-                                        h->data_oldest_nonremovable));
-
    /*
     * If in recovery fetch oldest xid in KnownAssignedXids, will be applied
     * after lock is released.
@@ -1875,8 +1856,6 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
            TransactionIdOlder(h->shared_oldest_nonremovable, kaxmin);
        h->data_oldest_nonremovable =
            TransactionIdOlder(h->data_oldest_nonremovable, kaxmin);
-       h->catalog_oldest_nonremovable =
-           TransactionIdOlder(h->catalog_oldest_nonremovable, kaxmin);
        /* temp relations cannot be accessed in recovery */
    }
    else
@@ -1903,9 +1882,6 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
        h->data_oldest_nonremovable =
            TransactionIdRetreatedBy(h->data_oldest_nonremovable,
                                     vacuum_defer_cleanup_age);
-       h->catalog_oldest_nonremovable =
-           TransactionIdRetreatedBy(h->catalog_oldest_nonremovable,
-                                    vacuum_defer_cleanup_age);
        /* defer doesn't apply to temp relations */
    }
 
@@ -1928,9 +1904,7 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
    h->shared_oldest_nonremovable =
        TransactionIdOlder(h->shared_oldest_nonremovable,
                           h->slot_catalog_xmin);
-   h->catalog_oldest_nonremovable =
-       TransactionIdOlder(h->catalog_oldest_nonremovable,
-                          h->slot_xmin);
+   h->catalog_oldest_nonremovable = h->data_oldest_nonremovable;
    h->catalog_oldest_nonremovable =
        TransactionIdOlder(h->catalog_oldest_nonremovable,
                           h->slot_catalog_xmin);