LCOV - code coverage report
Current view: top level - src/backend/commands - vacuumparallel.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.8 % 403 390
Test Date: 2026-10-16 08:15:56 Functions: 100.0 % 19 19
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 83.7 % 190 159

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * vacuumparallel.c
       4                 :             :  *    Support routines for parallel vacuum and autovacuum execution. In the
       5                 :             :  *    comments below, the word "vacuum" will refer to both vacuum and
       6                 :             :  *    autovacuum.
       7                 :             :  *
       8                 :             :  * This file contains routines that are intended to support setting up, using,
       9                 :             :  * and tearing down a ParallelVacuumState.
      10                 :             :  *
      11                 :             :  * In a parallel vacuum, we perform both index bulk deletion and index cleanup
      12                 :             :  * with parallel worker processes.  Individual indexes are processed by one
      13                 :             :  * vacuum process.  ParallelVacuumState contains shared information as well as
      14                 :             :  * the memory space for storing dead items allocated in the DSA area.  We
      15                 :             :  * launch parallel worker processes at the start of parallel index
      16                 :             :  * bulk-deletion and index cleanup and once all indexes are processed, the
      17                 :             :  * parallel worker processes exit.  Each time we process indexes in parallel,
      18                 :             :  * the parallel context is re-initialized so that the same DSM can be used for
      19                 :             :  * multiple passes of index bulk-deletion and index cleanup.
      20                 :             :  *
      21                 :             :  * For parallel autovacuum, we need to propagate cost-based vacuum delay
      22                 :             :  * parameters from the leader to its workers, as the leader's parameters can
      23                 :             :  * change even while processing a table (e.g., due to a config reload).
      24                 :             :  * The PVSharedCostParams struct manages these parameters using a
      25                 :             :  * generation counter. Each parallel worker polls this shared state and
      26                 :             :  * refreshes its local delay parameters whenever a change is detected.
      27                 :             :  *
      28                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      29                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      30                 :             :  *
      31                 :             :  * IDENTIFICATION
      32                 :             :  *    src/backend/commands/vacuumparallel.c
      33                 :             :  *
      34                 :             :  *-------------------------------------------------------------------------
      35                 :             :  */
      36                 :             : #include "postgres.h"
      37                 :             : 
      38                 :             : #include "access/amapi.h"
      39                 :             : #include "access/table.h"
      40                 :             : #include "access/xact.h"
      41                 :             : #include "commands/progress.h"
      42                 :             : #include "commands/vacuum.h"
      43                 :             : #include "executor/instrument.h"
      44                 :             : #include "optimizer/paths.h"
      45                 :             : #include "pgstat.h"
      46                 :             : #include "postmaster/interrupt.h"
      47                 :             : #include "storage/bufmgr.h"
      48                 :             : #include "storage/proc.h"
      49                 :             : #include "tcop/tcopprot.h"
      50                 :             : #include "utils/injection_point.h"
      51                 :             : #include "utils/lsyscache.h"
      52                 :             : #include "utils/rel.h"
      53                 :             : 
      54                 :             : /*
      55                 :             :  * DSM keys for parallel vacuum.  Unlike other parallel execution code, since
      56                 :             :  * we don't need to worry about DSM keys conflicting with plan_node_id we can
      57                 :             :  * use small integers.
      58                 :             :  */
      59                 :             : #define PARALLEL_VACUUM_KEY_SHARED          1
      60                 :             : #define PARALLEL_VACUUM_KEY_QUERY_TEXT      2
      61                 :             : #define PARALLEL_VACUUM_KEY_BUFFER_USAGE    3
      62                 :             : #define PARALLEL_VACUUM_KEY_WAL_USAGE       4
      63                 :             : #define PARALLEL_VACUUM_KEY_INDEX_STATS     5
      64                 :             : 
      65                 :             : /*
      66                 :             :  * Struct for cost-based vacuum delay related parameters to share among an
      67                 :             :  * autovacuum worker and its parallel vacuum workers.
      68                 :             :  */
      69                 :             : typedef struct PVSharedCostParams
      70                 :             : {
      71                 :             :     /*
      72                 :             :      * The generation counter is incremented by the leader process each time
      73                 :             :      * it updates the shared cost-based vacuum delay parameters. Parallel
      74                 :             :      * vacuum workers compare it with their local generation,
      75                 :             :      * shared_params_generation_local, to detect whether they need to refresh
      76                 :             :      * their local parameters. The generation starts from 1 so that a freshly
      77                 :             :      * started worker (whose local copy is 0) will always load the initial
      78                 :             :      * parameters on its first check.
      79                 :             :      */
      80                 :             :     pg_atomic_uint32 generation;
      81                 :             : 
      82                 :             :     slock_t     mutex;          /* protects all fields below */
      83                 :             : 
      84                 :             :     /* Parameters to share with parallel workers */
      85                 :             :     double      cost_delay;
      86                 :             :     int         cost_limit;
      87                 :             :     int         cost_page_dirty;
      88                 :             :     int         cost_page_hit;
      89                 :             :     int         cost_page_miss;
      90                 :             :     bool        track_cost_delay_timing;
      91                 :             : } PVSharedCostParams;
      92                 :             : 
      93                 :             : /*
      94                 :             :  * Shared information among parallel workers.  So this is allocated in the DSM
      95                 :             :  * segment.
      96                 :             :  */
      97                 :             : typedef struct PVShared
      98                 :             : {
      99                 :             :     /*
     100                 :             :      * Target table relid, log level (for messages about parallel workers
     101                 :             :      * launched during VACUUM VERBOSE) and query ID.  These fields are not
     102                 :             :      * modified during the parallel vacuum.
     103                 :             :      */
     104                 :             :     Oid         relid;
     105                 :             :     int         elevel;
     106                 :             :     int64       queryid;
     107                 :             : 
     108                 :             :     /*
     109                 :             :      * Fields for both index vacuum and cleanup.
     110                 :             :      *
     111                 :             :      * reltuples is the total number of input heap tuples.  We set either old
     112                 :             :      * live tuples in the index vacuum case or the new live tuples in the
     113                 :             :      * index cleanup case.
     114                 :             :      *
     115                 :             :      * estimated_count is true if reltuples is an estimated value.  (Note that
     116                 :             :      * reltuples could be -1 in this case, indicating we have no idea.)
     117                 :             :      */
     118                 :             :     double      reltuples;
     119                 :             :     bool        estimated_count;
     120                 :             : 
     121                 :             :     /*
     122                 :             :      * In single process vacuum we could consume more memory during index
     123                 :             :      * vacuuming or cleanup apart from the memory for heap scanning.  In
     124                 :             :      * parallel vacuum, since individual vacuum workers can consume memory
     125                 :             :      * equal to maintenance_work_mem, the new maintenance_work_mem for each
     126                 :             :      * worker is set such that the parallel operation doesn't consume more
     127                 :             :      * memory than single process vacuum.
     128                 :             :      */
     129                 :             :     int         maintenance_work_mem_worker;
     130                 :             : 
     131                 :             :     /*
     132                 :             :      * The number of buffers each worker's Buffer Access Strategy ring should
     133                 :             :      * contain.
     134                 :             :      */
     135                 :             :     int         ring_nbuffers;
     136                 :             : 
     137                 :             :     /*
     138                 :             :      * Shared vacuum cost balance.  During parallel vacuum,
     139                 :             :      * VacuumSharedCostBalance points to this value and it accumulates the
     140                 :             :      * balance of each parallel vacuum worker.
     141                 :             :      */
     142                 :             :     pg_atomic_uint32 cost_balance;
     143                 :             : 
     144                 :             :     /*
     145                 :             :      * Number of active parallel workers.  This is used for computing the
     146                 :             :      * minimum threshold of the vacuum cost balance before a worker sleeps for
     147                 :             :      * cost-based delay.
     148                 :             :      */
     149                 :             :     pg_atomic_uint32 active_nworkers;
     150                 :             : 
     151                 :             :     /* Counter for vacuuming and cleanup */
     152                 :             :     pg_atomic_uint32 idx;
     153                 :             : 
     154                 :             :     /* DSA handle where the TidStore lives */
     155                 :             :     dsa_handle  dead_items_dsa_handle;
     156                 :             : 
     157                 :             :     /* DSA pointer to the shared TidStore */
     158                 :             :     dsa_pointer dead_items_handle;
     159                 :             : 
     160                 :             :     /* Statistics of shared dead items */
     161                 :             :     VacDeadItemsInfo dead_items_info;
     162                 :             : 
     163                 :             :     /*
     164                 :             :      * If 'true' then we are running parallel autovacuum. Otherwise, we are
     165                 :             :      * running parallel maintenance VACUUM.
     166                 :             :      */
     167                 :             :     bool        is_autovacuum;
     168                 :             : 
     169                 :             :     /*
     170                 :             :      * Cost-based vacuum delay parameters shared between the autovacuum leader
     171                 :             :      * and its parallel workers.
     172                 :             :      */
     173                 :             :     PVSharedCostParams cost_params;
     174                 :             : } PVShared;
     175                 :             : 
     176                 :             : /* Status used during parallel index vacuum or cleanup */
     177                 :             : typedef enum PVIndVacStatus
     178                 :             : {
     179                 :             :     PARALLEL_INDVAC_STATUS_INITIAL = 0,
     180                 :             :     PARALLEL_INDVAC_STATUS_NEED_BULKDELETE,
     181                 :             :     PARALLEL_INDVAC_STATUS_NEED_CLEANUP,
     182                 :             :     PARALLEL_INDVAC_STATUS_COMPLETED,
     183                 :             : } PVIndVacStatus;
     184                 :             : 
     185                 :             : /*
     186                 :             :  * Struct for index vacuum statistics of an index that is used for parallel vacuum.
     187                 :             :  * This includes the status of parallel index vacuum as well as index statistics.
     188                 :             :  */
     189                 :             : typedef struct PVIndStats
     190                 :             : {
     191                 :             :     /*
     192                 :             :      * The following two fields are set by leader process before executing
     193                 :             :      * parallel index vacuum or parallel index cleanup.  These fields are not
     194                 :             :      * fixed for the entire VACUUM operation.  They are only fixed for an
     195                 :             :      * individual parallel index vacuum and cleanup.
     196                 :             :      *
     197                 :             :      * parallel_workers_can_process is true if both leader and worker can
     198                 :             :      * process the index, otherwise only leader can process it.
     199                 :             :      */
     200                 :             :     PVIndVacStatus status;
     201                 :             :     bool        parallel_workers_can_process;
     202                 :             : 
     203                 :             :     /*
     204                 :             :      * Individual worker or leader stores the result of index vacuum or
     205                 :             :      * cleanup.
     206                 :             :      */
     207                 :             :     bool        istat_updated;  /* are the stats updated? */
     208                 :             :     IndexBulkDeleteResult istat;
     209                 :             : } PVIndStats;
     210                 :             : 
     211                 :             : /*
     212                 :             :  * Struct for maintaining a parallel vacuum state. typedef appears in vacuum.h.
     213                 :             :  */
     214                 :             : struct ParallelVacuumState
     215                 :             : {
     216                 :             :     /* NULL for worker processes */
     217                 :             :     ParallelContext *pcxt;
     218                 :             : 
     219                 :             :     /* Parent Heap Relation */
     220                 :             :     Relation    heaprel;
     221                 :             : 
     222                 :             :     /* Target indexes */
     223                 :             :     Relation   *indrels;
     224                 :             :     int         nindexes;
     225                 :             : 
     226                 :             :     /* Shared information among parallel vacuum workers */
     227                 :             :     PVShared   *shared;
     228                 :             : 
     229                 :             :     /*
     230                 :             :      * Shared index statistics among parallel vacuum workers. The array
     231                 :             :      * element is allocated for every index, even those indexes where parallel
     232                 :             :      * index vacuuming is unsafe or not worthwhile (e.g.,
     233                 :             :      * will_parallel_vacuum[] is false).  During parallel vacuum,
     234                 :             :      * IndexBulkDeleteResult of each index is kept in DSM and is copied into
     235                 :             :      * local memory at the end of parallel vacuum.
     236                 :             :      */
     237                 :             :     PVIndStats *indstats;
     238                 :             : 
     239                 :             :     /* Shared dead items space among parallel vacuum workers */
     240                 :             :     TidStore   *dead_items;
     241                 :             : 
     242                 :             :     /* Points to buffer usage area in DSM */
     243                 :             :     BufferUsage *buffer_usage;
     244                 :             : 
     245                 :             :     /* Points to WAL usage area in DSM */
     246                 :             :     WalUsage   *wal_usage;
     247                 :             : 
     248                 :             :     /*
     249                 :             :      * False if the index is totally unsuitable target for all parallel
     250                 :             :      * processing. For example, the index could be <
     251                 :             :      * min_parallel_index_scan_size cutoff.
     252                 :             :      */
     253                 :             :     bool       *will_parallel_vacuum;
     254                 :             : 
     255                 :             :     /*
     256                 :             :      * The number of indexes that support parallel index bulk-deletion and
     257                 :             :      * parallel index cleanup respectively.
     258                 :             :      */
     259                 :             :     int         nindexes_parallel_bulkdel;
     260                 :             :     int         nindexes_parallel_cleanup;
     261                 :             :     int         nindexes_parallel_condcleanup;
     262                 :             : 
     263                 :             :     /* Buffer access strategy used by leader process */
     264                 :             :     BufferAccessStrategy bstrategy;
     265                 :             : 
     266                 :             :     /*
     267                 :             :      * Error reporting state.  The error callback is set only for workers
     268                 :             :      * processes during parallel index vacuum.
     269                 :             :      */
     270                 :             :     char       *relnamespace;
     271                 :             :     char       *relname;
     272                 :             :     char       *indname;
     273                 :             :     PVIndVacStatus status;
     274                 :             : };
     275                 :             : 
     276                 :             : static PVSharedCostParams *pv_shared_cost_params = NULL;
     277                 :             : 
     278                 :             : /*
     279                 :             :  * Worker-local copy of the last cost-parameter generation this worker has
     280                 :             :  * applied.  Initialized to 0; since the leader initializes the shared
     281                 :             :  * generation counter to 1, the first call to
     282                 :             :  * parallel_vacuum_update_shared_delay_params() will always detect a
     283                 :             :  * mismatch and read the initial parameters from shared memory.
     284                 :             :  */
     285                 :             : static uint32 shared_params_generation_local = 0;
     286                 :             : 
     287                 :             : static int  parallel_vacuum_compute_workers(Relation *indrels, int nindexes, int nrequested,
     288                 :             :                                             bool *will_parallel_vacuum);
     289                 :             : static void parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scans,
     290                 :             :                                                 bool vacuum, PVWorkerStats *wstats);
     291                 :             : static void parallel_vacuum_process_safe_indexes(ParallelVacuumState *pvs);
     292                 :             : static void parallel_vacuum_process_unsafe_indexes(ParallelVacuumState *pvs);
     293                 :             : static void parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel,
     294                 :             :                                               PVIndStats *indstats);
     295                 :             : static bool parallel_vacuum_index_is_parallel_safe(Relation indrel, int num_index_scans,
     296                 :             :                                                    bool vacuum);
     297                 :             : static void parallel_vacuum_error_callback(void *arg);
     298                 :             : static inline void parallel_vacuum_set_cost_parameters(PVSharedCostParams *params);
     299                 :             : static void parallel_vacuum_dsm_detach(dsm_segment *seg, Datum arg);
     300                 :             : 
     301                 :             : /*
     302                 :             :  * Try to enter parallel mode and create a parallel context.  Then initialize
     303                 :             :  * shared memory state.
     304                 :             :  *
     305                 :             :  * On success, return parallel vacuum state.  Otherwise return NULL.
     306                 :             :  */
     307                 :             : ParallelVacuumState *
     308                 :       42985 : parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
     309                 :             :                      int nrequested_workers, int vac_work_mem,
     310                 :             :                      int elevel, BufferAccessStrategy bstrategy)
     311                 :             : {
     312                 :             :     ParallelVacuumState *pvs;
     313                 :             :     ParallelContext *pcxt;
     314                 :             :     PVShared   *shared;
     315                 :             :     TidStore   *dead_items;
     316                 :             :     PVIndStats *indstats;
     317                 :             :     BufferUsage *buffer_usage;
     318                 :             :     WalUsage   *wal_usage;
     319                 :             :     bool       *will_parallel_vacuum;
     320                 :             :     Size        est_indstats_len;
     321                 :             :     Size        est_shared_len;
     322                 :       42985 :     int         nindexes_mwm = 0;
     323                 :       42985 :     int         parallel_workers = 0;
     324                 :             :     int         querylen;
     325                 :             : 
     326                 :             :     /*
     327                 :             :      * A parallel vacuum must be requested and there must be indexes on the
     328                 :             :      * relation
     329                 :             :      */
     330                 :             :     Assert(nrequested_workers >= 0);
     331                 :             :     Assert(nindexes > 0);
     332                 :             : 
     333                 :             :     /*
     334                 :             :      * Compute the number of parallel vacuum workers to launch
     335                 :             :      */
     336                 :       42985 :     will_parallel_vacuum = palloc0_array(bool, nindexes);
     337                 :       42985 :     parallel_workers = parallel_vacuum_compute_workers(indrels, nindexes,
     338                 :             :                                                        nrequested_workers,
     339                 :             :                                                        will_parallel_vacuum);
     340         [ +  + ]:       42985 :     if (parallel_workers <= 0)
     341                 :             :     {
     342                 :             :         /* Can't perform vacuum in parallel -- return NULL */
     343                 :       42958 :         pfree(will_parallel_vacuum);
     344                 :       42958 :         return NULL;
     345                 :             :     }
     346                 :             : 
     347                 :          27 :     pvs = palloc0_object(ParallelVacuumState);
     348                 :          27 :     pvs->indrels = indrels;
     349                 :          27 :     pvs->nindexes = nindexes;
     350                 :          27 :     pvs->will_parallel_vacuum = will_parallel_vacuum;
     351                 :          27 :     pvs->bstrategy = bstrategy;
     352                 :          27 :     pvs->heaprel = rel;
     353                 :             : 
     354                 :          27 :     EnterParallelMode();
     355                 :          27 :     pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
     356                 :             :                                  parallel_workers);
     357                 :             :     Assert(pcxt->nworkers > 0);
     358                 :          27 :     pvs->pcxt = pcxt;
     359                 :             : 
     360                 :             :     /* Estimate size for index vacuum stats -- PARALLEL_VACUUM_KEY_INDEX_STATS */
     361                 :          27 :     est_indstats_len = mul_size(sizeof(PVIndStats), nindexes);
     362                 :          27 :     shm_toc_estimate_chunk(&pcxt->estimator, est_indstats_len);
     363                 :          27 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     364                 :             : 
     365                 :             :     /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
     366                 :          27 :     est_shared_len = sizeof(PVShared);
     367                 :          27 :     shm_toc_estimate_chunk(&pcxt->estimator, est_shared_len);
     368                 :          27 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     369                 :             : 
     370                 :             :     /*
     371                 :             :      * Estimate space for BufferUsage and WalUsage --
     372                 :             :      * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
     373                 :             :      *
     374                 :             :      * If there are no extensions loaded that care, we could skip this.  We
     375                 :             :      * have no way of knowing whether anyone's looking at pgBufferUsage or
     376                 :             :      * pgWalUsage, so do it unconditionally.
     377                 :             :      */
     378                 :          27 :     shm_toc_estimate_chunk(&pcxt->estimator,
     379                 :             :                            mul_size(sizeof(BufferUsage), pcxt->nworkers));
     380                 :          27 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     381                 :          27 :     shm_toc_estimate_chunk(&pcxt->estimator,
     382                 :             :                            mul_size(sizeof(WalUsage), pcxt->nworkers));
     383                 :          27 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     384                 :             : 
     385                 :             :     /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
     386         [ +  + ]:          27 :     if (debug_query_string)
     387                 :             :     {
     388                 :          23 :         querylen = strlen(debug_query_string);
     389                 :          23 :         shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
     390                 :          23 :         shm_toc_estimate_keys(&pcxt->estimator, 1);
     391                 :             :     }
     392                 :             :     else
     393                 :           4 :         querylen = 0;           /* keep compiler quiet */
     394                 :             : 
     395                 :          27 :     InitializeParallelDSM(pcxt);
     396                 :             : 
     397                 :             :     /* Prepare index vacuum stats */
     398                 :          27 :     indstats = (PVIndStats *) shm_toc_allocate(pcxt->toc, est_indstats_len);
     399   [ +  -  +  -  :         603 :     MemSet(indstats, 0, est_indstats_len);
          +  -  +  -  +  
                      + ]
     400         [ +  + ]:         123 :     for (int i = 0; i < nindexes; i++)
     401                 :             :     {
     402                 :          96 :         Relation    indrel = indrels[i];
     403                 :          96 :         uint8       vacoptions = indrel->rd_indam->amparallelvacuumoptions;
     404                 :             : 
     405                 :             :         /*
     406                 :             :          * Cleanup option should be either disabled, always performing in
     407                 :             :          * parallel or conditionally performing in parallel.
     408                 :             :          */
     409                 :             :         Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
     410                 :             :                ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
     411                 :             :         Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
     412                 :             : 
     413         [ +  + ]:          96 :         if (!will_parallel_vacuum[i])
     414                 :           4 :             continue;
     415                 :             : 
     416         [ +  + ]:          92 :         if (indrel->rd_indam->amusemaintenanceworkmem)
     417                 :           8 :             nindexes_mwm++;
     418                 :             : 
     419                 :             :         /*
     420                 :             :          * Remember the number of indexes that support parallel operation for
     421                 :             :          * each phase.
     422                 :             :          */
     423         [ +  + ]:          92 :         if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
     424                 :          84 :             pvs->nindexes_parallel_bulkdel++;
     425         [ +  + ]:          92 :         if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
     426                 :          16 :             pvs->nindexes_parallel_cleanup++;
     427         [ +  + ]:          92 :         if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
     428                 :          68 :             pvs->nindexes_parallel_condcleanup++;
     429                 :             :     }
     430                 :          27 :     shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_INDEX_STATS, indstats);
     431                 :          27 :     pvs->indstats = indstats;
     432                 :             : 
     433                 :             :     /* Prepare shared information */
     434                 :          27 :     shared = (PVShared *) shm_toc_allocate(pcxt->toc, est_shared_len);
     435   [ +  -  +  -  :         459 :     MemSet(shared, 0, est_shared_len);
          +  -  +  -  +  
                      + ]
     436                 :          27 :     shared->relid = RelationGetRelid(rel);
     437                 :          27 :     shared->elevel = elevel;
     438                 :          27 :     shared->queryid = pgstat_get_my_query_id();
     439                 :          27 :     shared->maintenance_work_mem_worker =
     440                 :             :         (nindexes_mwm > 0) ?
     441         [ +  + ]:          27 :         vac_work_mem / Min(parallel_workers, nindexes_mwm) :
     442                 :             :         vac_work_mem;
     443                 :             : 
     444                 :          27 :     shared->dead_items_info.max_bytes = vac_work_mem * (size_t) 1024;
     445                 :             : 
     446                 :             :     /* Prepare DSA space for dead items */
     447                 :          27 :     dead_items = TidStoreCreateShared(shared->dead_items_info.max_bytes,
     448                 :             :                                       LWTRANCHE_PARALLEL_VACUUM_DSA);
     449                 :          27 :     pvs->dead_items = dead_items;
     450                 :          27 :     shared->dead_items_handle = TidStoreGetHandle(dead_items);
     451                 :          27 :     shared->dead_items_dsa_handle = dsa_get_handle(TidStoreGetDSA(dead_items));
     452                 :             : 
     453                 :             :     /* Use the same buffer size for all workers */
     454                 :          27 :     shared->ring_nbuffers = GetAccessStrategyBufferCount(bstrategy);
     455                 :             : 
     456                 :          27 :     pg_atomic_init_u32(&(shared->cost_balance), 0);
     457                 :          27 :     pg_atomic_init_u32(&(shared->active_nworkers), 0);
     458                 :          27 :     pg_atomic_init_u32(&(shared->idx), 0);
     459                 :             : 
     460                 :          27 :     shared->is_autovacuum = AmAutoVacuumWorkerProcess();
     461                 :             : 
     462                 :             :     /*
     463                 :             :      * Initialize shared cost-based vacuum delay parameters if it's for
     464                 :             :      * autovacuum and the parallel context has workers. Note that the parallel
     465                 :             :      * context falls back to the leader's private memory with no workers and
     466                 :             :      * no segment when the maximum number of DSM segments has been reached.
     467                 :             :      * There are then no workers to propagate the parameters to, and no
     468                 :             :      * segment to register the detach callback on.
     469                 :             :      */
     470   [ +  +  +  - ]:          27 :     if (shared->is_autovacuum && pcxt->nworkers > 0)
     471                 :             :     {
     472                 :           4 :         parallel_vacuum_set_cost_parameters(&shared->cost_params);
     473                 :           4 :         pg_atomic_init_u32(&shared->cost_params.generation, 1);
     474                 :           4 :         SpinLockInit(&shared->cost_params.mutex);
     475                 :             : 
     476                 :           4 :         pv_shared_cost_params = &(shared->cost_params);
     477                 :           4 :         on_dsm_detach(pcxt->seg, parallel_vacuum_dsm_detach, (Datum) 0);
     478                 :             :     }
     479                 :             : 
     480                 :          27 :     shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_SHARED, shared);
     481                 :          27 :     pvs->shared = shared;
     482                 :             : 
     483                 :             :     /*
     484                 :             :      * Allocate space for each worker's BufferUsage and WalUsage; no need to
     485                 :             :      * initialize
     486                 :             :      */
     487                 :          27 :     buffer_usage = shm_toc_allocate(pcxt->toc,
     488                 :          27 :                                     mul_size(sizeof(BufferUsage), pcxt->nworkers));
     489                 :          27 :     shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, buffer_usage);
     490                 :          27 :     pvs->buffer_usage = buffer_usage;
     491                 :          27 :     wal_usage = shm_toc_allocate(pcxt->toc,
     492                 :          27 :                                  mul_size(sizeof(WalUsage), pcxt->nworkers));
     493                 :          27 :     shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_WAL_USAGE, wal_usage);
     494                 :          27 :     pvs->wal_usage = wal_usage;
     495                 :             : 
     496                 :             :     /* Store query string for workers */
     497         [ +  + ]:          27 :     if (debug_query_string)
     498                 :             :     {
     499                 :             :         char       *sharedquery;
     500                 :             : 
     501                 :          23 :         sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
     502                 :          23 :         memcpy(sharedquery, debug_query_string, querylen + 1);
     503                 :          23 :         sharedquery[querylen] = '\0';
     504                 :          23 :         shm_toc_insert(pcxt->toc,
     505                 :             :                        PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
     506                 :             :     }
     507                 :             : 
     508                 :             :     /* Success -- return parallel vacuum state */
     509                 :          27 :     return pvs;
     510                 :             : }
     511                 :             : 
     512                 :             : /*
     513                 :             :  * Destroy the parallel context, and end parallel mode.
     514                 :             :  *
     515                 :             :  * Since writes are not allowed during parallel mode, copy the
     516                 :             :  * updated index statistics from DSM into local memory and then later use that
     517                 :             :  * to update the index statistics.  One might think that we can exit from
     518                 :             :  * parallel mode, update the index statistics and then destroy parallel
     519                 :             :  * context, but that won't be safe (see ExitParallelMode).
     520                 :             :  */
     521                 :             : void
     522                 :          26 : parallel_vacuum_end(ParallelVacuumState *pvs, IndexBulkDeleteResult **istats)
     523                 :             : {
     524                 :             :     Assert(!IsParallelWorker());
     525                 :             : 
     526                 :             :     /* Copy the updated statistics */
     527         [ +  + ]:         120 :     for (int i = 0; i < pvs->nindexes; i++)
     528                 :             :     {
     529                 :          94 :         PVIndStats *indstats = &(pvs->indstats[i]);
     530                 :             : 
     531         [ +  + ]:          94 :         if (indstats->istat_updated)
     532                 :             :         {
     533                 :          66 :             istats[i] = palloc0_object(IndexBulkDeleteResult);
     534                 :          66 :             memcpy(istats[i], &indstats->istat, sizeof(IndexBulkDeleteResult));
     535                 :             :         }
     536                 :             :         else
     537                 :          28 :             istats[i] = NULL;
     538                 :             :     }
     539                 :             : 
     540                 :          26 :     TidStoreDestroy(pvs->dead_items);
     541                 :             : 
     542                 :          26 :     DestroyParallelContext(pvs->pcxt);
     543                 :          26 :     ExitParallelMode();
     544                 :             : 
     545         [ +  + ]:          26 :     if (AmAutoVacuumWorkerProcess())
     546                 :           3 :         pv_shared_cost_params = NULL;
     547                 :             : 
     548                 :          26 :     pfree(pvs->will_parallel_vacuum);
     549                 :          26 :     pfree(pvs);
     550                 :          26 : }
     551                 :             : 
     552                 :             : /*
     553                 :             :  * DSM detach callback. This is invoked when an autovacuum worker detaches
     554                 :             :  * from the DSM segment holding PVShared. It ensures to reset the local pointer
     555                 :             :  * to the shared state even if parallel vacuum raises an error and doesn't
     556                 :             :  * call parallel_vacuum_end().
     557                 :             :  */
     558                 :             : static void
     559                 :           4 : parallel_vacuum_dsm_detach(dsm_segment *seg, Datum arg)
     560                 :             : {
     561                 :             :     Assert(AmAutoVacuumWorkerProcess());
     562                 :           4 :     pv_shared_cost_params = NULL;
     563                 :           4 : }
     564                 :             : 
     565                 :             : /*
     566                 :             :  * Returns the dead items space and dead items information.
     567                 :             :  */
     568                 :             : TidStore *
     569                 :          47 : parallel_vacuum_get_dead_items(ParallelVacuumState *pvs, VacDeadItemsInfo **dead_items_info_p)
     570                 :             : {
     571                 :          47 :     *dead_items_info_p = &(pvs->shared->dead_items_info);
     572                 :          47 :     return pvs->dead_items;
     573                 :             : }
     574                 :             : 
     575                 :             : /* Forget all items in dead_items */
     576                 :             : void
     577                 :          20 : parallel_vacuum_reset_dead_items(ParallelVacuumState *pvs)
     578                 :             : {
     579                 :          20 :     VacDeadItemsInfo *dead_items_info = &(pvs->shared->dead_items_info);
     580                 :             : 
     581                 :             :     /*
     582                 :             :      * Free the current tidstore and return allocated DSA segments to the
     583                 :             :      * operating system. Then we recreate the tidstore with the same max_bytes
     584                 :             :      * limitation we just used.
     585                 :             :      */
     586                 :          20 :     TidStoreDestroy(pvs->dead_items);
     587                 :          20 :     pvs->dead_items = TidStoreCreateShared(dead_items_info->max_bytes,
     588                 :             :                                            LWTRANCHE_PARALLEL_VACUUM_DSA);
     589                 :             : 
     590                 :             :     /* Update the DSA pointer for dead_items to the new one */
     591                 :          20 :     pvs->shared->dead_items_dsa_handle = dsa_get_handle(TidStoreGetDSA(pvs->dead_items));
     592                 :          20 :     pvs->shared->dead_items_handle = TidStoreGetHandle(pvs->dead_items);
     593                 :             : 
     594                 :             :     /* Reset the counter */
     595                 :          20 :     dead_items_info->num_items = 0;
     596                 :          20 : }
     597                 :             : 
     598                 :             : /*
     599                 :             :  * Do parallel index bulk-deletion with parallel workers.
     600                 :             :  */
     601                 :             : void
     602                 :          21 : parallel_vacuum_bulkdel_all_indexes(ParallelVacuumState *pvs, long num_table_tuples,
     603                 :             :                                     int num_index_scans, PVWorkerStats *wstats)
     604                 :             : {
     605                 :             :     Assert(!IsParallelWorker());
     606                 :             : 
     607                 :             :     /*
     608                 :             :      * We can only provide an approximate value of num_heap_tuples, at least
     609                 :             :      * for now.
     610                 :             :      */
     611                 :          21 :     pvs->shared->reltuples = num_table_tuples;
     612                 :          21 :     pvs->shared->estimated_count = true;
     613                 :             : 
     614                 :          21 :     parallel_vacuum_process_all_indexes(pvs, num_index_scans, true, wstats);
     615                 :          20 : }
     616                 :             : 
     617                 :             : /*
     618                 :             :  * Do parallel index cleanup with parallel workers.
     619                 :             :  */
     620                 :             : void
     621                 :          26 : parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs, long num_table_tuples,
     622                 :             :                                     int num_index_scans, bool estimated_count,
     623                 :             :                                     PVWorkerStats *wstats)
     624                 :             : {
     625                 :             :     Assert(!IsParallelWorker());
     626                 :             : 
     627                 :             :     /*
     628                 :             :      * We can provide a better estimate of total number of surviving tuples
     629                 :             :      * (we assume indexes are more interested in that than in the number of
     630                 :             :      * nominally live tuples).
     631                 :             :      */
     632                 :          26 :     pvs->shared->reltuples = num_table_tuples;
     633                 :          26 :     pvs->shared->estimated_count = estimated_count;
     634                 :             : 
     635                 :          26 :     parallel_vacuum_process_all_indexes(pvs, num_index_scans, false, wstats);
     636                 :          26 : }
     637                 :             : 
     638                 :             : /*
     639                 :             :  * Fill in the given structure with cost-based vacuum delay parameter values.
     640                 :             :  */
     641                 :             : static inline void
     642                 :           6 : parallel_vacuum_set_cost_parameters(PVSharedCostParams *params)
     643                 :             : {
     644                 :           6 :     params->cost_delay = vacuum_cost_delay;
     645                 :           6 :     params->cost_limit = vacuum_cost_limit;
     646                 :           6 :     params->cost_page_dirty = VacuumCostPageDirty;
     647                 :           6 :     params->cost_page_hit = VacuumCostPageHit;
     648                 :           6 :     params->cost_page_miss = VacuumCostPageMiss;
     649                 :           6 :     params->track_cost_delay_timing = track_cost_delay_timing;
     650                 :           6 : }
     651                 :             : 
     652                 :             : /*
     653                 :             :  * Updates the cost-based vacuum delay parameters for parallel autovacuum
     654                 :             :  * workers.
     655                 :             :  *
     656                 :             :  * For non-autovacuum parallel workers, this function will have no effect.
     657                 :             :  */
     658                 :             : void
     659                 :         363 : parallel_vacuum_update_shared_delay_params(void)
     660                 :             : {
     661                 :             :     uint32      params_generation;
     662                 :             : 
     663                 :             :     Assert(IsParallelWorker());
     664                 :             : 
     665                 :             :     /* Quick return if the worker is not running for the autovacuum */
     666         [ -  + ]:         363 :     if (pv_shared_cost_params == NULL)
     667                 :           0 :         return;
     668                 :             : 
     669                 :         363 :     params_generation = pg_atomic_read_u32(&pv_shared_cost_params->generation);
     670                 :             :     Assert(shared_params_generation_local <= params_generation);
     671                 :             : 
     672                 :             :     /* Return if parameters had not changed in the leader */
     673         [ +  + ]:         363 :     if (params_generation == shared_params_generation_local)
     674                 :         359 :         return;
     675                 :             : 
     676                 :           4 :     SpinLockAcquire(&pv_shared_cost_params->mutex);
     677                 :           4 :     VacuumCostDelay = pv_shared_cost_params->cost_delay;
     678                 :           4 :     VacuumCostLimit = pv_shared_cost_params->cost_limit;
     679                 :           4 :     VacuumCostPageDirty = pv_shared_cost_params->cost_page_dirty;
     680                 :           4 :     VacuumCostPageHit = pv_shared_cost_params->cost_page_hit;
     681                 :           4 :     VacuumCostPageMiss = pv_shared_cost_params->cost_page_miss;
     682                 :           4 :     track_cost_delay_timing = pv_shared_cost_params->track_cost_delay_timing;
     683                 :           4 :     SpinLockRelease(&pv_shared_cost_params->mutex);
     684                 :             : 
     685                 :           4 :     VacuumUpdateCosts();
     686                 :             : 
     687                 :           4 :     shared_params_generation_local = params_generation;
     688                 :             : 
     689   [ +  -  +  + ]:           4 :     elog(DEBUG2,
     690                 :             :          "parallel autovacuum worker updated cost params: cost_limit=%d, cost_delay=%g, cost_page_miss=%d, cost_page_dirty=%d, cost_page_hit=%d, track_cost_delay_timing=%s",
     691                 :             :          vacuum_cost_limit,
     692                 :             :          vacuum_cost_delay,
     693                 :             :          VacuumCostPageMiss,
     694                 :             :          VacuumCostPageDirty,
     695                 :             :          VacuumCostPageHit,
     696                 :             :          track_cost_delay_timing ? "yes" : "no");
     697                 :             : }
     698                 :             : 
     699                 :             : /*
     700                 :             :  * Store the cost-based vacuum delay parameters in the shared memory so that
     701                 :             :  * parallel vacuum workers can consume them (see
     702                 :             :  * parallel_vacuum_update_shared_delay_params()).
     703                 :             :  */
     704                 :             : void
     705                 :        3737 : parallel_vacuum_propagate_shared_delay_params(void)
     706                 :             : {
     707                 :             :     Assert(AmAutoVacuumWorkerProcess());
     708                 :             : 
     709                 :             :     /*
     710                 :             :      * Quick return if the leader process is not sharing the delay parameters.
     711                 :             :      */
     712         [ +  + ]:        3737 :     if (pv_shared_cost_params == NULL)
     713                 :        3687 :         return;
     714                 :             : 
     715                 :             :     /*
     716                 :             :      * Check if any delay parameters have changed. We can read them without
     717                 :             :      * locks as only the leader can modify them.
     718                 :             :      */
     719         [ +  + ]:          50 :     if (vacuum_cost_delay == pv_shared_cost_params->cost_delay &&
     720         [ +  + ]:          49 :         vacuum_cost_limit == pv_shared_cost_params->cost_limit &&
     721         [ +  - ]:          48 :         VacuumCostPageDirty == pv_shared_cost_params->cost_page_dirty &&
     722         [ +  - ]:          48 :         VacuumCostPageHit == pv_shared_cost_params->cost_page_hit &&
     723         [ +  - ]:          48 :         VacuumCostPageMiss == pv_shared_cost_params->cost_page_miss &&
     724         [ +  - ]:          48 :         track_cost_delay_timing == pv_shared_cost_params->track_cost_delay_timing)
     725                 :          48 :         return;
     726                 :             : 
     727                 :             :     /* Update the shared delay parameters */
     728                 :           2 :     SpinLockAcquire(&pv_shared_cost_params->mutex);
     729                 :           2 :     parallel_vacuum_set_cost_parameters(pv_shared_cost_params);
     730                 :           2 :     SpinLockRelease(&pv_shared_cost_params->mutex);
     731                 :             : 
     732                 :             :     /*
     733                 :             :      * Increment the generation of the parameters, i.e. let parallel workers
     734                 :             :      * know that they should re-read shared cost params.
     735                 :             :      */
     736                 :           2 :     pg_atomic_fetch_add_u32(&pv_shared_cost_params->generation, 1);
     737                 :             : }
     738                 :             : 
     739                 :             : /*
     740                 :             :  * Reload the configuration file if requested, and refresh the cost-based
     741                 :             :  * delay parameters of an autovacuum worker running a parallel vacuum
     742                 :             :  * (leader), propagating any change to its parallel workers.
     743                 :             :  */
     744                 :             : void
     745                 :          16 : parallel_vacuum_refresh_cost_params(void)
     746                 :             : {
     747                 :             :     Assert(AmAutoVacuumWorkerProcess());
     748                 :             : 
     749                 :             :     /*
     750                 :             :      * Quick return if the leader is not sharing the delay parameters with
     751                 :             :      * parallel workers.
     752                 :             :      */
     753         [ -  + ]:          16 :     if (pv_shared_cost_params == NULL)
     754                 :           0 :         return;
     755                 :             : 
     756         [ -  + ]:          16 :     if (ConfigReloadPending)
     757                 :             :     {
     758                 :           0 :         ConfigReloadPending = false;
     759                 :           0 :         ProcessConfigFile(PGC_SIGHUP);
     760                 :             : 
     761                 :             :         /* This re-divides the cost limit too */
     762                 :           0 :         VacuumUpdateCosts();
     763                 :             :     }
     764                 :             :     else
     765                 :             :     {
     766                 :             :         /* The number of workers sharing the cost limit may have changed */
     767                 :          16 :         AutoVacuumUpdateCostLimit();
     768                 :             :     }
     769                 :             : 
     770                 :          16 :     parallel_vacuum_propagate_shared_delay_params();
     771                 :             : }
     772                 :             : 
     773                 :             : /*
     774                 :             :  * Compute the number of parallel worker processes to request.  Both index
     775                 :             :  * vacuum and index cleanup can be executed with parallel workers.
     776                 :             :  * The index is eligible for parallel vacuum iff its size is greater than
     777                 :             :  * min_parallel_index_scan_size as invoking workers for very small indexes
     778                 :             :  * can hurt performance.
     779                 :             :  *
     780                 :             :  * nrequested is the number of parallel workers that user requested.  If
     781                 :             :  * nrequested is 0, we compute the parallel degree based on nindexes, that is
     782                 :             :  * the number of indexes that support parallel vacuum.  This function also
     783                 :             :  * sets will_parallel_vacuum to remember indexes that participate in parallel
     784                 :             :  * vacuum.
     785                 :             :  */
     786                 :             : static int
     787                 :       42985 : parallel_vacuum_compute_workers(Relation *indrels, int nindexes, int nrequested,
     788                 :             :                                 bool *will_parallel_vacuum)
     789                 :             : {
     790                 :       42985 :     int         nindexes_parallel = 0;
     791                 :       42985 :     int         nindexes_parallel_bulkdel = 0;
     792                 :       42985 :     int         nindexes_parallel_cleanup = 0;
     793                 :             :     int         parallel_workers;
     794                 :             :     int         max_workers;
     795                 :             : 
     796                 :       85970 :     max_workers = AmAutoVacuumWorkerProcess() ?
     797         [ +  + ]:       42985 :         autovacuum_max_parallel_workers :
     798                 :             :         max_parallel_maintenance_workers;
     799                 :             : 
     800                 :             :     /*
     801                 :             :      * We don't allow performing parallel operation in standalone backend or
     802                 :             :      * when parallelism is disabled.
     803                 :             :      */
     804   [ +  +  +  + ]:       42985 :     if (!IsUnderPostmaster || max_workers == 0)
     805                 :       39530 :         return 0;
     806                 :             : 
     807                 :             :     /*
     808                 :             :      * Compute the number of indexes that can participate in parallel vacuum.
     809                 :             :      */
     810         [ +  + ]:       11276 :     for (int i = 0; i < nindexes; i++)
     811                 :             :     {
     812                 :        7821 :         Relation    indrel = indrels[i];
     813                 :        7821 :         uint8       vacoptions = indrel->rd_indam->amparallelvacuumoptions;
     814                 :             : 
     815                 :             :         /* Skip index that is not a suitable target for parallel index vacuum */
     816         [ +  - ]:        7821 :         if (vacoptions == VACUUM_OPTION_NO_PARALLEL ||
     817         [ +  + ]:        7821 :             RelationGetNumberOfBlocks(indrel) < min_parallel_index_scan_size)
     818                 :        7717 :             continue;
     819                 :             : 
     820                 :         104 :         will_parallel_vacuum[i] = true;
     821                 :             : 
     822         [ +  + ]:         104 :         if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
     823                 :          96 :             nindexes_parallel_bulkdel++;
     824         [ +  + ]:         104 :         if (((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0) ||
     825         [ +  + ]:          88 :             ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0))
     826                 :          96 :             nindexes_parallel_cleanup++;
     827                 :             :     }
     828                 :             : 
     829                 :        3455 :     nindexes_parallel = Max(nindexes_parallel_bulkdel,
     830                 :             :                             nindexes_parallel_cleanup);
     831                 :             : 
     832                 :             :     /* The leader process takes one index */
     833                 :        3455 :     nindexes_parallel--;
     834                 :             : 
     835                 :             :     /* No index supports parallel vacuum */
     836         [ +  + ]:        3455 :     if (nindexes_parallel <= 0)
     837                 :        3428 :         return 0;
     838                 :             : 
     839                 :             :     /* Compute the parallel degree */
     840                 :          27 :     parallel_workers = (nrequested > 0) ?
     841         [ +  + ]:          27 :         Min(nrequested, nindexes_parallel) : nindexes_parallel;
     842                 :             : 
     843                 :             :     /* Cap by GUC variable */
     844                 :          27 :     parallel_workers = Min(parallel_workers, max_workers);
     845                 :             : 
     846                 :          27 :     return parallel_workers;
     847                 :             : }
     848                 :             : 
     849                 :             : /*
     850                 :             :  * Perform index vacuum or index cleanup with parallel workers.  This function
     851                 :             :  * must be used by the parallel vacuum leader process.
     852                 :             :  *
     853                 :             :  * If wstats is not NULL, the parallel worker statistics are updated.
     854                 :             :  */
     855                 :             : static void
     856                 :          47 : parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scans,
     857                 :             :                                     bool vacuum, PVWorkerStats *wstats)
     858                 :             : {
     859                 :             :     int         nworkers;
     860                 :             :     PVIndVacStatus new_status;
     861                 :             : 
     862                 :             :     Assert(!IsParallelWorker());
     863                 :             : 
     864         [ +  + ]:          47 :     if (vacuum)
     865                 :             :     {
     866                 :          21 :         new_status = PARALLEL_INDVAC_STATUS_NEED_BULKDELETE;
     867                 :             : 
     868                 :             :         /* Determine the number of parallel workers to launch */
     869                 :          21 :         nworkers = pvs->nindexes_parallel_bulkdel;
     870                 :             :     }
     871                 :             :     else
     872                 :             :     {
     873                 :          26 :         new_status = PARALLEL_INDVAC_STATUS_NEED_CLEANUP;
     874                 :             : 
     875                 :             :         /* Determine the number of parallel workers to launch */
     876                 :          26 :         nworkers = pvs->nindexes_parallel_cleanup;
     877                 :             : 
     878                 :             :         /* Add conditionally parallel-aware indexes if in the first time call */
     879         [ +  + ]:          26 :         if (num_index_scans == 0)
     880                 :          14 :             nworkers += pvs->nindexes_parallel_condcleanup;
     881                 :             :     }
     882                 :             : 
     883                 :             :     /* The leader process will participate */
     884                 :          47 :     nworkers--;
     885                 :             : 
     886                 :             :     /*
     887                 :             :      * It is possible that parallel context is initialized with fewer workers
     888                 :             :      * than the number of indexes that need a separate worker in the current
     889                 :             :      * phase, so we need to consider it.  See
     890                 :             :      * parallel_vacuum_compute_workers().
     891                 :             :      */
     892                 :          47 :     nworkers = Min(nworkers, pvs->pcxt->nworkers);
     893                 :             : 
     894                 :             :     /* Update the statistics, if we asked to */
     895   [ +  -  +  + ]:          47 :     if (wstats != NULL && nworkers > 0)
     896                 :          36 :         wstats->nplanned += nworkers;
     897                 :             : 
     898                 :             :     /*
     899                 :             :      * Set index vacuum status and mark whether parallel vacuum worker can
     900                 :             :      * process it.
     901                 :             :      */
     902         [ +  + ]:         197 :     for (int i = 0; i < pvs->nindexes; i++)
     903                 :             :     {
     904                 :         150 :         PVIndStats *indstats = &(pvs->indstats[i]);
     905                 :             : 
     906                 :             :         Assert(indstats->status == PARALLEL_INDVAC_STATUS_INITIAL);
     907                 :         150 :         indstats->status = new_status;
     908                 :         150 :         indstats->parallel_workers_can_process =
     909   [ +  +  +  + ]:         292 :             (pvs->will_parallel_vacuum[i] &&
     910                 :         142 :              parallel_vacuum_index_is_parallel_safe(pvs->indrels[i],
     911                 :             :                                                     num_index_scans,
     912                 :         150 :                                                     vacuum));
     913                 :             :     }
     914                 :             : 
     915                 :             :     /* Reset the parallel index processing and progress counters */
     916                 :          47 :     pg_atomic_write_u32(&(pvs->shared->idx), 0);
     917                 :             : 
     918                 :             :     /* Setup the shared cost-based vacuum delay and launch workers */
     919         [ +  + ]:          47 :     if (nworkers > 0)
     920                 :             :     {
     921                 :             :         /* Reinitialize parallel context to relaunch parallel workers */
     922         [ +  + ]:          36 :         if (num_index_scans > 0)
     923                 :           9 :             ReinitializeParallelDSM(pvs->pcxt);
     924                 :             : 
     925                 :             :         /*
     926                 :             :          * Set up shared cost balance and the number of active workers for
     927                 :             :          * vacuum delay.  We need to do this before launching workers as
     928                 :             :          * otherwise, they might not see the updated values for these
     929                 :             :          * parameters.
     930                 :             :          */
     931                 :          36 :         pg_atomic_write_u32(&(pvs->shared->cost_balance), VacuumCostBalance);
     932                 :          36 :         pg_atomic_write_u32(&(pvs->shared->active_nworkers), 0);
     933                 :             : 
     934                 :             :         /*
     935                 :             :          * The number of workers can vary between bulkdelete and cleanup
     936                 :             :          * phase.
     937                 :             :          */
     938                 :          36 :         ReinitializeParallelWorkers(pvs->pcxt, nworkers);
     939                 :             : 
     940                 :          36 :         LaunchParallelWorkers(pvs->pcxt);
     941                 :             : 
     942         [ +  - ]:          36 :         if (pvs->pcxt->nworkers_launched > 0)
     943                 :             :         {
     944                 :             :             /*
     945                 :             :              * Reset the local cost values for leader backend as we have
     946                 :             :              * already accumulated the remaining balance of heap.
     947                 :             :              */
     948                 :          36 :             VacuumCostBalance = 0;
     949                 :          36 :             VacuumCostBalanceLocal = 0;
     950                 :             : 
     951                 :             :             /* Enable shared cost balance for leader backend */
     952                 :          36 :             VacuumSharedCostBalance = &(pvs->shared->cost_balance);
     953                 :          36 :             VacuumActiveNWorkers = &(pvs->shared->active_nworkers);
     954                 :             : 
     955                 :             :             /* Update the statistics, if we asked to */
     956         [ +  - ]:          36 :             if (wstats != NULL)
     957                 :          36 :                 wstats->nlaunched += pvs->pcxt->nworkers_launched;
     958                 :             :         }
     959                 :             : 
     960         [ +  + ]:          36 :         if (vacuum)
     961         [ +  + ]:          21 :             ereport(pvs->shared->elevel,
     962                 :             :                     (errmsg(ngettext("launched %d parallel vacuum worker for index vacuuming (planned: %d)",
     963                 :             :                                      "launched %d parallel vacuum workers for index vacuuming (planned: %d)",
     964                 :             :                                      pvs->pcxt->nworkers_launched),
     965                 :             :                             pvs->pcxt->nworkers_launched, nworkers)));
     966                 :             :         else
     967         [ -  + ]:          15 :             ereport(pvs->shared->elevel,
     968                 :             :                     (errmsg(ngettext("launched %d parallel vacuum worker for index cleanup (planned: %d)",
     969                 :             :                                      "launched %d parallel vacuum workers for index cleanup (planned: %d)",
     970                 :             :                                      pvs->pcxt->nworkers_launched),
     971                 :             :                             pvs->pcxt->nworkers_launched, nworkers)));
     972                 :             :     }
     973                 :             : 
     974                 :             :     /* Vacuum the indexes that can be processed by only leader process */
     975                 :          47 :     parallel_vacuum_process_unsafe_indexes(pvs);
     976                 :             : 
     977                 :             :     /*
     978                 :             :      * Join as a parallel worker.  The leader vacuums alone processes all
     979                 :             :      * parallel-safe indexes in the case where no workers are launched.
     980                 :             :      */
     981                 :          47 :     parallel_vacuum_process_safe_indexes(pvs);
     982                 :             : 
     983                 :             :     /*
     984                 :             :      * Next, accumulate buffer and WAL usage.  (This must wait for the workers
     985                 :             :      * to finish, or we might get incomplete data.)
     986                 :             :      */
     987         [ +  + ]:          47 :     if (nworkers > 0)
     988                 :             :     {
     989                 :             :         /* Wait for all vacuum workers to finish */
     990                 :          36 :         WaitForParallelWorkersToFinish(pvs->pcxt);
     991                 :             : 
     992         [ +  + ]:          79 :         for (int i = 0; i < pvs->pcxt->nworkers_launched; i++)
     993                 :          44 :             InstrAccumParallelQuery(&pvs->buffer_usage[i], &pvs->wal_usage[i]);
     994                 :             :     }
     995                 :             : 
     996                 :             :     /*
     997                 :             :      * Reset all index status back to initial (while checking that we have
     998                 :             :      * vacuumed all indexes).
     999                 :             :      */
    1000         [ +  + ]:         194 :     for (int i = 0; i < pvs->nindexes; i++)
    1001                 :             :     {
    1002                 :         148 :         PVIndStats *indstats = &(pvs->indstats[i]);
    1003                 :             : 
    1004         [ -  + ]:         148 :         if (indstats->status != PARALLEL_INDVAC_STATUS_COMPLETED)
    1005         [ #  # ]:           0 :             elog(ERROR, "parallel index vacuum on index \"%s\" is not completed",
    1006                 :             :                  RelationGetRelationName(pvs->indrels[i]));
    1007                 :             : 
    1008                 :         148 :         indstats->status = PARALLEL_INDVAC_STATUS_INITIAL;
    1009                 :             :     }
    1010                 :             : 
    1011                 :             :     /*
    1012                 :             :      * Carry the shared balance value to heap scan and disable shared costing
    1013                 :             :      */
    1014         [ +  + ]:          46 :     if (VacuumSharedCostBalance)
    1015                 :             :     {
    1016                 :          35 :         VacuumCostBalance = pg_atomic_read_u32(VacuumSharedCostBalance);
    1017                 :          35 :         VacuumSharedCostBalance = NULL;
    1018                 :          35 :         VacuumActiveNWorkers = NULL;
    1019                 :             :     }
    1020                 :          46 : }
    1021                 :             : 
    1022                 :             : /*
    1023                 :             :  * Index vacuum/cleanup routine used by the leader process and parallel
    1024                 :             :  * vacuum worker processes to vacuum the indexes in parallel.
    1025                 :             :  */
    1026                 :             : static void
    1027                 :          91 : parallel_vacuum_process_safe_indexes(ParallelVacuumState *pvs)
    1028                 :             : {
    1029                 :             :     /*
    1030                 :             :      * Increment the active worker count if we are able to launch any worker.
    1031                 :             :      */
    1032         [ +  + ]:          91 :     if (VacuumActiveNWorkers)
    1033                 :          80 :         pg_atomic_add_fetch_u32(VacuumActiveNWorkers, 1);
    1034                 :             : 
    1035                 :             :     /* Loop until all indexes are vacuumed */
    1036                 :             :     for (;;)
    1037                 :         150 :     {
    1038                 :             :         int         idx;
    1039                 :             :         PVIndStats *indstats;
    1040                 :             : 
    1041                 :             :         /* Get an index number to process */
    1042                 :         241 :         idx = pg_atomic_fetch_add_u32(&(pvs->shared->idx), 1);
    1043                 :             : 
    1044                 :             :         /* Done for all indexes? */
    1045         [ +  + ]:         241 :         if (idx >= pvs->nindexes)
    1046                 :          91 :             break;
    1047                 :             : 
    1048                 :         150 :         indstats = &(pvs->indstats[idx]);
    1049                 :             : 
    1050                 :             :         /*
    1051                 :             :          * Skip vacuuming index that is unsafe for workers or has an
    1052                 :             :          * unsuitable target for parallel index vacuum (this is vacuumed in
    1053                 :             :          * parallel_vacuum_process_unsafe_indexes() by the leader).
    1054                 :             :          */
    1055         [ +  + ]:         150 :         if (!indstats->parallel_workers_can_process)
    1056                 :          48 :             continue;
    1057                 :             : 
    1058                 :             :         /* Do vacuum or cleanup of the index */
    1059                 :         102 :         parallel_vacuum_process_one_index(pvs, pvs->indrels[idx], indstats);
    1060                 :             :     }
    1061                 :             : 
    1062                 :             :     /*
    1063                 :             :      * We have completed the index vacuum so decrement the active worker
    1064                 :             :      * count.
    1065                 :             :      */
    1066         [ +  + ]:          91 :     if (VacuumActiveNWorkers)
    1067                 :          80 :         pg_atomic_sub_fetch_u32(VacuumActiveNWorkers, 1);
    1068                 :          91 : }
    1069                 :             : 
    1070                 :             : /*
    1071                 :             :  * Perform parallel vacuuming of indexes in leader process.
    1072                 :             :  *
    1073                 :             :  * Handles index vacuuming (or index cleanup) for indexes that are not
    1074                 :             :  * parallel safe.  It's possible that this will vary for a given index, based
    1075                 :             :  * on details like whether we're performing index cleanup right now.
    1076                 :             :  *
    1077                 :             :  * Also performs vacuuming of smaller indexes that fell under the size cutoff
    1078                 :             :  * enforced by parallel_vacuum_compute_workers().
    1079                 :             :  */
    1080                 :             : static void
    1081                 :          47 : parallel_vacuum_process_unsafe_indexes(ParallelVacuumState *pvs)
    1082                 :             : {
    1083                 :             :     Assert(!IsParallelWorker());
    1084                 :             : 
    1085                 :             :     /*
    1086                 :             :      * Increment the active worker count if we are able to launch any worker.
    1087                 :             :      */
    1088         [ +  + ]:          47 :     if (VacuumActiveNWorkers)
    1089                 :          36 :         pg_atomic_add_fetch_u32(VacuumActiveNWorkers, 1);
    1090                 :             : 
    1091         [ +  + ]:         197 :     for (int i = 0; i < pvs->nindexes; i++)
    1092                 :             :     {
    1093                 :         150 :         PVIndStats *indstats = &(pvs->indstats[i]);
    1094                 :             : 
    1095                 :             :         /* Skip, indexes that are safe for workers */
    1096         [ +  + ]:         150 :         if (indstats->parallel_workers_can_process)
    1097                 :         102 :             continue;
    1098                 :             : 
    1099                 :             :         /* Do vacuum or cleanup of the index */
    1100                 :          48 :         parallel_vacuum_process_one_index(pvs, pvs->indrels[i], indstats);
    1101                 :             :     }
    1102                 :             : 
    1103                 :             :     /*
    1104                 :             :      * We have completed the index vacuum so decrement the active worker
    1105                 :             :      * count.
    1106                 :             :      */
    1107         [ +  + ]:          47 :     if (VacuumActiveNWorkers)
    1108                 :          36 :         pg_atomic_sub_fetch_u32(VacuumActiveNWorkers, 1);
    1109                 :          47 : }
    1110                 :             : 
    1111                 :             : /*
    1112                 :             :  * Vacuum or cleanup index either by leader process or by one of the worker
    1113                 :             :  * process.  After vacuuming the index this function copies the index
    1114                 :             :  * statistics returned from ambulkdelete and amvacuumcleanup to the DSM
    1115                 :             :  * segment.
    1116                 :             :  */
    1117                 :             : static void
    1118                 :         150 : parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel,
    1119                 :             :                                   PVIndStats *indstats)
    1120                 :             : {
    1121                 :         150 :     IndexBulkDeleteResult *istat = NULL;
    1122                 :             :     IndexBulkDeleteResult *istat_res;
    1123                 :             :     IndexVacuumInfo ivinfo;
    1124                 :         150 :     const int   progress_index[] = {
    1125                 :             :         PROGRESS_VACUUM_PHASE,
    1126                 :             :         PROGRESS_VACUUM_CURRENT_INDEX_RELID
    1127                 :             :     };
    1128                 :             :     int64       progress_val[2];
    1129                 :         150 :     const int   reset_index[] = {
    1130                 :             :         PROGRESS_VACUUM_CURRENT_INDEX_RELID,
    1131                 :             :         PROGRESS_SCAN_BLOCKS_TOTAL,
    1132                 :             :         PROGRESS_SCAN_BLOCKS_DONE
    1133                 :             :     };
    1134                 :         150 :     const int64 reset_val[] = {(int64) InvalidOid, 0, 0};
    1135                 :             : 
    1136                 :             :     /*
    1137                 :             :      * Update the pointer to the corresponding bulk-deletion result if someone
    1138                 :             :      * has already updated it
    1139                 :             :      */
    1140         [ +  + ]:         150 :     if (indstats->istat_updated)
    1141                 :          54 :         istat = &(indstats->istat);
    1142                 :             : 
    1143                 :         150 :     ivinfo.index = indrel;
    1144                 :         150 :     ivinfo.heaprel = pvs->heaprel;
    1145                 :         150 :     ivinfo.analyze_only = false;
    1146                 :         150 :     ivinfo.is_autovacuum = pvs->shared->is_autovacuum;
    1147                 :         150 :     ivinfo.report_progress = true;
    1148                 :         150 :     ivinfo.message_level = DEBUG2;
    1149                 :         150 :     ivinfo.estimated_count = pvs->shared->estimated_count;
    1150                 :         150 :     ivinfo.num_heap_tuples = pvs->shared->reltuples;
    1151                 :         150 :     ivinfo.strategy = pvs->bstrategy;
    1152                 :             : 
    1153                 :             :     /* Update error traceback information */
    1154                 :         150 :     pvs->indname = pstrdup(RelationGetRelationName(indrel));
    1155                 :         150 :     pvs->status = indstats->status;
    1156                 :             : 
    1157                 :             :     /* Report the phase and the index we're about to process */
    1158                 :         300 :     progress_val[0] = (indstats->status == PARALLEL_INDVAC_STATUS_NEED_BULKDELETE)
    1159                 :             :         ? PROGRESS_VACUUM_PHASE_VACUUM_INDEX
    1160         [ +  + ]:         150 :         : PROGRESS_VACUUM_PHASE_INDEX_CLEANUP;
    1161                 :         150 :     progress_val[1] = (int64) RelationGetRelid(indrel);
    1162                 :         150 :     pgstat_progress_update_multi_param(2, progress_index, progress_val);
    1163                 :             : 
    1164      [ +  +  - ]:         150 :     switch (indstats->status)
    1165                 :             :     {
    1166                 :          56 :         case PARALLEL_INDVAC_STATUS_NEED_BULKDELETE:
    1167                 :          56 :             istat_res = vac_bulkdel_one_index(&ivinfo, istat, pvs->dead_items,
    1168                 :          56 :                                               &pvs->shared->dead_items_info);
    1169                 :          56 :             break;
    1170                 :          94 :         case PARALLEL_INDVAC_STATUS_NEED_CLEANUP:
    1171                 :          94 :             istat_res = vac_cleanup_one_index(&ivinfo, istat);
    1172                 :          94 :             break;
    1173                 :           0 :         default:
    1174         [ #  # ]:           0 :             elog(ERROR, "unexpected parallel vacuum index status %d for index \"%s\"",
    1175                 :             :                  indstats->status,
    1176                 :             :                  RelationGetRelationName(indrel));
    1177                 :             :     }
    1178                 :             : 
    1179                 :         150 :     pgstat_progress_update_multi_param(3, reset_index, reset_val);
    1180                 :             : 
    1181                 :             :     /*
    1182                 :             :      * Copy the index bulk-deletion result returned from ambulkdelete and
    1183                 :             :      * amvacuumcleanup to the DSM segment if it's the first cycle because they
    1184                 :             :      * allocate locally and it's possible that an index will be vacuumed by a
    1185                 :             :      * different vacuum process the next cycle.  Copying the result normally
    1186                 :             :      * happens only the first time an index is vacuumed.  For any additional
    1187                 :             :      * vacuum pass, we directly point to the result on the DSM segment and
    1188                 :             :      * pass it to vacuum index APIs so that workers can update it directly.
    1189                 :             :      *
    1190                 :             :      * Since all vacuum workers write the bulk-deletion result at different
    1191                 :             :      * slots we can write them without locking.
    1192                 :             :      */
    1193   [ +  +  +  + ]:         150 :     if (!indstats->istat_updated && istat_res != NULL)
    1194                 :             :     {
    1195                 :          68 :         memcpy(&(indstats->istat), istat_res, sizeof(IndexBulkDeleteResult));
    1196                 :          68 :         indstats->istat_updated = true;
    1197                 :             : 
    1198                 :             :         /* Free the locally-allocated bulk-deletion result */
    1199                 :          68 :         pfree(istat_res);
    1200                 :             :     }
    1201                 :             : 
    1202                 :             :     /*
    1203                 :             :      * Update the status to completed. No need to lock here since each worker
    1204                 :             :      * touches different indexes.
    1205                 :             :      */
    1206                 :         150 :     indstats->status = PARALLEL_INDVAC_STATUS_COMPLETED;
    1207                 :             : 
    1208                 :             :     /* Reset error traceback information */
    1209                 :         150 :     pvs->status = PARALLEL_INDVAC_STATUS_COMPLETED;
    1210                 :         150 :     pfree(pvs->indname);
    1211                 :         150 :     pvs->indname = NULL;
    1212                 :             : 
    1213                 :             :     /*
    1214                 :             :      * Call the parallel variant of pgstat_progress_incr_param so workers can
    1215                 :             :      * report progress of index vacuum to the leader.
    1216                 :             :      */
    1217                 :         150 :     pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_INDEXES_PROCESSED, 1);
    1218                 :         150 : }
    1219                 :             : 
    1220                 :             : /*
    1221                 :             :  * Returns false, if the given index can't participate in the next execution of
    1222                 :             :  * parallel index vacuum or parallel index cleanup.
    1223                 :             :  */
    1224                 :             : static bool
    1225                 :         142 : parallel_vacuum_index_is_parallel_safe(Relation indrel, int num_index_scans,
    1226                 :             :                                        bool vacuum)
    1227                 :             : {
    1228                 :             :     uint8       vacoptions;
    1229                 :             : 
    1230                 :         142 :     vacoptions = indrel->rd_indam->amparallelvacuumoptions;
    1231                 :             : 
    1232                 :             :     /* In parallel vacuum case, check if it supports parallel bulk-deletion */
    1233         [ +  + ]:         142 :     if (vacuum)
    1234                 :          52 :         return ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0);
    1235                 :             : 
    1236                 :             :     /* Not safe, if the index does not support parallel cleanup */
    1237         [ +  + ]:          90 :     if (((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) &&
    1238         [ +  + ]:          74 :         ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0))
    1239                 :           8 :         return false;
    1240                 :             : 
    1241                 :             :     /*
    1242                 :             :      * Not safe, if the index supports parallel cleanup conditionally, but we
    1243                 :             :      * have already processed the index (for bulkdelete).  We do this to avoid
    1244                 :             :      * the need to invoke workers when parallel index cleanup doesn't need to
    1245                 :             :      * scan the index.  See the comments for option
    1246                 :             :      * VACUUM_OPTION_PARALLEL_COND_CLEANUP to know when indexes support
    1247                 :             :      * parallel cleanup conditionally.
    1248                 :             :      */
    1249         [ +  + ]:          82 :     if (num_index_scans > 0 &&
    1250         [ +  + ]:          33 :         ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0))
    1251                 :          31 :         return false;
    1252                 :             : 
    1253                 :          51 :     return true;
    1254                 :             : }
    1255                 :             : 
    1256                 :             : /*
    1257                 :             :  * Perform work within a launched parallel process.
    1258                 :             :  *
    1259                 :             :  * Parallel vacuum workers perform only index vacuum or index cleanup; they
    1260                 :             :  * report progress for the index they are processing.
    1261                 :             :  */
    1262                 :             : void
    1263                 :          45 : parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
    1264                 :             : {
    1265                 :             :     ParallelVacuumState pvs;
    1266                 :             :     Relation    rel;
    1267                 :             :     Relation   *indrels;
    1268                 :             :     PVIndStats *indstats;
    1269                 :             :     PVShared   *shared;
    1270                 :             :     TidStore   *dead_items;
    1271                 :             :     BufferUsage *buffer_usage;
    1272                 :             :     WalUsage   *wal_usage;
    1273                 :             :     int         nindexes;
    1274                 :             :     char       *sharedquery;
    1275                 :             :     ErrorContextCallback errcallback;
    1276                 :             : 
    1277                 :             :     /*
    1278                 :             :      * A parallel vacuum worker carries only the PROC_IN_VACUUM flag. The
    1279                 :             :      * leader, whether it's a backend running a VACUUM command or an
    1280                 :             :      * autovacuum worker, sets PROC_IN_VACUUM when it starts vacuuming the
    1281                 :             :      * table, and the worker inherits the flag by importing the leader's
    1282                 :             :      * snapshot (see ProcArrayInstallRestoredXmin). The leader's other flags
    1283                 :             :      * don't reach the worker: the snapshot import copies only the
    1284                 :             :      * PROC_XMIN_FLAGS bits, so PROC_VACUUM_FOR_WRAPAROUND isn't carried over,
    1285                 :             :      * and PROC_IS_AUTOVACUUM is never set on the worker in the first place
    1286                 :             :      * since parallel workers run as regular background workers, not
    1287                 :             :      * autovacuum workers.
    1288                 :             :      */
    1289                 :             :     Assert(MyProc->statusFlags == PROC_IN_VACUUM);
    1290                 :             : 
    1291         [ +  + ]:          45 :     elog(DEBUG1, "starting parallel vacuum worker");
    1292                 :             : 
    1293                 :             :     /* Used by tests to hold a worker while it is attached to its leader */
    1294                 :          45 :     INJECTION_POINT("parallel-vacuum-worker-start", NULL);
    1295                 :             : 
    1296                 :          44 :     shared = (PVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false);
    1297                 :             : 
    1298                 :             :     /* Set debug_query_string for individual workers */
    1299                 :          44 :     sharedquery = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_QUERY_TEXT, true);
    1300                 :          44 :     debug_query_string = sharedquery;
    1301                 :          44 :     pgstat_report_activity(STATE_RUNNING, debug_query_string);
    1302                 :             : 
    1303                 :             :     /* Track query ID */
    1304                 :          44 :     pgstat_report_query_id(shared->queryid, false);
    1305                 :             : 
    1306                 :             :     /*
    1307                 :             :      * Open table.  The lock mode is the same as the leader process.  It's
    1308                 :             :      * okay because the lock mode does not conflict among the parallel
    1309                 :             :      * workers.
    1310                 :             :      */
    1311                 :          44 :     rel = table_open(shared->relid, ShareUpdateExclusiveLock);
    1312                 :             : 
    1313                 :             :     /*
    1314                 :             :      * Open all indexes. indrels are sorted in order by OID, which should be
    1315                 :             :      * matched to the leader's one.
    1316                 :             :      */
    1317                 :          44 :     vac_open_indexes(rel, RowExclusiveLock, &nindexes, &indrels);
    1318                 :             :     Assert(nindexes > 0);
    1319                 :             : 
    1320                 :             :     /*
    1321                 :             :      * Apply the desired value of maintenance_work_mem within this process.
    1322                 :             :      * Really we should use SetConfigOption() to change a GUC, but since we're
    1323                 :             :      * already in parallel mode guc.c would complain about that.  Fortunately,
    1324                 :             :      * by the same token guc.c will not let any user-defined code change it.
    1325                 :             :      * So just avert your eyes while we do this:
    1326                 :             :      */
    1327         [ +  - ]:          44 :     if (shared->maintenance_work_mem_worker > 0)
    1328                 :          44 :         maintenance_work_mem = shared->maintenance_work_mem_worker;
    1329                 :             : 
    1330                 :             :     /* Set index statistics */
    1331                 :          44 :     indstats = (PVIndStats *) shm_toc_lookup(toc,
    1332                 :             :                                              PARALLEL_VACUUM_KEY_INDEX_STATS,
    1333                 :             :                                              false);
    1334                 :             : 
    1335                 :             :     /* Find dead_items in shared memory */
    1336                 :          44 :     dead_items = TidStoreAttach(shared->dead_items_dsa_handle,
    1337                 :             :                                 shared->dead_items_handle);
    1338                 :             : 
    1339                 :             :     /* Set cost-based vacuum delay */
    1340         [ +  + ]:          44 :     if (shared->is_autovacuum)
    1341                 :             :     {
    1342                 :             :         /*
    1343                 :             :          * Parallel autovacuum workers initialize cost-based delay parameters
    1344                 :             :          * from the leader's shared state rather than GUC defaults, because
    1345                 :             :          * the leader may have applied per-table or autovacuum-specific
    1346                 :             :          * overrides. pv_shared_cost_params must be set before calling
    1347                 :             :          * parallel_vacuum_update_shared_delay_params().
    1348                 :             :          */
    1349                 :           4 :         pv_shared_cost_params = &(shared->cost_params);
    1350                 :           4 :         parallel_vacuum_update_shared_delay_params();
    1351                 :             :     }
    1352                 :             :     else
    1353                 :          40 :         VacuumUpdateCosts();
    1354                 :             : 
    1355                 :          44 :     VacuumCostBalance = 0;
    1356                 :          44 :     VacuumCostBalanceLocal = 0;
    1357                 :          44 :     VacuumSharedCostBalance = &(shared->cost_balance);
    1358                 :          44 :     VacuumActiveNWorkers = &(shared->active_nworkers);
    1359                 :             : 
    1360                 :             :     /* Set parallel vacuum state */
    1361                 :          44 :     pvs.indrels = indrels;
    1362                 :          44 :     pvs.nindexes = nindexes;
    1363                 :          44 :     pvs.indstats = indstats;
    1364                 :          44 :     pvs.shared = shared;
    1365                 :          44 :     pvs.dead_items = dead_items;
    1366                 :          44 :     pvs.relnamespace = get_namespace_name(RelationGetNamespace(rel));
    1367                 :          44 :     pvs.relname = pstrdup(RelationGetRelationName(rel));
    1368                 :          44 :     pvs.heaprel = rel;
    1369                 :             : 
    1370                 :             :     /* These fields will be filled during index vacuum or cleanup */
    1371                 :          44 :     pvs.indname = NULL;
    1372                 :          44 :     pvs.status = PARALLEL_INDVAC_STATUS_INITIAL;
    1373                 :             : 
    1374                 :             :     /* Each parallel VACUUM worker gets its own access strategy. */
    1375                 :          88 :     pvs.bstrategy = GetAccessStrategyWithSize(BAS_VACUUM,
    1376                 :          44 :                                               shared->ring_nbuffers * (BLCKSZ / 1024));
    1377                 :             : 
    1378                 :             :     /* Setup error traceback support for ereport() */
    1379                 :          44 :     errcallback.callback = parallel_vacuum_error_callback;
    1380                 :          44 :     errcallback.arg = &pvs;
    1381                 :          44 :     errcallback.previous = error_context_stack;
    1382                 :          44 :     error_context_stack = &errcallback;
    1383                 :             : 
    1384                 :             :     /* Prepare to track buffer usage during parallel execution */
    1385                 :          44 :     InstrStartParallelQuery();
    1386                 :             : 
    1387                 :             :     /* Register this worker for vacuum progress reporting */
    1388                 :          44 :     pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, shared->relid);
    1389                 :             : 
    1390                 :             :     /* Process indexes to perform vacuum/cleanup */
    1391                 :          44 :     parallel_vacuum_process_safe_indexes(&pvs);
    1392                 :             : 
    1393                 :             :     /* Report buffer/WAL usage during parallel execution */
    1394                 :          44 :     buffer_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, false);
    1395                 :          44 :     wal_usage = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_WAL_USAGE, false);
    1396                 :          44 :     InstrEndParallelQuery(&buffer_usage[ParallelWorkerNumber],
    1397                 :          44 :                           &wal_usage[ParallelWorkerNumber]);
    1398                 :             : 
    1399                 :             :     /* Report any remaining cost-based vacuum delay time */
    1400         [ +  + ]:          44 :     if (parallel_vacuum_worker_delay_ns > 0)
    1401                 :           2 :         pgstat_progress_parallel_incr_param(PROGRESS_VACUUM_DELAY_TIME,
    1402                 :             :                                             parallel_vacuum_worker_delay_ns);
    1403                 :             : 
    1404                 :          44 :     TidStoreDetach(dead_items);
    1405                 :             : 
    1406                 :             :     /* Pop the error context stack */
    1407                 :          44 :     error_context_stack = errcallback.previous;
    1408                 :             : 
    1409                 :          44 :     pgstat_progress_end_command();
    1410                 :             : 
    1411                 :          44 :     vac_close_indexes(nindexes, indrels, RowExclusiveLock);
    1412                 :          44 :     table_close(rel, ShareUpdateExclusiveLock);
    1413                 :          44 :     FreeAccessStrategy(pvs.bstrategy);
    1414                 :             : 
    1415         [ +  + ]:          44 :     if (shared->is_autovacuum)
    1416                 :           4 :         pv_shared_cost_params = NULL;
    1417                 :          44 : }
    1418                 :             : 
    1419                 :             : /*
    1420                 :             :  * Error context callback for errors occurring during parallel index vacuum.
    1421                 :             :  * The error context messages should match the messages set in the lazy vacuum
    1422                 :             :  * error context.  If you change this function, change vacuum_error_callback()
    1423                 :             :  * as well.
    1424                 :             :  */
    1425                 :             : static void
    1426                 :           7 : parallel_vacuum_error_callback(void *arg)
    1427                 :             : {
    1428                 :           7 :     ParallelVacuumState *errinfo = arg;
    1429                 :             : 
    1430      [ +  -  - ]:           7 :     switch (errinfo->status)
    1431                 :             :     {
    1432                 :           7 :         case PARALLEL_INDVAC_STATUS_NEED_BULKDELETE:
    1433                 :           7 :             errcontext("while vacuuming index \"%s\" of relation \"%s.%s\"",
    1434                 :             :                        errinfo->indname,
    1435                 :             :                        errinfo->relnamespace,
    1436                 :             :                        errinfo->relname);
    1437                 :           7 :             break;
    1438                 :           0 :         case PARALLEL_INDVAC_STATUS_NEED_CLEANUP:
    1439                 :           0 :             errcontext("while cleaning up index \"%s\" of relation \"%s.%s\"",
    1440                 :             :                        errinfo->indname,
    1441                 :             :                        errinfo->relnamespace,
    1442                 :             :                        errinfo->relname);
    1443                 :           0 :             break;
    1444                 :           0 :         case PARALLEL_INDVAC_STATUS_INITIAL:
    1445                 :             :         case PARALLEL_INDVAC_STATUS_COMPLETED:
    1446                 :             :         default:
    1447                 :           0 :             return;
    1448                 :             :     }
    1449                 :             : }
        

Generated by: LCOV version 2.0-1