Skip to content

Commit d9d0762

Browse files
committed
VACUUM: ignore indexing operations with CONCURRENTLY
As envisioned in commit c98763b, it is possible for VACUUM to ignore certain transactions that are executing CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY for the purposes of computing Xmin; that's because we know those transactions are not going to examine any other tables, and are not going to execute anything else in the same transaction. (Only operations on "safe" indexes can be ignored: those on indexes that are neither partial nor expressional). This is extremely useful in cases where CIC/RC can run for a very long time, because that used to be a significant headache for concurrent vacuuming of other tables. Reviewed-by: Matthias van de Meent <boekewurm+postgres@gmail.com> Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com> Discussion: https://postgr.es/m/20210115133858.GA18931@alvherre.pgsql
1 parent 6f6f284 commit d9d0762

File tree

4 files changed

+39
-9
lines changed

4 files changed

+39
-9
lines changed

doc/src/sgml/ref/create_index.sgml

+2
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,8 @@ Indexes:
855855
Like any long-running transaction, <command>CREATE INDEX</command> on a
856856
table can affect which tuples can be removed by concurrent
857857
<command>VACUUM</command> on any other table.
858+
Excepted from this are operations with the <literal>CONCURRENTLY</literal>
859+
option for indexes that are not partial and do not index any expressions.
858860
</para>
859861

860862
<para>

doc/src/sgml/ref/reindex.sgml

+2
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ Indexes:
478478
Like any long-running transaction, <command>REINDEX</command> on a table
479479
can affect which tuples can be removed by concurrent
480480
<command>VACUUM</command> on any other table.
481+
Excepted from this are operations with the <literal>CONCURRENTLY</literal>
482+
option for indexes that are not partial and do not index any expressions.
481483
</para>
482484

483485
<para>

src/backend/storage/ipc/procarray.c

+34-8
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,13 @@ TransactionIdIsActive(TransactionId xid)
16101610
* relations that's not required, since only backends in my own database could
16111611
* ever see the tuples in them. Also, we can ignore concurrently running lazy
16121612
* VACUUMs because (a) they must be working on other tables, and (b) they
1613-
* don't need to do snapshot-based lookups.
1613+
* don't need to do snapshot-based lookups. Similarly, for the non-catalog
1614+
* horizon, we can ignore CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY
1615+
* when they are working on non-partial, non-expressional indexes, for the
1616+
* same reasons and because they can't run in transaction blocks. (They are
1617+
* not possible to ignore for catalogs, because CIC and RC do some catalog
1618+
* operations.) Do note that this means that CIC and RC must use a lock level
1619+
* that conflicts with VACUUM.
16141620
*
16151621
* This also computes a horizon used to truncate pg_subtrans. For that
16161622
* backends in all databases have to be considered, and concurrently running
@@ -1660,9 +1666,6 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
16601666
bool in_recovery = RecoveryInProgress();
16611667
TransactionId *other_xids = ProcGlobal->xids;
16621668

1663-
/* inferred after ProcArrayLock is released */
1664-
h->catalog_oldest_nonremovable = InvalidTransactionId;
1665-
16661669
LWLockAcquire(ProcArrayLock, LW_SHARED);
16671670

16681671
h->latest_completed = ShmemVariableCache->latestCompletedXid;
@@ -1682,6 +1685,7 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
16821685

16831686
h->oldest_considered_running = initial;
16841687
h->shared_oldest_nonremovable = initial;
1688+
h->catalog_oldest_nonremovable = initial;
16851689
h->data_oldest_nonremovable = initial;
16861690

16871691
/*
@@ -1752,7 +1756,7 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
17521756
if (statusFlags & (PROC_IN_VACUUM | PROC_IN_LOGICAL_DECODING))
17531757
continue;
17541758

1755-
/* shared tables need to take backends in all database into account */
1759+
/* shared tables need to take backends in all databases into account */
17561760
h->shared_oldest_nonremovable =
17571761
TransactionIdOlder(h->shared_oldest_nonremovable, xmin);
17581762

@@ -1773,11 +1777,26 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
17731777
MyDatabaseId == InvalidOid || proc->databaseId == MyDatabaseId ||
17741778
proc->databaseId == 0) /* always include WalSender */
17751779
{
1776-
h->data_oldest_nonremovable =
1777-
TransactionIdOlder(h->data_oldest_nonremovable, xmin);
1780+
/*
1781+
* We can ignore this backend if it's running CREATE INDEX
1782+
* CONCURRENTLY or REINDEX CONCURRENTLY on a "safe" index -- but
1783+
* only on vacuums of user-defined tables.
1784+
*/
1785+
if (!(statusFlags & PROC_IN_SAFE_IC))
1786+
h->data_oldest_nonremovable =
1787+
TransactionIdOlder(h->data_oldest_nonremovable, xmin);
1788+
1789+
/* Catalog tables need to consider all backends in this db */
1790+
h->catalog_oldest_nonremovable =
1791+
TransactionIdOlder(h->catalog_oldest_nonremovable, xmin);
1792+
17781793
}
17791794
}
17801795

1796+
/* catalog horizon should never be later than data */
1797+
Assert(TransactionIdPrecedesOrEquals(h->catalog_oldest_nonremovable,
1798+
h->data_oldest_nonremovable));
1799+
17811800
/*
17821801
* If in recovery fetch oldest xid in KnownAssignedXids, will be applied
17831802
* after lock is released.
@@ -1799,6 +1818,8 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
17991818
TransactionIdOlder(h->shared_oldest_nonremovable, kaxmin);
18001819
h->data_oldest_nonremovable =
18011820
TransactionIdOlder(h->data_oldest_nonremovable, kaxmin);
1821+
h->catalog_oldest_nonremovable =
1822+
TransactionIdOlder(h->catalog_oldest_nonremovable, kaxmin);
18021823
/* temp relations cannot be accessed in recovery */
18031824
}
18041825
else
@@ -1825,6 +1846,9 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
18251846
h->data_oldest_nonremovable =
18261847
TransactionIdRetreatedBy(h->data_oldest_nonremovable,
18271848
vacuum_defer_cleanup_age);
1849+
h->catalog_oldest_nonremovable =
1850+
TransactionIdRetreatedBy(h->catalog_oldest_nonremovable,
1851+
vacuum_defer_cleanup_age);
18281852
/* defer doesn't apply to temp relations */
18291853
}
18301854

@@ -1847,7 +1871,9 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
18471871
h->shared_oldest_nonremovable =
18481872
TransactionIdOlder(h->shared_oldest_nonremovable,
18491873
h->slot_catalog_xmin);
1850-
h->catalog_oldest_nonremovable = h->data_oldest_nonremovable;
1874+
h->catalog_oldest_nonremovable =
1875+
TransactionIdOlder(h->catalog_oldest_nonremovable,
1876+
h->slot_xmin);
18511877
h->catalog_oldest_nonremovable =
18521878
TransactionIdOlder(h->catalog_oldest_nonremovable,
18531879
h->slot_catalog_xmin);

src/backend/utils/misc/guc.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2583,7 +2583,7 @@ static struct config_int ConfigureNamesInt[] =
25832583
NULL
25842584
},
25852585
&vacuum_defer_cleanup_age,
2586-
0, 0, 1000000,
2586+
0, 0, 1000000, /* see ComputeXidHorizons */
25872587
NULL, NULL, NULL
25882588
},
25892589

0 commit comments

Comments
 (0)