PostgreSQL Source Code  git master
vacuumlazy.c File Reference
#include "postgres.h"
#include <math.h>
#include "access/amapi.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heapam_xlog.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/index.h"
#include "catalog/storage.h"
#include "commands/dbcommands.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "executor/instrument.h"
#include "miscadmin.h"
#include "optimizer/paths.h"
#include "pgstat.h"
#include "portability/instr_time.h"
#include "postmaster/autovacuum.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
#include "tcop/tcopprot.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/timestamp.h"
Include dependency graph for vacuumlazy.c:

Go to the source code of this file.

Data Structures

struct  LVDeadTuples
 
struct  LVShared
 
struct  LVSharedIndStats
 
struct  LVParallelState
 
struct  LVRelState
 
struct  LVPagePruneState
 
struct  LVSavedErrInfo
 

Macros

#define REL_TRUNCATE_MINIMUM   1000
 
#define REL_TRUNCATE_FRACTION   16
 
#define VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL   20 /* ms */
 
#define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL   50 /* ms */
 
#define VACUUM_TRUNCATE_LOCK_TIMEOUT   5000 /* ms */
 
#define BYPASS_THRESHOLD_PAGES   0.02 /* i.e. 2% of rel_pages */
 
#define FAILSAFE_MIN_PAGES   ((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / BLCKSZ))
 
#define VACUUM_FSM_EVERY_PAGES   ((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / BLCKSZ))
 
#define LAZY_ALLOC_TUPLES   MaxHeapTuplesPerPage
 
#define SKIP_PAGES_THRESHOLD   ((BlockNumber) 32)
 
#define PREFETCH_SIZE   ((BlockNumber) 32)
 
#define PARALLEL_VACUUM_KEY_SHARED   1
 
#define PARALLEL_VACUUM_KEY_DEAD_TUPLES   2
 
#define PARALLEL_VACUUM_KEY_QUERY_TEXT   3
 
#define PARALLEL_VACUUM_KEY_BUFFER_USAGE   4
 
#define PARALLEL_VACUUM_KEY_WAL_USAGE   5
 
#define ParallelVacuumIsActive(vacrel)   ((vacrel)->lps != NULL)
 
#define SizeOfDeadTuples(cnt)
 
#define MAXDEADTUPLES(max_size)   (((max_size) - offsetof(LVDeadTuples, itemptrs)) / sizeof(ItemPointerData))
 
#define SizeOfLVShared   (offsetof(LVShared, bitmap) + sizeof(bits8))
 
#define GetSharedIndStats(s)   ((LVSharedIndStats *)((char *)(s) + ((LVShared *)(s))->offset))
 
#define IndStatsIsNull(s, i)   (!(((LVShared *)(s))->bitmap[(i) >> 3] & (1 << ((i) & 0x07))))
 
#define FORCE_CHECK_PAGE()   (blkno == nblocks - 1 && should_attempt_truncation(vacrel, params))
 

Typedefs

typedef struct LVDeadTuples LVDeadTuples
 
typedef struct LVShared LVShared
 
typedef struct LVSharedIndStats LVSharedIndStats
 
typedef struct LVParallelState LVParallelState
 
typedef struct LVRelState LVRelState
 
typedef struct LVPagePruneState LVPagePruneState
 
typedef struct LVSavedErrInfo LVSavedErrInfo
 

Enumerations

enum  VacErrPhase {
  VACUUM_ERRCB_PHASE_UNKNOWN, VACUUM_ERRCB_PHASE_SCAN_HEAP, VACUUM_ERRCB_PHASE_VACUUM_INDEX, VACUUM_ERRCB_PHASE_VACUUM_HEAP,
  VACUUM_ERRCB_PHASE_INDEX_CLEANUP, VACUUM_ERRCB_PHASE_TRUNCATE
}
 

Functions

static void lazy_scan_heap (LVRelState *vacrel, VacuumParams *params, bool aggressive)
 
static void lazy_scan_prune (LVRelState *vacrel, Buffer buf, BlockNumber blkno, Page page, GlobalVisState *vistest, LVPagePruneState *prunestate)
 
static void lazy_vacuum (LVRelState *vacrel, bool onecall)
 
static bool lazy_vacuum_all_indexes (LVRelState *vacrel)
 
static void lazy_vacuum_heap_rel (LVRelState *vacrel)
 
static int lazy_vacuum_heap_page (LVRelState *vacrel, BlockNumber blkno, Buffer buffer, int tupindex, Buffer *vmbuffer)
 
static bool lazy_check_needs_freeze (Buffer buf, bool *hastup, LVRelState *vacrel)
 
static bool lazy_check_wraparound_failsafe (LVRelState *vacrel)
 
static void do_parallel_lazy_vacuum_all_indexes (LVRelState *vacrel)
 
static void do_parallel_lazy_cleanup_all_indexes (LVRelState *vacrel)
 
static void do_parallel_vacuum_or_cleanup (LVRelState *vacrel, int nworkers)
 
static void do_parallel_processing (LVRelState *vacrel, LVShared *lvshared)
 
static void do_serial_processing_for_unsafe_indexes (LVRelState *vacrel, LVShared *lvshared)
 
static IndexBulkDeleteResultparallel_process_one_index (Relation indrel, IndexBulkDeleteResult *istat, LVShared *lvshared, LVSharedIndStats *shared_indstats, LVRelState *vacrel)
 
static void lazy_cleanup_all_indexes (LVRelState *vacrel)
 
static IndexBulkDeleteResultlazy_vacuum_one_index (Relation indrel, IndexBulkDeleteResult *istat, double reltuples, LVRelState *vacrel)
 
static IndexBulkDeleteResultlazy_cleanup_one_index (Relation indrel, IndexBulkDeleteResult *istat, double reltuples, bool estimated_count, LVRelState *vacrel)
 
static bool should_attempt_truncation (LVRelState *vacrel, VacuumParams *params)
 
static void lazy_truncate_heap (LVRelState *vacrel)
 
static BlockNumber count_nondeletable_pages (LVRelState *vacrel)
 
static long compute_max_dead_tuples (BlockNumber relblocks, bool hasindex)
 
static void lazy_space_alloc (LVRelState *vacrel, int nworkers, BlockNumber relblocks)
 
static void lazy_space_free (LVRelState *vacrel)
 
static bool lazy_tid_reaped (ItemPointer itemptr, void *state)
 
static int vac_cmp_itemptr (const void *left, const void *right)
 
static bool heap_page_is_all_visible (LVRelState *vacrel, Buffer buf, TransactionId *visibility_cutoff_xid, bool *all_frozen)
 
static int compute_parallel_vacuum_workers (LVRelState *vacrel, int nrequested, bool *can_parallel_vacuum)
 
static void update_index_statistics (LVRelState *vacrel)
 
static LVParallelStatebegin_parallel_vacuum (LVRelState *vacrel, BlockNumber nblocks, int nrequested)
 
static void end_parallel_vacuum (LVRelState *vacrel)
 
static LVSharedIndStatsparallel_stats_for_idx (LVShared *lvshared, int getidx)
 
static bool parallel_processing_is_safe (Relation indrel, LVShared *lvshared)
 
static void vacuum_error_callback (void *arg)
 
static void update_vacuum_error_info (LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
 
static void restore_vacuum_error_info (LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel)
 
void heap_vacuum_rel (Relation rel, VacuumParams *params, BufferAccessStrategy bstrategy)
 
void parallel_vacuum_main (dsm_segment *seg, shm_toc *toc)
 

Variables

static int elevel = -1
 

Macro Definition Documentation

◆ BYPASS_THRESHOLD_PAGES

#define BYPASS_THRESHOLD_PAGES   0.02 /* i.e. 2% of rel_pages */

Definition at line 110 of file vacuumlazy.c.

Referenced by lazy_vacuum().

◆ FAILSAFE_MIN_PAGES

#define FAILSAFE_MIN_PAGES   ((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / BLCKSZ))

Definition at line 116 of file vacuumlazy.c.

Referenced by lazy_check_wraparound_failsafe().

◆ FORCE_CHECK_PAGE

#define FORCE_CHECK_PAGE ( )    (blkno == nblocks - 1 && should_attempt_truncation(vacrel, params))

Referenced by lazy_scan_heap().

◆ GetSharedIndStats

#define GetSharedIndStats (   s)    ((LVSharedIndStats *)((char *)(s) + ((LVShared *)(s))->offset))

Definition at line 268 of file vacuumlazy.c.

Referenced by parallel_stats_for_idx().

◆ IndStatsIsNull

#define IndStatsIsNull (   s,
  i 
)    (!(((LVShared *)(s))->bitmap[(i) >> 3] & (1 << ((i) & 0x07))))

Definition at line 270 of file vacuumlazy.c.

Referenced by parallel_stats_for_idx().

◆ LAZY_ALLOC_TUPLES

#define LAZY_ALLOC_TUPLES   MaxHeapTuplesPerPage

Definition at line 133 of file vacuumlazy.c.

Referenced by compute_max_dead_tuples().

◆ MAXDEADTUPLES

#define MAXDEADTUPLES (   max_size)    (((max_size) - offsetof(LVDeadTuples, itemptrs)) / sizeof(ItemPointerData))

Definition at line 194 of file vacuumlazy.c.

Referenced by compute_max_dead_tuples(), and lazy_vacuum().

◆ PARALLEL_VACUUM_KEY_BUFFER_USAGE

#define PARALLEL_VACUUM_KEY_BUFFER_USAGE   4

Definition at line 155 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and parallel_vacuum_main().

◆ PARALLEL_VACUUM_KEY_DEAD_TUPLES

#define PARALLEL_VACUUM_KEY_DEAD_TUPLES   2

Definition at line 153 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and parallel_vacuum_main().

◆ PARALLEL_VACUUM_KEY_QUERY_TEXT

#define PARALLEL_VACUUM_KEY_QUERY_TEXT   3

Definition at line 154 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and parallel_vacuum_main().

◆ PARALLEL_VACUUM_KEY_SHARED

#define PARALLEL_VACUUM_KEY_SHARED   1

Definition at line 152 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and parallel_vacuum_main().

◆ PARALLEL_VACUUM_KEY_WAL_USAGE

#define PARALLEL_VACUUM_KEY_WAL_USAGE   5

Definition at line 156 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and parallel_vacuum_main().

◆ ParallelVacuumIsActive

#define ParallelVacuumIsActive (   vacrel)    ((vacrel)->lps != NULL)

◆ PREFETCH_SIZE

#define PREFETCH_SIZE   ((BlockNumber) 32)

Definition at line 145 of file vacuumlazy.c.

Referenced by count_nondeletable_pages().

◆ REL_TRUNCATE_FRACTION

#define REL_TRUNCATE_FRACTION   16

Definition at line 93 of file vacuumlazy.c.

Referenced by should_attempt_truncation().

◆ REL_TRUNCATE_MINIMUM

#define REL_TRUNCATE_MINIMUM   1000

Definition at line 92 of file vacuumlazy.c.

Referenced by should_attempt_truncation().

◆ SizeOfDeadTuples

#define SizeOfDeadTuples (   cnt)
Value:
mul_size(sizeof(ItemPointerData), cnt))
Size mul_size(Size s1, Size s2)
Definition: shmem.c:519
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
#define offsetof(type, field)
Definition: c.h:727

Definition at line 191 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and lazy_space_alloc().

◆ SizeOfLVShared

#define SizeOfLVShared   (offsetof(LVShared, bitmap) + sizeof(bits8))

Definition at line 267 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum().

◆ SKIP_PAGES_THRESHOLD

#define SKIP_PAGES_THRESHOLD   ((BlockNumber) 32)

Definition at line 139 of file vacuumlazy.c.

Referenced by lazy_scan_heap().

◆ VACUUM_FSM_EVERY_PAGES

#define VACUUM_FSM_EVERY_PAGES   ((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / BLCKSZ))

Definition at line 125 of file vacuumlazy.c.

Referenced by lazy_scan_heap().

◆ VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL

#define VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL   20 /* ms */

Definition at line 102 of file vacuumlazy.c.

Referenced by count_nondeletable_pages().

◆ VACUUM_TRUNCATE_LOCK_TIMEOUT

#define VACUUM_TRUNCATE_LOCK_TIMEOUT   5000 /* ms */

Definition at line 104 of file vacuumlazy.c.

Referenced by lazy_truncate_heap().

◆ VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL

#define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL   50 /* ms */

Definition at line 103 of file vacuumlazy.c.

Referenced by lazy_truncate_heap().

Typedef Documentation

◆ LVDeadTuples

typedef struct LVDeadTuples LVDeadTuples

◆ LVPagePruneState

◆ LVParallelState

◆ LVRelState

typedef struct LVRelState LVRelState

◆ LVSavedErrInfo

◆ LVShared

typedef struct LVShared LVShared

◆ LVSharedIndStats

Enumeration Type Documentation

◆ VacErrPhase

Enumerator
VACUUM_ERRCB_PHASE_UNKNOWN 
VACUUM_ERRCB_PHASE_SCAN_HEAP 
VACUUM_ERRCB_PHASE_VACUUM_INDEX 
VACUUM_ERRCB_PHASE_VACUUM_HEAP 
VACUUM_ERRCB_PHASE_INDEX_CLEANUP 
VACUUM_ERRCB_PHASE_TRUNCATE 

Definition at line 165 of file vacuumlazy.c.

Function Documentation

◆ begin_parallel_vacuum()

static LVParallelState * begin_parallel_vacuum ( LVRelState vacrel,
BlockNumber  nblocks,
int  nrequested 
)
static

Definition at line 3826 of file vacuumlazy.c.

References LVShared::active_nworkers, add_size(), IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::amusemaintenanceworkmem, Assert, LVShared::bitmap, BITMAPLEN, LVParallelState::buffer_usage, compute_max_dead_tuples(), compute_parallel_vacuum_workers(), LVShared::cost_balance, CreateParallelContext(), LVRelState::dead_tuples, debug_query_string, LVShared::elevel, elevel, EnterParallelMode(), ParallelContext::estimator, idx(), LVShared::idx, LVRelState::indrels, InitializeParallelDSM(), LVDeadTuples::itemptrs, LVParallelState::lvshared, maintenance_work_mem, LVShared::maintenance_work_mem_worker, LVDeadTuples::max_tuples, MAXALIGN, MemSet, Min, mul_size(), LVRelState::nindexes, LVParallelState::nindexes_parallel_bulkdel, LVParallelState::nindexes_parallel_cleanup, LVParallelState::nindexes_parallel_condcleanup, LVDeadTuples::num_tuples, ParallelContext::nworkers, LVShared::offset, palloc0(), PARALLEL_VACUUM_KEY_BUFFER_USAGE, PARALLEL_VACUUM_KEY_DEAD_TUPLES, PARALLEL_VACUUM_KEY_QUERY_TEXT, PARALLEL_VACUUM_KEY_SHARED, PARALLEL_VACUUM_KEY_WAL_USAGE, LVParallelState::pcxt, pfree(), pg_atomic_init_u32(), RelationData::rd_indam, LVRelState::rel, RelationGetRelid, LVShared::relid, shm_toc_allocate(), shm_toc_estimate_chunk, shm_toc_estimate_keys, shm_toc_insert(), SizeOfDeadTuples, SizeOfLVShared, ParallelContext::toc, VACUUM_OPTION_MAX_VALID_VALUE, VACUUM_OPTION_PARALLEL_BULKDEL, VACUUM_OPTION_PARALLEL_CLEANUP, VACUUM_OPTION_PARALLEL_COND_CLEANUP, and LVParallelState::wal_usage.

Referenced by lazy_space_alloc().

3828 {
3829  LVParallelState *lps = NULL;
3830  Relation *indrels = vacrel->indrels;
3831  int nindexes = vacrel->nindexes;
3832  ParallelContext *pcxt;
3833  LVShared *shared;
3834  LVDeadTuples *dead_tuples;
3835  BufferUsage *buffer_usage;
3836  WalUsage *wal_usage;
3837  bool *can_parallel_vacuum;
3838  long maxtuples;
3839  Size est_shared;
3840  Size est_deadtuples;
3841  int nindexes_mwm = 0;
3842  int parallel_workers = 0;
3843  int querylen;
3844 
3845  /*
3846  * A parallel vacuum must be requested and there must be indexes on the
3847  * relation
3848  */
3849  Assert(nrequested >= 0);
3850  Assert(nindexes > 0);
3851 
3852  /*
3853  * Compute the number of parallel vacuum workers to launch
3854  */
3855  can_parallel_vacuum = (bool *) palloc0(sizeof(bool) * nindexes);
3856  parallel_workers = compute_parallel_vacuum_workers(vacrel,
3857  nrequested,
3858  can_parallel_vacuum);
3859 
3860  /* Can't perform vacuum in parallel */
3861  if (parallel_workers <= 0)
3862  {
3863  pfree(can_parallel_vacuum);
3864  return lps;
3865  }
3866 
3867  lps = (LVParallelState *) palloc0(sizeof(LVParallelState));
3868 
3870  pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
3871  parallel_workers);
3872  Assert(pcxt->nworkers > 0);
3873  lps->pcxt = pcxt;
3874 
3875  /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
3876  est_shared = MAXALIGN(add_size(SizeOfLVShared, BITMAPLEN(nindexes)));
3877  for (int idx = 0; idx < nindexes; idx++)
3878  {
3879  Relation indrel = indrels[idx];
3880  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
3881 
3882  /*
3883  * Cleanup option should be either disabled, always performing in
3884  * parallel or conditionally performing in parallel.
3885  */
3886  Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
3887  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
3888  Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
3889 
3890  /* Skip indexes that don't participate in parallel vacuum */
3891  if (!can_parallel_vacuum[idx])
3892  continue;
3893 
3894  if (indrel->rd_indam->amusemaintenanceworkmem)
3895  nindexes_mwm++;
3896 
3897  est_shared = add_size(est_shared, sizeof(LVSharedIndStats));
3898 
3899  /*
3900  * Remember the number of indexes that support parallel operation for
3901  * each phase.
3902  */
3903  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
3905  if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
3907  if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
3909  }
3910  shm_toc_estimate_chunk(&pcxt->estimator, est_shared);
3911  shm_toc_estimate_keys(&pcxt->estimator, 1);
3912 
3913  /* Estimate size for dead tuples -- PARALLEL_VACUUM_KEY_DEAD_TUPLES */
3914  maxtuples = compute_max_dead_tuples(nblocks, true);
3915  est_deadtuples = MAXALIGN(SizeOfDeadTuples(maxtuples));
3916  shm_toc_estimate_chunk(&pcxt->estimator, est_deadtuples);
3917  shm_toc_estimate_keys(&pcxt->estimator, 1);
3918 
3919  /*
3920  * Estimate space for BufferUsage and WalUsage --
3921  * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
3922  *
3923  * If there are no extensions loaded that care, we could skip this. We
3924  * have no way of knowing whether anyone's looking at pgBufferUsage or
3925  * pgWalUsage, so do it unconditionally.
3926  */
3928  mul_size(sizeof(BufferUsage), pcxt->nworkers));
3929  shm_toc_estimate_keys(&pcxt->estimator, 1);
3931  mul_size(sizeof(WalUsage), pcxt->nworkers));
3932  shm_toc_estimate_keys(&pcxt->estimator, 1);
3933 
3934  /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
3935  if (debug_query_string)
3936  {
3937  querylen = strlen(debug_query_string);
3938  shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
3939  shm_toc_estimate_keys(&pcxt->estimator, 1);
3940  }
3941  else
3942  querylen = 0; /* keep compiler quiet */
3943 
3944  InitializeParallelDSM(pcxt);
3945 
3946  /* Prepare shared information */
3947  shared = (LVShared *) shm_toc_allocate(pcxt->toc, est_shared);
3948  MemSet(shared, 0, est_shared);
3949  shared->relid = RelationGetRelid(vacrel->rel);
3950  shared->elevel = elevel;
3951  shared->maintenance_work_mem_worker =
3952  (nindexes_mwm > 0) ?
3953  maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
3955 
3956  pg_atomic_init_u32(&(shared->cost_balance), 0);
3957  pg_atomic_init_u32(&(shared->active_nworkers), 0);
3958  pg_atomic_init_u32(&(shared->idx), 0);
3959  shared->offset = MAXALIGN(add_size(SizeOfLVShared, BITMAPLEN(nindexes)));
3960 
3961  /*
3962  * Initialize variables for shared index statistics, set NULL bitmap and
3963  * the size of stats for each index.
3964  */
3965  memset(shared->bitmap, 0x00, BITMAPLEN(nindexes));
3966  for (int idx = 0; idx < nindexes; idx++)
3967  {
3968  if (!can_parallel_vacuum[idx])
3969  continue;
3970 
3971  /* Set NOT NULL as this index does support parallelism */
3972  shared->bitmap[idx >> 3] |= 1 << (idx & 0x07);
3973  }
3974 
3976  lps->lvshared = shared;
3977 
3978  /* Prepare the dead tuple space */
3979  dead_tuples = (LVDeadTuples *) shm_toc_allocate(pcxt->toc, est_deadtuples);
3980  dead_tuples->max_tuples = maxtuples;
3981  dead_tuples->num_tuples = 0;
3982  MemSet(dead_tuples->itemptrs, 0, sizeof(ItemPointerData) * maxtuples);
3983  shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_DEAD_TUPLES, dead_tuples);
3984  vacrel->dead_tuples = dead_tuples;
3985 
3986  /*
3987  * Allocate space for each worker's BufferUsage and WalUsage; no need to
3988  * initialize
3989  */
3990  buffer_usage = shm_toc_allocate(pcxt->toc,
3991  mul_size(sizeof(BufferUsage), pcxt->nworkers));
3992  shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, buffer_usage);
3993  lps->buffer_usage = buffer_usage;
3994  wal_usage = shm_toc_allocate(pcxt->toc,
3995  mul_size(sizeof(WalUsage), pcxt->nworkers));
3997  lps->wal_usage = wal_usage;
3998 
3999  /* Store query string for workers */
4000  if (debug_query_string)
4001  {
4002  char *sharedquery;
4003 
4004  sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
4005  memcpy(sharedquery, debug_query_string, querylen + 1);
4006  sharedquery[querylen] = '\0';
4007  shm_toc_insert(pcxt->toc,
4008  PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
4009  }
4010 
4011  pfree(can_parallel_vacuum);
4012  return lps;
4013 }
uint8 amparallelvacuumoptions
Definition: amapi.h:248
struct IndexAmRoutine * rd_indam
Definition: rel.h:201
WalUsage * wal_usage
Definition: vacuumlazy.c:295
Relation * indrels
Definition: vacuumlazy.c:310
ItemPointerData itemptrs[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:186
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:164
#define PARALLEL_VACUUM_KEY_DEAD_TUPLES
Definition: vacuumlazy.c:153
Oid relid
Definition: vacuumlazy.c:207
#define SizeOfDeadTuples(cnt)
Definition: vacuumlazy.c:191
BufferUsage * buffer_usage
Definition: vacuumlazy.c:292
shm_toc_estimator estimator
Definition: parallel.h:42
#define Min(x, y)
Definition: c.h:986
int nindexes
Definition: vacuumlazy.c:311
unsigned char uint8
Definition: c.h:439
#define VACUUM_OPTION_MAX_VALID_VALUE
Definition: vacuum.h:63
#define MemSet(start, val, len)
Definition: c.h:1008
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
static long compute_max_dead_tuples(BlockNumber relblocks, bool hasindex)
Definition: vacuumlazy.c:3438
#define BITMAPLEN(NATTS)
Definition: htup_details.h:546
int nindexes_parallel_bulkdel
Definition: vacuumlazy.c:301
int maintenance_work_mem_worker
Definition: vacuumlazy.c:239
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
int nindexes_parallel_condcleanup
Definition: vacuumlazy.c:303
#define PARALLEL_VACUUM_KEY_QUERY_TEXT
Definition: vacuumlazy.c:154
ParallelContext * pcxt
Definition: vacuumlazy.c:286
#define SizeOfLVShared
Definition: vacuumlazy.c:267
pg_atomic_uint32 cost_balance
Definition: vacuumlazy.c:246
void pfree(void *pointer)
Definition: mcxt.c:1169
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
int elevel
Definition: vacuumlazy.c:208
pg_atomic_uint32 idx
Definition: vacuumlazy.c:260
const char * debug_query_string
Definition: postgres.c:89
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:202
static int elevel
Definition: vacuumlazy.c:400
int nindexes_parallel_cleanup
Definition: vacuumlazy.c:302
Size mul_size(Size s1, Size s2)
Definition: shmem.c:519
void * palloc0(Size size)
Definition: mcxt.c:1093
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
#define PARALLEL_VACUUM_KEY_SHARED
Definition: vacuumlazy.c:152
pg_atomic_uint32 active_nworkers
Definition: vacuumlazy.c:253
int maintenance_work_mem
Definition: globals.c:126
bool amusemaintenanceworkmem
Definition: amapi.h:246
static int compute_parallel_vacuum_workers(LVRelState *vacrel, int nrequested, bool *can_parallel_vacuum)
Definition: vacuumlazy.c:3732
#define PARALLEL_VACUUM_KEY_BUFFER_USAGE
Definition: vacuumlazy.c:155
#define Assert(condition)
Definition: c.h:804
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:52
bits8 bitmap[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:262
#define PARALLEL_VACUUM_KEY_WAL_USAGE
Definition: vacuumlazy.c:156
size_t Size
Definition: c.h:540
#define shm_toc_estimate_keys(e, cnt)
Definition: shm_toc.h:53
#define MAXALIGN(LEN)
Definition: c.h:757
void EnterParallelMode(void)
Definition: xact.c:979
LVShared * lvshared
Definition: vacuumlazy.c:289
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:45
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition: shm_toc.c:88
Relation rel
Definition: vacuumlazy.c:309
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition: shm_toc.c:171
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:223
uint32 offset
Definition: vacuumlazy.c:261
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:60
#define RelationGetRelid(relation)
Definition: rel.h:469
shm_toc * toc
Definition: parallel.h:45

◆ compute_max_dead_tuples()

static long compute_max_dead_tuples ( BlockNumber  relblocks,
bool  hasindex 
)
static

Definition at line 3438 of file vacuumlazy.c.

References autovacuum_work_mem, IsAutoVacuumWorkerProcess(), LAZY_ALLOC_TUPLES, maintenance_work_mem, Max, MaxAllocSize, MAXDEADTUPLES, MaxHeapTuplesPerPage, and Min.

Referenced by begin_parallel_vacuum(), and lazy_space_alloc().

3439 {
3440  long maxtuples;
3441  int vac_work_mem = IsAutoVacuumWorkerProcess() &&
3442  autovacuum_work_mem != -1 ?
3444 
3445  if (hasindex)
3446  {
3447  maxtuples = MAXDEADTUPLES(vac_work_mem * 1024L);
3448  maxtuples = Min(maxtuples, INT_MAX);
3449  maxtuples = Min(maxtuples, MAXDEADTUPLES(MaxAllocSize));
3450 
3451  /* curious coding here to ensure the multiplication can't overflow */
3452  if ((BlockNumber) (maxtuples / LAZY_ALLOC_TUPLES) > relblocks)
3453  maxtuples = relblocks * LAZY_ALLOC_TUPLES;
3454 
3455  /* stay sane if small maintenance_work_mem */
3456  maxtuples = Max(maxtuples, MaxHeapTuplesPerPage);
3457  }
3458  else
3459  maxtuples = MaxHeapTuplesPerPage;
3460 
3461  return maxtuples;
3462 }
int autovacuum_work_mem
Definition: autovacuum.c:117
#define Min(x, y)
Definition: c.h:986
#define MaxHeapTuplesPerPage
Definition: htup_details.h:573
uint32 BlockNumber
Definition: block.h:31
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3448
#define MaxAllocSize
Definition: memutils.h:40
int maintenance_work_mem
Definition: globals.c:126
#define Max(x, y)
Definition: c.h:980
#define MAXDEADTUPLES(max_size)
Definition: vacuumlazy.c:194
#define LAZY_ALLOC_TUPLES
Definition: vacuumlazy.c:133

◆ compute_parallel_vacuum_workers()

static int compute_parallel_vacuum_workers ( LVRelState vacrel,
int  nrequested,
bool can_parallel_vacuum 
)
static

Definition at line 3732 of file vacuumlazy.c.

References IndexAmRoutine::amparallelvacuumoptions, idx(), LVRelState::indrels, IsUnderPostmaster, Max, max_parallel_maintenance_workers, Min, min_parallel_index_scan_size, LVRelState::nindexes, RelationData::rd_indam, RelationGetNumberOfBlocks, VACUUM_OPTION_NO_PARALLEL, VACUUM_OPTION_PARALLEL_BULKDEL, VACUUM_OPTION_PARALLEL_CLEANUP, and VACUUM_OPTION_PARALLEL_COND_CLEANUP.

Referenced by begin_parallel_vacuum().

3734 {
3735  int nindexes_parallel = 0;
3736  int nindexes_parallel_bulkdel = 0;
3737  int nindexes_parallel_cleanup = 0;
3738  int parallel_workers;
3739 
3740  /*
3741  * We don't allow performing parallel operation in standalone backend or
3742  * when parallelism is disabled.
3743  */
3745  return 0;
3746 
3747  /*
3748  * Compute the number of indexes that can participate in parallel vacuum.
3749  */
3750  for (int idx = 0; idx < vacrel->nindexes; idx++)
3751  {
3752  Relation indrel = vacrel->indrels[idx];
3753  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
3754 
3755  if (vacoptions == VACUUM_OPTION_NO_PARALLEL ||
3757  continue;
3758 
3759  can_parallel_vacuum[idx] = true;
3760 
3761  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
3762  nindexes_parallel_bulkdel++;
3763  if (((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0) ||
3764  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0))
3765  nindexes_parallel_cleanup++;
3766  }
3767 
3768  nindexes_parallel = Max(nindexes_parallel_bulkdel,
3769  nindexes_parallel_cleanup);
3770 
3771  /* The leader process takes one index */
3772  nindexes_parallel--;
3773 
3774  /* No index supports parallel vacuum */
3775  if (nindexes_parallel <= 0)
3776  return 0;
3777 
3778  /* Compute the parallel degree */
3779  parallel_workers = (nrequested > 0) ?
3780  Min(nrequested, nindexes_parallel) : nindexes_parallel;
3781 
3782  /* Cap by max_parallel_maintenance_workers */
3783  parallel_workers = Min(parallel_workers, max_parallel_maintenance_workers);
3784 
3785  return parallel_workers;
3786 }
uint8 amparallelvacuumoptions
Definition: amapi.h:248
struct IndexAmRoutine * rd_indam
Definition: rel.h:201
Relation * indrels
Definition: vacuumlazy.c:310
#define Min(x, y)
Definition: c.h:986
int nindexes
Definition: vacuumlazy.c:311
unsigned char uint8
Definition: c.h:439
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
bool IsUnderPostmaster
Definition: globals.c:112
int min_parallel_index_scan_size
Definition: allpaths.c:65
#define VACUUM_OPTION_NO_PARALLEL
Definition: vacuum.h:39
int max_parallel_maintenance_workers
Definition: globals.c:127
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:213
#define Max(x, y)
Definition: c.h:980
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:52
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:45
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:60

◆ count_nondeletable_pages()

static BlockNumber count_nondeletable_pages ( LVRelState vacrel)
static

Definition at line 3299 of file vacuumlazy.c.

References AccessExclusiveLock, LVRelState::bstrategy, buf, BUFFER_LOCK_SHARE, BufferGetPage, CHECK_FOR_INTERRUPTS, elevel, ereport, errmsg(), FirstOffsetNumber, INSTR_TIME_GET_MICROSEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SUBTRACT, InvalidBlockNumber, ItemIdIsUsed, LVRelState::lock_waiter_detected, LockBuffer(), LockHasWaitersRelation(), MAIN_FORKNUM, LVRelState::nonempty_pages, OffsetNumberNext, PageGetItemId, PageGetMaxOffsetNumber, PageIsEmpty, PageIsNew, PREFETCH_SIZE, PrefetchBuffer(), RBM_NORMAL, ReadBufferExtended(), LVRelState::rel, LVRelState::rel_pages, LVRelState::relname, StaticAssertStmt, UnlockReleaseBuffer(), and VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL.

Referenced by lazy_truncate_heap().

3300 {
3301  BlockNumber blkno;
3302  BlockNumber prefetchedUntil;
3303  instr_time starttime;
3304 
3305  /* Initialize the starttime if we check for conflicting lock requests */
3306  INSTR_TIME_SET_CURRENT(starttime);
3307 
3308  /*
3309  * Start checking blocks at what we believe relation end to be and move
3310  * backwards. (Strange coding of loop control is needed because blkno is
3311  * unsigned.) To make the scan faster, we prefetch a few blocks at a time
3312  * in forward direction, so that OS-level readahead can kick in.
3313  */
3314  blkno = vacrel->rel_pages;
3316  "prefetch size must be power of 2");
3317  prefetchedUntil = InvalidBlockNumber;
3318  while (blkno > vacrel->nonempty_pages)
3319  {
3320  Buffer buf;
3321  Page page;
3322  OffsetNumber offnum,
3323  maxoff;
3324  bool hastup;
3325 
3326  /*
3327  * Check if another process requests a lock on our relation. We are
3328  * holding an AccessExclusiveLock here, so they will be waiting. We
3329  * only do this once per VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL, and we
3330  * only check if that interval has elapsed once every 32 blocks to
3331  * keep the number of system calls and actual shared lock table
3332  * lookups to a minimum.
3333  */
3334  if ((blkno % 32) == 0)
3335  {
3336  instr_time currenttime;
3337  instr_time elapsed;
3338 
3339  INSTR_TIME_SET_CURRENT(currenttime);
3340  elapsed = currenttime;
3341  INSTR_TIME_SUBTRACT(elapsed, starttime);
3342  if ((INSTR_TIME_GET_MICROSEC(elapsed) / 1000)
3344  {
3346  {
3347  ereport(elevel,
3348  (errmsg("\"%s\": suspending truncate due to conflicting lock request",
3349  vacrel->relname)));
3350 
3351  vacrel->lock_waiter_detected = true;
3352  return blkno;
3353  }
3354  starttime = currenttime;
3355  }
3356  }
3357 
3358  /*
3359  * We don't insert a vacuum delay point here, because we have an
3360  * exclusive lock on the table which we want to hold for as short a
3361  * time as possible. We still need to check for interrupts however.
3362  */
3364 
3365  blkno--;
3366 
3367  /* If we haven't prefetched this lot yet, do so now. */
3368  if (prefetchedUntil > blkno)
3369  {
3370  BlockNumber prefetchStart;
3371  BlockNumber pblkno;
3372 
3373  prefetchStart = blkno & ~(PREFETCH_SIZE - 1);
3374  for (pblkno = prefetchStart; pblkno <= blkno; pblkno++)
3375  {
3376  PrefetchBuffer(vacrel->rel, MAIN_FORKNUM, pblkno);
3378  }
3379  prefetchedUntil = prefetchStart;
3380  }
3381 
3382  buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
3383  vacrel->bstrategy);
3384 
3385  /* In this phase we only need shared access to the buffer */
3387 
3388  page = BufferGetPage(buf);
3389 
3390  if (PageIsNew(page) || PageIsEmpty(page))
3391  {
3392  UnlockReleaseBuffer(buf);
3393  continue;
3394  }
3395 
3396  hastup = false;
3397  maxoff = PageGetMaxOffsetNumber(page);
3398  for (offnum = FirstOffsetNumber;
3399  offnum <= maxoff;
3400  offnum = OffsetNumberNext(offnum))
3401  {
3402  ItemId itemid;
3403 
3404  itemid = PageGetItemId(page, offnum);
3405 
3406  /*
3407  * Note: any non-unused item should be taken as a reason to keep
3408  * this page. We formerly thought that DEAD tuples could be
3409  * thrown away, but that's not so, because we'd not have cleaned
3410  * out their index entries.
3411  */
3412  if (ItemIdIsUsed(itemid))
3413  {
3414  hastup = true;
3415  break; /* can stop scanning */
3416  }
3417  } /* scan along page */
3418 
3419  UnlockReleaseBuffer(buf);
3420 
3421  /* Done scanning if we found a tuple here */
3422  if (hastup)
3423  return blkno + 1;
3424  }
3425 
3426  /*
3427  * If we fall out of the loop, all the previously-thought-to-be-empty
3428  * pages still are; we need not bother to look at the last known-nonempty
3429  * page.
3430  */
3431  return vacrel->nonempty_pages;
3432 }
#define PageIsEmpty(page)
Definition: bufpage.h:222
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:744
struct timeval instr_time
Definition: instr_time.h:150
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
uint32 BlockNumber
Definition: block.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:918
#define PREFETCH_SIZE
Definition: vacuumlazy.c:145
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
static char * buf
Definition: pg_test_fsync.c:68
#define FirstOffsetNumber
Definition: off.h:27
BlockNumber nonempty_pages
Definition: vacuumlazy.c:354
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
static int elevel
Definition: vacuumlazy.c:400
#define VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL
Definition: vacuumlazy.c:102
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#define ereport(elevel,...)
Definition: elog.h:157
bool LockHasWaitersRelation(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:346
char * relname
Definition: vacuumlazy.c:337
bool lock_waiter_detected
Definition: vacuumlazy.c:355
#define INSTR_TIME_GET_MICROSEC(t)
Definition: instr_time.h:205
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define InvalidBlockNumber
Definition: block.h:33
BufferAccessStrategy bstrategy
Definition: vacuumlazy.c:319
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
Relation rel
Definition: vacuumlazy.c:309
#define AccessExclusiveLock
Definition: lockdefs.h:45
BlockNumber rel_pages
Definition: vacuumlazy.c:347
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:909
PrefetchBufferResult PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
Definition: bufmgr.c:587
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:120
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ do_parallel_lazy_cleanup_all_indexes()

static void do_parallel_lazy_cleanup_all_indexes ( LVRelState vacrel)
static

Definition at line 2647 of file vacuumlazy.c.

References do_parallel_vacuum_or_cleanup(), LVShared::estimated_count, LVShared::first_time, LVShared::for_cleanup, LVRelState::lps, LVParallelState::lvshared, LVRelState::new_rel_tuples, LVParallelState::nindexes_parallel_cleanup, LVParallelState::nindexes_parallel_condcleanup, LVRelState::num_index_scans, LVRelState::rel_pages, LVShared::reltuples, and LVRelState::tupcount_pages.

Referenced by lazy_cleanup_all_indexes().

2648 {
2649  int nworkers;
2650 
2651  /*
2652  * If parallel vacuum is active we perform index cleanup with parallel
2653  * workers.
2654  *
2655  * Tell parallel workers to do index cleanup.
2656  */
2657  vacrel->lps->lvshared->for_cleanup = true;
2658  vacrel->lps->lvshared->first_time = (vacrel->num_index_scans == 0);
2659 
2660  /*
2661  * Now we can provide a better estimate of total number of surviving
2662  * tuples (we assume indexes are more interested in that than in the
2663  * number of nominally live tuples).
2664  */
2665  vacrel->lps->lvshared->reltuples = vacrel->new_rel_tuples;
2666  vacrel->lps->lvshared->estimated_count =
2667  (vacrel->tupcount_pages < vacrel->rel_pages);
2668 
2669  /* Determine the number of parallel workers to launch */
2670  if (vacrel->lps->lvshared->first_time)
2671  nworkers = vacrel->lps->nindexes_parallel_cleanup +
2673  else
2674  nworkers = vacrel->lps->nindexes_parallel_cleanup;
2675 
2676  do_parallel_vacuum_or_cleanup(vacrel, nworkers);
2677 }
LVParallelState * lps
Definition: vacuumlazy.c:320
bool estimated_count
Definition: vacuumlazy.c:229
int num_index_scans
Definition: vacuumlazy.c:364
int nindexes_parallel_condcleanup
Definition: vacuumlazy.c:303
bool first_time
Definition: vacuumlazy.c:216
double reltuples
Definition: vacuumlazy.c:228
int nindexes_parallel_cleanup
Definition: vacuumlazy.c:302
double new_rel_tuples
Definition: vacuumlazy.c:358
BlockNumber tupcount_pages
Definition: vacuumlazy.c:351
LVShared * lvshared
Definition: vacuumlazy.c:289
bool for_cleanup
Definition: vacuumlazy.c:215
BlockNumber rel_pages
Definition: vacuumlazy.c:347
static void do_parallel_vacuum_or_cleanup(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:2686

◆ do_parallel_lazy_vacuum_all_indexes()

static void do_parallel_lazy_vacuum_all_indexes ( LVRelState vacrel)
static

Definition at line 2626 of file vacuumlazy.c.

References do_parallel_vacuum_or_cleanup(), LVShared::estimated_count, LVShared::first_time, LVShared::for_cleanup, LVRelState::lps, LVParallelState::lvshared, LVParallelState::nindexes_parallel_bulkdel, LVRelState::old_live_tuples, and LVShared::reltuples.

Referenced by lazy_vacuum_all_indexes().

2627 {
2628  /* Tell parallel workers to do index vacuuming */
2629  vacrel->lps->lvshared->for_cleanup = false;
2630  vacrel->lps->lvshared->first_time = false;
2631 
2632  /*
2633  * We can only provide an approximate value of num_heap_tuples in vacuum
2634  * cases.
2635  */
2636  vacrel->lps->lvshared->reltuples = vacrel->old_live_tuples;
2637  vacrel->lps->lvshared->estimated_count = true;
2638 
2640  vacrel->lps->nindexes_parallel_bulkdel);
2641 }
LVParallelState * lps
Definition: vacuumlazy.c:320
bool estimated_count
Definition: vacuumlazy.c:229
int nindexes_parallel_bulkdel
Definition: vacuumlazy.c:301
bool first_time
Definition: vacuumlazy.c:216
double reltuples
Definition: vacuumlazy.c:228
LVShared * lvshared
Definition: vacuumlazy.c:289
bool for_cleanup
Definition: vacuumlazy.c:215
double old_live_tuples
Definition: vacuumlazy.c:324
static void do_parallel_vacuum_or_cleanup(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:2686

◆ do_parallel_processing()

static void do_parallel_processing ( LVRelState vacrel,
LVShared lvshared 
)
static

Definition at line 2799 of file vacuumlazy.c.

References idx(), LVShared::idx, LVRelState::indrels, LVRelState::indstats, LVRelState::nindexes, parallel_process_one_index(), parallel_processing_is_safe(), parallel_stats_for_idx(), pg_atomic_add_fetch_u32(), pg_atomic_fetch_add_u32(), pg_atomic_sub_fetch_u32(), and VacuumActiveNWorkers.

Referenced by do_parallel_vacuum_or_cleanup(), and parallel_vacuum_main().

2800 {
2801  /*
2802  * Increment the active worker count if we are able to launch any worker.
2803  */
2806 
2807  /* Loop until all indexes are vacuumed */
2808  for (;;)
2809  {
2810  int idx;
2811  LVSharedIndStats *shared_istat;
2812  Relation indrel;
2813  IndexBulkDeleteResult *istat;
2814 
2815  /* Get an index number to process */
2816  idx = pg_atomic_fetch_add_u32(&(lvshared->idx), 1);
2817 
2818  /* Done for all indexes? */
2819  if (idx >= vacrel->nindexes)
2820  break;
2821 
2822  /* Get the index statistics of this index from DSM */
2823  shared_istat = parallel_stats_for_idx(lvshared, idx);
2824 
2825  /* Skip indexes not participating in parallelism */
2826  if (shared_istat == NULL)
2827  continue;
2828 
2829  indrel = vacrel->indrels[idx];
2830 
2831  /*
2832  * Skip processing indexes that are unsafe for workers (these are
2833  * processed in do_serial_processing_for_unsafe_indexes() by leader)
2834  */
2835  if (!parallel_processing_is_safe(indrel, lvshared))
2836  continue;
2837 
2838  /* Do vacuum or cleanup of the index */
2839  istat = (vacrel->indstats[idx]);
2840  vacrel->indstats[idx] = parallel_process_one_index(indrel, istat,
2841  lvshared,
2842  shared_istat,
2843  vacrel);
2844  }
2845 
2846  /*
2847  * We have completed the index vacuum so decrement the active worker
2848  * count.
2849  */
2852 }
Relation * indrels
Definition: vacuumlazy.c:310
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:79
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:401
int nindexes
Definition: vacuumlazy.c:311
static uint32 pg_atomic_add_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:386
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
static IndexBulkDeleteResult * parallel_process_one_index(Relation indrel, IndexBulkDeleteResult *istat, LVShared *lvshared, LVSharedIndStats *shared_indstats, LVRelState *vacrel)
Definition: vacuumlazy.c:2912
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
pg_atomic_uint32 idx
Definition: vacuumlazy.c:260
static bool parallel_processing_is_safe(Relation indrel, LVShared *lvshared)
Definition: vacuumlazy.c:4092
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4068
static uint32 pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:328

◆ do_parallel_vacuum_or_cleanup()

static void do_parallel_vacuum_or_cleanup ( LVRelState vacrel,
int  nworkers 
)
static

Definition at line 2686 of file vacuumlazy.c.

References LVShared::active_nworkers, Assert, LVParallelState::buffer_usage, LVShared::cost_balance, do_parallel_processing(), do_serial_processing_for_unsafe_indexes(), elevel, ereport, errmsg(), LVShared::for_cleanup, i, LVShared::idx, InstrAccumParallelQuery(), IsParallelWorker, LaunchParallelWorkers(), LVRelState::lps, LVParallelState::lvshared, Min, ngettext, LVRelState::nindexes, LVRelState::num_index_scans, ParallelContext::nworkers, ParallelContext::nworkers_launched, ParallelVacuumIsActive, LVParallelState::pcxt, pg_atomic_read_u32(), pg_atomic_write_u32(), ReinitializeParallelDSM(), ReinitializeParallelWorkers(), VacuumActiveNWorkers, VacuumCostBalance, VacuumCostBalanceLocal, VacuumSharedCostBalance, WaitForParallelWorkersToFinish(), and LVParallelState::wal_usage.

Referenced by do_parallel_lazy_cleanup_all_indexes(), and do_parallel_lazy_vacuum_all_indexes().

2687 {
2688  LVParallelState *lps = vacrel->lps;
2689 
2691  Assert(ParallelVacuumIsActive(vacrel));
2692  Assert(vacrel->nindexes > 0);
2693 
2694  /* The leader process will participate */
2695  nworkers--;
2696 
2697  /*
2698  * It is possible that parallel context is initialized with fewer workers
2699  * than the number of indexes that need a separate worker in the current
2700  * phase, so we need to consider it. See compute_parallel_vacuum_workers.
2701  */
2702  nworkers = Min(nworkers, lps->pcxt->nworkers);
2703 
2704  /* Setup the shared cost-based vacuum delay and launch workers */
2705  if (nworkers > 0)
2706  {
2707  if (vacrel->num_index_scans > 0)
2708  {
2709  /* Reset the parallel index processing counter */
2710  pg_atomic_write_u32(&(lps->lvshared->idx), 0);
2711 
2712  /* Reinitialize the parallel context to relaunch parallel workers */
2714  }
2715 
2716  /*
2717  * Set up shared cost balance and the number of active workers for
2718  * vacuum delay. We need to do this before launching workers as
2719  * otherwise, they might not see the updated values for these
2720  * parameters.
2721  */
2724 
2725  /*
2726  * The number of workers can vary between bulkdelete and cleanup
2727  * phase.
2728  */
2729  ReinitializeParallelWorkers(lps->pcxt, nworkers);
2730 
2732 
2733  if (lps->pcxt->nworkers_launched > 0)
2734  {
2735  /*
2736  * Reset the local cost values for leader backend as we have
2737  * already accumulated the remaining balance of heap.
2738  */
2739  VacuumCostBalance = 0;
2741 
2742  /* Enable shared cost balance for leader backend */
2745  }
2746 
2747  if (lps->lvshared->for_cleanup)
2748  ereport(elevel,
2749  (errmsg(ngettext("launched %d parallel vacuum worker for index cleanup (planned: %d)",
2750  "launched %d parallel vacuum workers for index cleanup (planned: %d)",
2751  lps->pcxt->nworkers_launched),
2752  lps->pcxt->nworkers_launched, nworkers)));
2753  else
2754  ereport(elevel,
2755  (errmsg(ngettext("launched %d parallel vacuum worker for index vacuuming (planned: %d)",
2756  "launched %d parallel vacuum workers for index vacuuming (planned: %d)",
2757  lps->pcxt->nworkers_launched),
2758  lps->pcxt->nworkers_launched, nworkers)));
2759  }
2760 
2761  /* Process the indexes that can be processed by only leader process */
2763 
2764  /*
2765  * Join as a parallel worker. The leader process alone processes all the
2766  * indexes in the case where no workers are launched.
2767  */
2768  do_parallel_processing(vacrel, lps->lvshared);
2769 
2770  /*
2771  * Next, accumulate buffer and WAL usage. (This must wait for the workers
2772  * to finish, or we might get incomplete data.)
2773  */
2774  if (nworkers > 0)
2775  {
2776  /* Wait for all vacuum workers to finish */
2778 
2779  for (int i = 0; i < lps->pcxt->nworkers_launched; i++)
2781  }
2782 
2783  /*
2784  * Carry the shared balance value to heap scan and disable shared costing
2785  */
2787  {
2789  VacuumSharedCostBalance = NULL;
2790  VacuumActiveNWorkers = NULL;
2791  }
2792 }
WalUsage * wal_usage
Definition: vacuumlazy.c:295
LVParallelState * lps
Definition: vacuumlazy.c:320
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:79
int VacuumCostBalance
Definition: globals.c:151
int num_index_scans
Definition: vacuumlazy.c:364
BufferUsage * buffer_usage
Definition: vacuumlazy.c:292
#define Min(x, y)
Definition: c.h:986
int nindexes
Definition: vacuumlazy.c:311
void ReinitializeParallelWorkers(ParallelContext *pcxt, int nworkers_to_launch)
Definition: parallel.c:514
ParallelContext * pcxt
Definition: vacuumlazy.c:286
pg_atomic_uint32 cost_balance
Definition: vacuumlazy.c:246
void WaitForParallelWorkersToFinish(ParallelContext *pcxt)
Definition: parallel.c:751
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:162
static void do_serial_processing_for_unsafe_indexes(LVRelState *vacrel, LVShared *lvshared)
Definition: vacuumlazy.c:2859
pg_atomic_uint32 idx
Definition: vacuumlazy.c:260
int nworkers_launched
Definition: parallel.h:38
void LaunchParallelWorkers(ParallelContext *pcxt)
Definition: parallel.c:528
void InstrAccumParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition: instrument.c:218
#define IsParallelWorker()
Definition: parallel.h:61
static void do_parallel_processing(LVRelState *vacrel, LVShared *lvshared)
Definition: vacuumlazy.c:2799
int VacuumCostBalanceLocal
Definition: vacuum.c:80
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:78
static int elevel
Definition: vacuumlazy.c:400
#define ngettext(s, p, n)
Definition: c.h:1182
void ReinitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:464
pg_atomic_uint32 active_nworkers
Definition: vacuumlazy.c:253
#define ereport(elevel,...)
Definition: elog.h:157
#define Assert(condition)
Definition: c.h:804
LVShared * lvshared
Definition: vacuumlazy.c:289
bool for_cleanup
Definition: vacuumlazy.c:215
int errmsg(const char *fmt,...)
Definition: elog.c:909
int i
static void pg_atomic_write_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
Definition: atomics.h:258
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:241

◆ do_serial_processing_for_unsafe_indexes()

static void do_serial_processing_for_unsafe_indexes ( LVRelState vacrel,
LVShared lvshared 
)
static

Definition at line 2859 of file vacuumlazy.c.

References Assert, idx(), LVRelState::indrels, LVRelState::indstats, IsParallelWorker, LVRelState::nindexes, parallel_process_one_index(), parallel_processing_is_safe(), parallel_stats_for_idx(), pg_atomic_add_fetch_u32(), pg_atomic_sub_fetch_u32(), and VacuumActiveNWorkers.

Referenced by do_parallel_vacuum_or_cleanup().

2860 {
2862 
2863  /*
2864  * Increment the active worker count if we are able to launch any worker.
2865  */
2868 
2869  for (int idx = 0; idx < vacrel->nindexes; idx++)
2870  {
2871  LVSharedIndStats *shared_istat;
2872  Relation indrel;
2873  IndexBulkDeleteResult *istat;
2874 
2875  shared_istat = parallel_stats_for_idx(lvshared, idx);
2876 
2877  /* Skip already-complete indexes */
2878  if (shared_istat != NULL)
2879  continue;
2880 
2881  indrel = vacrel->indrels[idx];
2882 
2883  /*
2884  * We're only here for the unsafe indexes
2885  */
2886  if (parallel_processing_is_safe(indrel, lvshared))
2887  continue;
2888 
2889  /* Do vacuum or cleanup of the index */
2890  istat = (vacrel->indstats[idx]);
2891  vacrel->indstats[idx] = parallel_process_one_index(indrel, istat,
2892  lvshared,
2893  shared_istat,
2894  vacrel);
2895  }
2896 
2897  /*
2898  * We have completed the index vacuum so decrement the active worker
2899  * count.
2900  */
2903 }
Relation * indrels
Definition: vacuumlazy.c:310
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:79
static uint32 pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
Definition: atomics.h:401
int nindexes
Definition: vacuumlazy.c:311
static uint32 pg_atomic_add_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 add_)
Definition: atomics.h:386
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
static IndexBulkDeleteResult * parallel_process_one_index(Relation indrel, IndexBulkDeleteResult *istat, LVShared *lvshared, LVSharedIndStats *shared_indstats, LVRelState *vacrel)
Definition: vacuumlazy.c:2912
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
static bool parallel_processing_is_safe(Relation indrel, LVShared *lvshared)
Definition: vacuumlazy.c:4092
#define IsParallelWorker()
Definition: parallel.h:61
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4068
#define Assert(condition)
Definition: c.h:804

◆ end_parallel_vacuum()

static void end_parallel_vacuum ( LVRelState vacrel)
static

Definition at line 4025 of file vacuumlazy.c.

References Assert, DestroyParallelContext(), ExitParallelMode(), idx(), LVRelState::indstats, IsParallelWorker, LVSharedIndStats::istat, LVRelState::lps, LVParallelState::lvshared, LVRelState::nindexes, palloc0(), parallel_stats_for_idx(), LVParallelState::pcxt, pfree(), and LVSharedIndStats::updated.

Referenced by lazy_space_free().

4026 {
4027  IndexBulkDeleteResult **indstats = vacrel->indstats;
4028  LVParallelState *lps = vacrel->lps;
4029  int nindexes = vacrel->nindexes;
4030 
4032 
4033  /* Copy the updated statistics */
4034  for (int idx = 0; idx < nindexes; idx++)
4035  {
4036  LVSharedIndStats *shared_istat;
4037 
4038  shared_istat = parallel_stats_for_idx(lps->lvshared, idx);
4039 
4040  /*
4041  * Skip unused slot. The statistics of this index are already stored
4042  * in local memory.
4043  */
4044  if (shared_istat == NULL)
4045  continue;
4046 
4047  if (shared_istat->updated)
4048  {
4049  indstats[idx] = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
4050  memcpy(indstats[idx], &(shared_istat->istat), sizeof(IndexBulkDeleteResult));
4051  }
4052  else
4053  indstats[idx] = NULL;
4054  }
4055 
4057  ExitParallelMode();
4058 
4059  /* Deactivate parallel vacuum */
4060  pfree(lps);
4061  vacrel->lps = NULL;
4062 }
LVParallelState * lps
Definition: vacuumlazy.c:320
int nindexes
Definition: vacuumlazy.c:311
IndexBulkDeleteResult istat
Definition: vacuumlazy.c:280
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
ParallelContext * pcxt
Definition: vacuumlazy.c:286
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:905
void pfree(void *pointer)
Definition: mcxt.c:1169
void ExitParallelMode(void)
Definition: xact.c:992
#define IsParallelWorker()
Definition: parallel.h:61
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4068
void * palloc0(Size size)
Definition: mcxt.c:1093
#define Assert(condition)
Definition: c.h:804
LVShared * lvshared
Definition: vacuumlazy.c:289

◆ heap_page_is_all_visible()

static bool heap_page_is_all_visible ( LVRelState vacrel,
Buffer  buf,
TransactionId visibility_cutoff_xid,
bool all_frozen 
)
static

Definition at line 3605 of file vacuumlazy.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, elog, ERROR, FirstOffsetNumber, heap_tuple_needs_eventual_freeze(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetXmin, HeapTupleHeaderXminCommitted, HeapTupleSatisfiesVacuum(), InvalidOffsetNumber, InvalidTransactionId, ItemIdGetLength, ItemIdIsDead, ItemIdIsNormal, ItemIdIsRedirected, ItemIdIsUsed, ItemPointerSet, LVRelState::offnum, OffsetNumberNext, LVRelState::OldestXmin, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, LVRelState::rel, RelationGetRelid, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransactionIdFollows(), and TransactionIdPrecedes().

Referenced by lazy_scan_prune(), and lazy_vacuum_heap_page().

3608 {
3609  Page page = BufferGetPage(buf);
3611  OffsetNumber offnum,
3612  maxoff;
3613  bool all_visible = true;
3614 
3615  *visibility_cutoff_xid = InvalidTransactionId;
3616  *all_frozen = true;
3617 
3618  /*
3619  * This is a stripped down version of the line pointer scan in
3620  * lazy_scan_heap(). So if you change anything here, also check that code.
3621  */
3622  maxoff = PageGetMaxOffsetNumber(page);
3623  for (offnum = FirstOffsetNumber;
3624  offnum <= maxoff && all_visible;
3625  offnum = OffsetNumberNext(offnum))
3626  {
3627  ItemId itemid;
3628  HeapTupleData tuple;
3629 
3630  /*
3631  * Set the offset number so that we can display it along with any
3632  * error that occurred while processing this tuple.
3633  */
3634  vacrel->offnum = offnum;
3635  itemid = PageGetItemId(page, offnum);
3636 
3637  /* Unused or redirect line pointers are of no interest */
3638  if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid))
3639  continue;
3640 
3641  ItemPointerSet(&(tuple.t_self), blockno, offnum);
3642 
3643  /*
3644  * Dead line pointers can have index pointers pointing to them. So
3645  * they can't be treated as visible
3646  */
3647  if (ItemIdIsDead(itemid))
3648  {
3649  all_visible = false;
3650  *all_frozen = false;
3651  break;
3652  }
3653 
3654  Assert(ItemIdIsNormal(itemid));
3655 
3656  tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
3657  tuple.t_len = ItemIdGetLength(itemid);
3658  tuple.t_tableOid = RelationGetRelid(vacrel->rel);
3659 
3660  switch (HeapTupleSatisfiesVacuum(&tuple, vacrel->OldestXmin, buf))
3661  {
3662  case HEAPTUPLE_LIVE:
3663  {
3664  TransactionId xmin;
3665 
3666  /* Check comments in lazy_scan_heap. */
3668  {
3669  all_visible = false;
3670  *all_frozen = false;
3671  break;
3672  }
3673 
3674  /*
3675  * The inserter definitely committed. But is it old enough
3676  * that everyone sees it as committed?
3677  */
3678  xmin = HeapTupleHeaderGetXmin(tuple.t_data);
3679  if (!TransactionIdPrecedes(xmin, vacrel->OldestXmin))
3680  {
3681  all_visible = false;
3682  *all_frozen = false;
3683  break;
3684  }
3685 
3686  /* Track newest xmin on page. */
3687  if (TransactionIdFollows(xmin, *visibility_cutoff_xid))
3688  *visibility_cutoff_xid = xmin;
3689 
3690  /* Check whether this tuple is already frozen or not */
3691  if (all_visible && *all_frozen &&
3693  *all_frozen = false;
3694  }
3695  break;
3696 
3697  case HEAPTUPLE_DEAD:
3701  {
3702  all_visible = false;
3703  *all_frozen = false;
3704  break;
3705  }
3706  default:
3707  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
3708  break;
3709  }
3710  } /* scan along page */
3711 
3712  /* Clear the offset information once we have processed the given page. */
3713  vacrel->offnum = InvalidOffsetNumber;
3714 
3715  return all_visible;
3716 }
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:334
uint32 TransactionId
Definition: c.h:587
OffsetNumber offnum
Definition: vacuumlazy.c:340
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
uint32 BlockNumber
Definition: block.h:31
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple)
Definition: heapam.c:7115
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define HeapTupleHeaderXminCommitted(tup)
Definition: htup_details.h:324
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:68
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
TransactionId OldestXmin
Definition: vacuumlazy.c:330
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:804
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
Relation rel
Definition: vacuumlazy.c:309
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2758
#define elog(elevel,...)
Definition: elog.h:232
#define RelationGetRelid(relation)
Definition: rel.h:469
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:127

◆ heap_vacuum_rel()

void heap_vacuum_rel ( Relation  rel,
VacuumParams params,
BufferAccessStrategy  bstrategy 
)

Definition at line 481 of file vacuumlazy.c.

References _, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), ErrorContextCallback::arg, Assert, LVRelState::bstrategy, buf, ErrorContextCallback::callback, StringInfoData::data, DEBUG2, LVRelState::do_failsafe, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, elevel, ereport, errmsg_internal(), error_context_stack, VacuumParams::freeze_min_age, VacuumParams::freeze_table_age, LVRelState::FreezeLimit, LVRelState::frozenskipped_pages, get_database_name(), get_namespace_name(), GetCurrentTimestamp(), i, VacuumParams::index_cleanup, LVRelState::indname, LVRelState::indrels, LVRelState::indstats, INFO, initStringInfo(), InvalidMultiXactId, InvalidOffsetNumber, InvalidTransactionId, VacuumParams::is_wraparound, IsAutoVacuumWorkerProcess(), lazy_scan_heap(), lazy_truncate_heap(), LOG, VacuumParams::log_min_duration, LVRelState::lpdead_item_pages, LVRelState::lpdead_items, Max, VacuumParams::multixact_freeze_min_age, VacuumParams::multixact_freeze_table_age, LVRelState::MultiXactCutoff, MultiXactIdPrecedesOrEquals(), MyDatabaseId, LVRelState::new_dead_tuples, LVRelState::new_live_tuples, LVRelState::new_rel_tuples, LVRelState::nindexes, NoLock, LVRelState::nonempty_pages, LVRelState::num_index_scans, IndexBulkDeleteResult::num_pages, LVRelState::old_live_tuples, LVRelState::old_rel_pages, LVRelState::OldestXmin, VacuumParams::options, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, LVRelState::pages_removed, palloc(), palloc0(), pfree(), pg_rusage_init(), pg_rusage_show(), pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), pgstat_report_vacuum(), pgStatBlockReadTime, pgStatBlockWriteTime, pgWalUsage, LVRelState::phase, LVRelState::pinskipped_pages, ErrorContextCallback::previous, PROGRESS_COMMAND_VACUUM, PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_FINAL_CLEANUP, pstrdup(), RelationData::rd_rel, LVRelState::rel, LVRelState::rel_pages, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, LVRelState::relfrozenxid, LVRelState::relminmxid, LVRelState::relname, LVRelState::relnamespace, RowExclusiveLock, LVRelState::scanned_pages, should_attempt_truncation(), TimestampDifference(), TimestampDifferenceExceeds(), track_io_timing, TransactionIdPrecedesOrEquals(), VacuumParams::truncate, LVRelState::tuples_deleted, update_vacuum_error_info(), vac_close_indexes(), vac_open_indexes(), vac_update_relstats(), VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_TERNARY_DEFAULT, VACOPT_TERNARY_DISABLED, VACOPT_VERBOSE, VACUUM_ERRCB_PHASE_TRUNCATE, VACUUM_ERRCB_PHASE_UNKNOWN, vacuum_error_callback(), vacuum_set_xid_limits(), VacuumPageDirty, VacuumPageHit, VacuumPageMiss, visibilitymap_count(), WalUsage::wal_bytes, WalUsage::wal_fpi, WalUsage::wal_records, and WalUsageAccumDiff().

Referenced by SampleHeapTupleVisible().

483 {
484  LVRelState *vacrel;
485  PGRUsage ru0;
486  TimestampTz starttime = 0;
487  WalUsage walusage_start = pgWalUsage;
488  WalUsage walusage = {0, 0, 0};
489  long secs;
490  int usecs;
491  double read_rate,
492  write_rate;
493  bool aggressive; /* should we scan all unfrozen pages? */
494  bool scanned_all_unfrozen; /* actually scanned all such pages? */
495  char **indnames = NULL;
496  TransactionId xidFullScanLimit;
497  MultiXactId mxactFullScanLimit;
498  BlockNumber new_rel_pages;
499  BlockNumber new_rel_allvisible;
500  double new_live_tuples;
501  TransactionId new_frozen_xid;
502  MultiXactId new_min_multi;
503  ErrorContextCallback errcallback;
504  PgStat_Counter startreadtime = 0;
505  PgStat_Counter startwritetime = 0;
506  TransactionId OldestXmin;
507  TransactionId FreezeLimit;
508  MultiXactId MultiXactCutoff;
509 
510  Assert(params != NULL);
513 
514  /* measure elapsed time iff autovacuum logging requires it */
515  if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
516  {
517  pg_rusage_init(&ru0);
518  starttime = GetCurrentTimestamp();
519  if (track_io_timing)
520  {
521  startreadtime = pgStatBlockReadTime;
522  startwritetime = pgStatBlockWriteTime;
523  }
524  }
525 
526  if (params->options & VACOPT_VERBOSE)
527  elevel = INFO;
528  else
529  elevel = DEBUG2;
530 
532  RelationGetRelid(rel));
533 
535  params->freeze_min_age,
536  params->freeze_table_age,
537  params->multixact_freeze_min_age,
539  &OldestXmin, &FreezeLimit, &xidFullScanLimit,
540  &MultiXactCutoff, &mxactFullScanLimit);
541 
542  /*
543  * We request an aggressive scan if the table's frozen Xid is now older
544  * than or equal to the requested Xid full-table scan limit; or if the
545  * table's minimum MultiXactId is older than or equal to the requested
546  * mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified.
547  */
548  aggressive = TransactionIdPrecedesOrEquals(rel->rd_rel->relfrozenxid,
549  xidFullScanLimit);
550  aggressive |= MultiXactIdPrecedesOrEquals(rel->rd_rel->relminmxid,
551  mxactFullScanLimit);
553  aggressive = true;
554 
555  vacrel = (LVRelState *) palloc0(sizeof(LVRelState));
556 
557  /* Set up high level stuff about rel */
558  vacrel->rel = rel;
559  vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes,
560  &vacrel->indrels);
561  vacrel->do_index_vacuuming = true;
562  vacrel->do_index_cleanup = true;
563  vacrel->do_failsafe = false;
564  if (params->index_cleanup == VACOPT_TERNARY_DISABLED)
565  {
566  vacrel->do_index_vacuuming = false;
567  vacrel->do_index_cleanup = false;
568  }
569  vacrel->bstrategy = bstrategy;
570  vacrel->old_rel_pages = rel->rd_rel->relpages;
571  vacrel->old_live_tuples = rel->rd_rel->reltuples;
572  vacrel->relfrozenxid = rel->rd_rel->relfrozenxid;
573  vacrel->relminmxid = rel->rd_rel->relminmxid;
574 
575  /* Set cutoffs for entire VACUUM */
576  vacrel->OldestXmin = OldestXmin;
577  vacrel->FreezeLimit = FreezeLimit;
578  vacrel->MultiXactCutoff = MultiXactCutoff;
579 
581  vacrel->relname = pstrdup(RelationGetRelationName(rel));
582  vacrel->indname = NULL;
584 
585  /* Save index names iff autovacuum logging requires it */
586  if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0 &&
587  vacrel->nindexes > 0)
588  {
589  indnames = palloc(sizeof(char *) * vacrel->nindexes);
590  for (int i = 0; i < vacrel->nindexes; i++)
591  indnames[i] =
593  }
594 
595  /*
596  * Setup error traceback support for ereport(). The idea is to set up an
597  * error context callback to display additional information on any error
598  * during a vacuum. During different phases of vacuum (heap scan, heap
599  * vacuum, index vacuum, index clean up, heap truncate), we update the
600  * error context callback to display appropriate information.
601  *
602  * Note that the index vacuum and heap vacuum phases may be called
603  * multiple times in the middle of the heap scan phase. So the old phase
604  * information is restored at the end of those phases.
605  */
606  errcallback.callback = vacuum_error_callback;
607  errcallback.arg = vacrel;
608  errcallback.previous = error_context_stack;
609  error_context_stack = &errcallback;
610 
611  /* Do the vacuuming */
612  lazy_scan_heap(vacrel, params, aggressive);
613 
614  /* Done with indexes */
615  vac_close_indexes(vacrel->nindexes, vacrel->indrels, NoLock);
616 
617  /*
618  * Compute whether we actually scanned the all unfrozen pages. If we did,
619  * we can adjust relfrozenxid and relminmxid.
620  *
621  * NB: We need to check this before truncating the relation, because that
622  * will change ->rel_pages.
623  */
624  if ((vacrel->scanned_pages + vacrel->frozenskipped_pages)
625  < vacrel->rel_pages)
626  {
627  Assert(!aggressive);
628  scanned_all_unfrozen = false;
629  }
630  else
631  scanned_all_unfrozen = true;
632 
633  /*
634  * Optionally truncate the relation.
635  */
636  if (should_attempt_truncation(vacrel, params))
637  {
638  /*
639  * Update error traceback information. This is the last phase during
640  * which we add context information to errors, so we don't need to
641  * revert to the previous phase.
642  */
644  vacrel->nonempty_pages,
646  lazy_truncate_heap(vacrel);
647  }
648 
649  /* Pop the error context stack */
650  error_context_stack = errcallback.previous;
651 
652  /* Report that we are now doing final cleanup */
655 
656  /*
657  * Update statistics in pg_class.
658  *
659  * In principle new_live_tuples could be -1 indicating that we (still)
660  * don't know the tuple count. In practice that probably can't happen,
661  * since we'd surely have scanned some pages if the table is new and
662  * nonempty.
663  *
664  * For safety, clamp relallvisible to be not more than what we're setting
665  * relpages to.
666  *
667  * Also, don't change relfrozenxid/relminmxid if we skipped any pages,
668  * since then we don't know for certain that all tuples have a newer xmin.
669  */
670  new_rel_pages = vacrel->rel_pages;
671  new_live_tuples = vacrel->new_live_tuples;
672 
673  visibilitymap_count(rel, &new_rel_allvisible, NULL);
674  if (new_rel_allvisible > new_rel_pages)
675  new_rel_allvisible = new_rel_pages;
676 
677  new_frozen_xid = scanned_all_unfrozen ? FreezeLimit : InvalidTransactionId;
678  new_min_multi = scanned_all_unfrozen ? MultiXactCutoff : InvalidMultiXactId;
679 
681  new_rel_pages,
682  new_live_tuples,
683  new_rel_allvisible,
684  vacrel->nindexes > 0,
685  new_frozen_xid,
686  new_min_multi,
687  false);
688 
689  /*
690  * Report results to the stats collector, too.
691  *
692  * Deliberately avoid telling the stats collector about LP_DEAD items that
693  * remain in the table due to VACUUM bypassing index and heap vacuuming.
694  * ANALYZE will consider the remaining LP_DEAD items to be dead tuples. It
695  * seems like a good idea to err on the side of not vacuuming again too
696  * soon in cases where the failsafe prevented significant amounts of heap
697  * vacuuming.
698  */
700  rel->rd_rel->relisshared,
701  Max(new_live_tuples, 0),
702  vacrel->new_dead_tuples);
704 
705  /* and log the action if appropriate */
706  if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
707  {
708  TimestampTz endtime = GetCurrentTimestamp();
709 
710  if (params->log_min_duration == 0 ||
711  TimestampDifferenceExceeds(starttime, endtime,
712  params->log_min_duration))
713  {
715  char *msgfmt;
716 
717  TimestampDifference(starttime, endtime, &secs, &usecs);
718 
719  memset(&walusage, 0, sizeof(WalUsage));
720  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
721 
722  read_rate = 0;
723  write_rate = 0;
724  if ((secs > 0) || (usecs > 0))
725  {
726  read_rate = (double) BLCKSZ * VacuumPageMiss / (1024 * 1024) /
727  (secs + usecs / 1000000.0);
728  write_rate = (double) BLCKSZ * VacuumPageDirty / (1024 * 1024) /
729  (secs + usecs / 1000000.0);
730  }
731 
732  /*
733  * This is pretty messy, but we split it up so that we can skip
734  * emitting individual parts of the message when not applicable.
735  */
736  initStringInfo(&buf);
737  if (params->is_wraparound)
738  {
739  /*
740  * While it's possible for a VACUUM to be both is_wraparound
741  * and !aggressive, that's just a corner-case -- is_wraparound
742  * implies aggressive. Produce distinct output for the corner
743  * case all the same, just in case.
744  */
745  if (aggressive)
746  msgfmt = _("automatic aggressive vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
747  else
748  msgfmt = _("automatic vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
749  }
750  else
751  {
752  if (aggressive)
753  msgfmt = _("automatic aggressive vacuum of table \"%s.%s.%s\": index scans: %d\n");
754  else
755  msgfmt = _("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n");
756  }
757  appendStringInfo(&buf, msgfmt,
759  vacrel->relnamespace,
760  vacrel->relname,
761  vacrel->num_index_scans);
762  appendStringInfo(&buf, _("pages: %u removed, %u remain, %u skipped due to pins, %u skipped frozen\n"),
763  vacrel->pages_removed,
764  vacrel->rel_pages,
765  vacrel->pinskipped_pages,
766  vacrel->frozenskipped_pages);
767  appendStringInfo(&buf,
768  _("tuples: %lld removed, %lld remain, %lld are dead but not yet removable, oldest xmin: %u\n"),
769  (long long) vacrel->tuples_deleted,
770  (long long) vacrel->new_rel_tuples,
771  (long long) vacrel->new_dead_tuples,
772  OldestXmin);
773  appendStringInfo(&buf,
774  _("buffer usage: %lld hits, %lld misses, %lld dirtied\n"),
775  (long long) VacuumPageHit,
776  (long long) VacuumPageMiss,
777  (long long) VacuumPageDirty);
778  if (vacrel->rel_pages > 0)
779  {
780  BlockNumber orig_rel_pages;
781 
782  if (vacrel->do_index_vacuuming)
783  {
784  msgfmt = _(" %u pages from table (%.2f%% of total) had %lld dead item identifiers removed\n");
785 
786  if (vacrel->nindexes == 0 || vacrel->num_index_scans == 0)
787  appendStringInfo(&buf, _("index scan not needed:"));
788  else
789  appendStringInfo(&buf, _("index scan needed:"));
790  }
791  else
792  {
793  msgfmt = _(" %u pages from table (%.2f%% of total) have %lld dead item identifiers\n");
794 
795  if (!vacrel->do_failsafe)
796  appendStringInfo(&buf, _("index scan bypassed:"));
797  else
798  appendStringInfo(&buf, _("index scan bypassed by failsafe:"));
799  }
800  orig_rel_pages = vacrel->rel_pages + vacrel->pages_removed;
801  appendStringInfo(&buf, msgfmt,
802  vacrel->lpdead_item_pages,
803  100.0 * vacrel->lpdead_item_pages / orig_rel_pages,
804  (long long) vacrel->lpdead_items);
805  }
806  for (int i = 0; i < vacrel->nindexes; i++)
807  {
808  IndexBulkDeleteResult *istat = vacrel->indstats[i];
809 
810  if (!istat)
811  continue;
812 
813  appendStringInfo(&buf,
814  _("index \"%s\": pages: %u in total, %u newly deleted, %u currently deleted, %u reusable\n"),
815  indnames[i],
816  istat->num_pages,
817  istat->pages_newly_deleted,
818  istat->pages_deleted,
819  istat->pages_free);
820  }
821  appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
822  read_rate, write_rate);
823  if (track_io_timing)
824  {
825  appendStringInfoString(&buf, _("I/O Timings:"));
826  if (pgStatBlockReadTime - startreadtime > 0)
827  appendStringInfo(&buf, _(" read=%.3f"),
828  (double) (pgStatBlockReadTime - startreadtime) / 1000);
829  if (pgStatBlockWriteTime - startwritetime > 0)
830  appendStringInfo(&buf, _(" write=%.3f"),
831  (double) (pgStatBlockWriteTime - startwritetime) / 1000);
832  appendStringInfoChar(&buf, '\n');
833  }
834  appendStringInfo(&buf, _("system usage: %s\n"), pg_rusage_show(&ru0));
835  appendStringInfo(&buf,
836  _("WAL usage: %lld records, %lld full page images, %llu bytes"),
837  (long long) walusage.wal_records,
838  (long long) walusage.wal_fpi,
839  (unsigned long long) walusage.wal_bytes);
840 
841  ereport(LOG,
842  (errmsg_internal("%s", buf.data)));
843  pfree(buf.data);
844  }
845  }
846 
847  /* Cleanup index statistics and index names */
848  for (int i = 0; i < vacrel->nindexes; i++)
849  {
850  if (vacrel->indstats[i])
851  pfree(vacrel->indstats[i]);
852 
853  if (indnames && indnames[i])
854  pfree(indnames[i]);
855  }
856 }
VacErrPhase phase
Definition: vacuumlazy.c:341
static void vacuum_error_callback(void *arg)
Definition: vacuumlazy.c:4239
int multixact_freeze_table_age
Definition: vacuum.h:213
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2095
int64 VacuumPageMiss
Definition: globals.c:148
int64 PgStat_Counter
Definition: pgstat.h:91
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4303
Relation * indrels
Definition: vacuumlazy.c:310
bool do_index_vacuuming
Definition: vacuumlazy.c:313
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition: vacuum.h:185
uint32 TransactionId
Definition: c.h:587
int num_index_scans
Definition: vacuumlazy.c:364
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1580
static bool should_attempt_truncation(LVRelState *vacrel, VacuumParams *params)
Definition: vacuumlazy.c:3145
int64 TimestampTz
Definition: timestamp.h:39
WalUsage pgWalUsage
Definition: instrument.c:22
char * pstrdup(const char *in)
Definition: mcxt.c:1299
int64 VacuumPageHit
Definition: globals.c:147
int nindexes
Definition: vacuumlazy.c:311
int64 wal_fpi
Definition: instrument.h:38
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:274
bool do_index_cleanup
Definition: vacuumlazy.c:314
bool do_failsafe
Definition: vacuumlazy.c:316
MultiXactId MultiXactCutoff
Definition: vacuumlazy.c:333
#define INFO
Definition: elog.h:33
void vacuum_set_xid_limits(Relation rel, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, TransactionId *oldestXmin, TransactionId *freezeLimit, TransactionId *xidFullScanLimit, MultiXactId *multiXactCutoff, MultiXactId *mxactFullScanLimit)
Definition: vacuum.c:943
int64 VacuumPageDirty
Definition: globals.c:149
uint32 BlockNumber
Definition: block.h:31
int64 lpdead_items
Definition: vacuumlazy.c:366
void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen)
BlockNumber frozenskipped_pages
Definition: vacuumlazy.c:350
#define LOG
Definition: elog.h:26
Form_pg_class rd_rel
Definition: rel.h:109
double new_live_tuples
Definition: vacuumlazy.c:359
void(* callback)(void *arg)
Definition: elog.h:247
bool TimestampDifferenceExceeds(TimestampTz start_time, TimestampTz stop_time, int msec)
Definition: timestamp.c:1711
struct ErrorContextCallback * previous
Definition: elog.h:246
int freeze_table_age
Definition: vacuum.h:210
void pgstat_progress_end_command(void)
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
ErrorContextCallback * error_context_stack
Definition: elog.c:93
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
bits32 options
Definition: vacuum.h:208
void pfree(void *pointer)
Definition: mcxt.c:1169
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:319
int freeze_min_age
Definition: vacuum.h:209
BlockNumber num_pages
Definition: genam.h:76
static void lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
Definition: vacuumlazy.c:887
BlockNumber pages_free
Definition: genam.h:82
PgStat_Counter pgStatBlockWriteTime
Definition: pgstat.c:247
BlockNumber scanned_pages
Definition: vacuumlazy.c:348
bool is_wraparound
Definition: vacuum.h:215
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2155
#define DEBUG2
Definition: elog.h:24
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3316
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
PgStat_Counter pgStatBlockReadTime
Definition: pgstat.c:246
#define NoLock
Definition: lockdefs.h:34
static char * buf
Definition: pg_test_fsync.c:68
#define RowExclusiveLock
Definition: lockdefs.h:38
BlockNumber nonempty_pages
Definition: vacuumlazy.c:354
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
#define InvalidTransactionId
Definition: transam.h:31
#define RelationGetRelationName(relation)
Definition: rel.h:503
BlockNumber pages_removed
Definition: vacuumlazy.c:352
BlockNumber pages_deleted
Definition: genam.h:81
static void lazy_truncate_heap(LVRelState *vacrel)
Definition: vacuumlazy.c:3169
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3448
BlockNumber pinskipped_pages
Definition: vacuumlazy.c:349
VacOptTernaryValue index_cleanup
Definition: vacuum.h:219
TransactionId FreezeLimit
Definition: vacuumlazy.c:332
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2052
TransactionId OldestXmin
Definition: vacuumlazy.c:330
static int elevel
Definition: vacuumlazy.c:400
char * indname
Definition: vacuumlazy.c:338
void * palloc0(Size size)
Definition: mcxt.c:1093
Oid MyDatabaseId
Definition: globals.c:88
BlockNumber old_rel_pages
Definition: vacuumlazy.c:323
#define InvalidMultiXactId
Definition: multixact.h:24
MultiXactId relminmxid
Definition: vacuumlazy.c:327
#define InvalidOffsetNumber
Definition: off.h:26
int64 tuples_deleted
Definition: vacuumlazy.c:365
VacOptTernaryValue truncate
Definition: vacuum.h:221
#define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP
Definition: progress.h:35
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:337
TransactionId MultiXactId
Definition: c.h:597
int errmsg_internal(const char *fmt,...)
Definition: elog.c:996
TransactionId relfrozenxid
Definition: vacuumlazy.c:326
#define Max(x, y)
Definition: c.h:980
double new_rel_tuples
Definition: vacuumlazy.c:358
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
BlockNumber lpdead_item_pages
Definition: vacuumlazy.c:353
int64 new_dead_tuples
Definition: vacuumlazy.c:367
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
int log_min_duration
Definition: vacuum.h:216
BufferAccessStrategy bstrategy
Definition: vacuumlazy.c:319
#define VACOPT_VERBOSE
Definition: vacuum.h:180
void pgstat_report_vacuum(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples)
Definition: pgstat.c:1567
int64 wal_records
Definition: instrument.h:37
Relation rel
Definition: vacuumlazy.c:309
BlockNumber rel_pages
Definition: vacuumlazy.c:347
void * palloc(Size size)
Definition: mcxt.c:1062
char * relnamespace
Definition: vacuumlazy.c:336
BlockNumber pages_newly_deleted
Definition: genam.h:80
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3170
int i
uint64 wal_bytes
Definition: instrument.h:39
void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition: timestamp.c:1656
void vac_update_relstats(Relation relation, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid, MultiXactId minmulti, bool in_outer_xact)
Definition: vacuum.c:1292
double old_live_tuples
Definition: vacuumlazy.c:324
#define _(x)
Definition: elog.c:89
#define RelationGetRelid(relation)
Definition: rel.h:469
int multixact_freeze_min_age
Definition: vacuum.h:211
bool track_io_timing
Definition: bufmgr.c:135
#define RelationGetNamespace(relation)
Definition: rel.h:510

◆ lazy_check_needs_freeze()

static bool lazy_check_needs_freeze ( Buffer  buf,
bool hastup,
LVRelState vacrel 
)
static

Definition at line 2505 of file vacuumlazy.c.

References BufferGetPage, FirstOffsetNumber, LVRelState::FreezeLimit, heap_tuple_needs_freeze(), InvalidOffsetNumber, ItemIdIsNormal, ItemIdIsUsed, LVRelState::MultiXactCutoff, LVRelState::offnum, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageIsEmpty, and PageIsNew.

Referenced by lazy_scan_heap().

2506 {
2507  Page page = BufferGetPage(buf);
2508  OffsetNumber offnum,
2509  maxoff;
2510  HeapTupleHeader tupleheader;
2511 
2512  *hastup = false;
2513 
2514  /*
2515  * New and empty pages, obviously, don't contain tuples. We could make
2516  * sure that the page is registered in the FSM, but it doesn't seem worth
2517  * waiting for a cleanup lock just for that, especially because it's
2518  * likely that the pin holder will do so.
2519  */
2520  if (PageIsNew(page) || PageIsEmpty(page))
2521  return false;
2522 
2523  maxoff = PageGetMaxOffsetNumber(page);
2524  for (offnum = FirstOffsetNumber;
2525  offnum <= maxoff;
2526  offnum = OffsetNumberNext(offnum))
2527  {
2528  ItemId itemid;
2529 
2530  /*
2531  * Set the offset number so that we can display it along with any
2532  * error that occurred while processing this tuple.
2533  */
2534  vacrel->offnum = offnum;
2535  itemid = PageGetItemId(page, offnum);
2536 
2537  /* this should match hastup test in count_nondeletable_pages() */
2538  if (ItemIdIsUsed(itemid))
2539  *hastup = true;
2540 
2541  /* dead and redirect items never need freezing */
2542  if (!ItemIdIsNormal(itemid))
2543  continue;
2544 
2545  tupleheader = (HeapTupleHeader) PageGetItem(page, itemid);
2546 
2547  if (heap_tuple_needs_freeze(tupleheader, vacrel->FreezeLimit,
2548  vacrel->MultiXactCutoff, buf))
2549  break;
2550  } /* scan along page */
2551 
2552  /* Clear the offset information once we have processed the given page. */
2553  vacrel->offnum = InvalidOffsetNumber;
2554 
2555  return (offnum <= maxoff);
2556 }
#define PageIsEmpty(page)
Definition: bufpage.h:222
OffsetNumber offnum
Definition: vacuumlazy.c:340
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
MultiXactId MultiXactCutoff
Definition: vacuumlazy.c:333
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
static char * buf
Definition: pg_test_fsync.c:68
#define FirstOffsetNumber
Definition: off.h:27
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid, MultiXactId cutoff_multi, Buffer buf)
Definition: heapam.c:7168
TransactionId FreezeLimit
Definition: vacuumlazy.c:332
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidOffsetNumber
Definition: off.h:26
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define PageIsNew(page)
Definition: bufpage.h:229
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ lazy_check_wraparound_failsafe()

static bool lazy_check_wraparound_failsafe ( LVRelState vacrel)
static

Definition at line 2581 of file vacuumlazy.c.

References Assert, LVRelState::do_failsafe, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, ereport, errdetail(), errhint(), errmsg(), FAILSAFE_MIN_PAGES, get_database_name(), MyDatabaseId, LVRelState::num_index_scans, LVRelState::rel_pages, LVRelState::relfrozenxid, LVRelState::relminmxid, LVRelState::relname, LVRelState::relnamespace, unlikely, vacuum_xid_failsafe_check(), VacuumCostActive, VacuumCostBalance, and WARNING.

Referenced by lazy_scan_heap(), and lazy_vacuum_all_indexes().

2582 {
2583  /* Avoid calling vacuum_xid_failsafe_check() very frequently */
2584  if (vacrel->num_index_scans == 0 &&
2585  vacrel->rel_pages <= FAILSAFE_MIN_PAGES)
2586  return false;
2587 
2588  /* Don't warn more than once per VACUUM */
2589  if (vacrel->do_failsafe)
2590  return true;
2591 
2593  vacrel->relminmxid)))
2594  {
2595  Assert(vacrel->do_index_vacuuming);
2596  Assert(vacrel->do_index_cleanup);
2597 
2598  vacrel->do_index_vacuuming = false;
2599  vacrel->do_index_cleanup = false;
2600  vacrel->do_failsafe = true;
2601 
2602  ereport(WARNING,
2603  (errmsg("abandoned index vacuuming of table \"%s.%s.%s\" as a failsafe after %d index scans",
2605  vacrel->relnamespace,
2606  vacrel->relname,
2607  vacrel->num_index_scans),
2608  errdetail("table's relfrozenxid or relminmxid is too far in the past"),
2609  errhint("Consider increasing configuration parameter \"maintenance_work_mem\" or \"autovacuum_work_mem\".\n"
2610  "You might also need to consider other ways for VACUUM to keep up with the allocation of transaction IDs.")));
2611 
2612  /* Stop applying cost limits from this point on */
2613  VacuumCostActive = false;
2614  VacuumCostBalance = 0;
2615 
2616  return true;
2617  }
2618 
2619  return false;
2620 }
int errhint(const char *fmt,...)
Definition: elog.c:1156
int VacuumCostBalance
Definition: globals.c:151
bool do_index_vacuuming
Definition: vacuumlazy.c:313
int num_index_scans
Definition: vacuumlazy.c:364
bool do_index_cleanup
Definition: vacuumlazy.c:314
bool do_failsafe
Definition: vacuumlazy.c:316
#define FAILSAFE_MIN_PAGES
Definition: vacuumlazy.c:116
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2155
int errdetail(const char *fmt,...)
Definition: elog.c:1042
bool vacuum_xid_failsafe_check(TransactionId relfrozenxid, MultiXactId relminmxid)
Definition: vacuum.c:1149
#define WARNING
Definition: elog.h:40
Oid MyDatabaseId
Definition: globals.c:88
MultiXactId relminmxid
Definition: vacuumlazy.c:327
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:337
TransactionId relfrozenxid
Definition: vacuumlazy.c:326
#define Assert(condition)
Definition: c.h:804
BlockNumber rel_pages
Definition: vacuumlazy.c:347
int errmsg(const char *fmt,...)
Definition: elog.c:909
char * relnamespace
Definition: vacuumlazy.c:336
#define unlikely(x)
Definition: c.h:273
bool VacuumCostActive
Definition: globals.c:152

◆ lazy_cleanup_all_indexes()

static void lazy_cleanup_all_indexes ( LVRelState vacrel)
static

Definition at line 2966 of file vacuumlazy.c.

References Assert, do_parallel_lazy_cleanup_all_indexes(), idx(), LVRelState::indrels, LVRelState::indstats, IsParallelWorker, lazy_cleanup_one_index(), LVRelState::new_rel_tuples, LVRelState::nindexes, ParallelVacuumIsActive, pgstat_progress_update_param(), PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_INDEX_CLEANUP, LVRelState::rel_pages, and LVRelState::tupcount_pages.

Referenced by lazy_scan_heap().

2967 {
2969  Assert(vacrel->nindexes > 0);
2970 
2971  /* Report that we are now cleaning up indexes */
2974 
2975  if (!ParallelVacuumIsActive(vacrel))
2976  {
2977  double reltuples = vacrel->new_rel_tuples;
2978  bool estimated_count =
2979  vacrel->tupcount_pages < vacrel->rel_pages;
2980 
2981  for (int idx = 0; idx < vacrel->nindexes; idx++)
2982  {
2983  Relation indrel = vacrel->indrels[idx];
2984  IndexBulkDeleteResult *istat = vacrel->indstats[idx];
2985 
2986  vacrel->indstats[idx] =
2987  lazy_cleanup_one_index(indrel, istat, reltuples,
2988  estimated_count, vacrel);
2989  }
2990  }
2991  else
2992  {
2993  /* Outsource everything to parallel variant */
2995  }
2996 }
Relation * indrels
Definition: vacuumlazy.c:310
int nindexes
Definition: vacuumlazy.c:311
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:162
static IndexBulkDeleteResult * lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, double reltuples, bool estimated_count, LVRelState *vacrel)
Definition: vacuumlazy.c:3065
#define IsParallelWorker()
Definition: parallel.h:61
double new_rel_tuples
Definition: vacuumlazy.c:358
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
#define PROGRESS_VACUUM_PHASE_INDEX_CLEANUP
Definition: progress.h:33
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
BlockNumber tupcount_pages
Definition: vacuumlazy.c:351
BlockNumber rel_pages
Definition: vacuumlazy.c:347
static void do_parallel_lazy_cleanup_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2647

◆ lazy_cleanup_one_index()

static IndexBulkDeleteResult * lazy_cleanup_one_index ( Relation  indrel,
IndexBulkDeleteResult istat,
double  reltuples,
bool  estimated_count,
LVRelState vacrel 
)
static

Definition at line 3065 of file vacuumlazy.c.

References IndexVacuumInfo::analyze_only, Assert, LVRelState::bstrategy, elevel, ereport, errdetail(), errmsg(), IndexVacuumInfo::estimated_count, IndexVacuumInfo::index, index_vacuum_cleanup(), LVRelState::indname, InvalidBlockNumber, InvalidOffsetNumber, IndexVacuumInfo::message_level, IndexVacuumInfo::num_heap_tuples, pfree(), pg_rusage_init(), pg_rusage_show(), pstrdup(), RelationGetRelationName, IndexVacuumInfo::report_progress, restore_vacuum_error_info(), IndexVacuumInfo::strategy, update_vacuum_error_info(), and VACUUM_ERRCB_PHASE_INDEX_CLEANUP.

Referenced by lazy_cleanup_all_indexes(), and parallel_process_one_index().

3068 {
3069  IndexVacuumInfo ivinfo;
3070  PGRUsage ru0;
3071  LVSavedErrInfo saved_err_info;
3072 
3073  pg_rusage_init(&ru0);
3074 
3075  ivinfo.index = indrel;
3076  ivinfo.analyze_only = false;
3077  ivinfo.report_progress = false;
3078  ivinfo.estimated_count = estimated_count;
3079  ivinfo.message_level = elevel;
3080 
3081  ivinfo.num_heap_tuples = reltuples;
3082  ivinfo.strategy = vacrel->bstrategy;
3083 
3084  /*
3085  * Update error traceback information.
3086  *
3087  * The index name is saved during this phase and restored immediately
3088  * after this phase. See vacuum_error_callback.
3089  */
3090  Assert(vacrel->indname == NULL);
3091  vacrel->indname = pstrdup(RelationGetRelationName(indrel));
3092  update_vacuum_error_info(vacrel, &saved_err_info,
3095 
3096  istat = index_vacuum_cleanup(&ivinfo, istat);
3097 
3098  if (istat)
3099  {
3100  ereport(elevel,
3101  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
3102  RelationGetRelationName(indrel),
3103  (istat)->num_index_tuples,
3104  (istat)->num_pages),
3105  errdetail("%.0f index row versions were removed.\n"
3106  "%u index pages were newly deleted.\n"
3107  "%u index pages are currently deleted, of which %u are currently reusable.\n"
3108  "%s.",
3109  (istat)->tuples_removed,
3110  (istat)->pages_newly_deleted,
3111  (istat)->pages_deleted, (istat)->pages_free,
3112  pg_rusage_show(&ru0))));
3113  }
3114 
3115  /* Revert to the previous phase information for error traceback */
3116  restore_vacuum_error_info(vacrel, &saved_err_info);
3117  pfree(vacrel->indname);
3118  vacrel->indname = NULL;
3119 
3120  return istat;
3121 }
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4303
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:712
char * pstrdup(const char *in)
Definition: mcxt.c:1299
bool analyze_only
Definition: genam.h:47
bool report_progress
Definition: genam.h:48
BufferAccessStrategy strategy
Definition: genam.h:52
Relation index
Definition: genam.h:46
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
void pfree(void *pointer)
Definition: mcxt.c:1169
int errdetail(const char *fmt,...)
Definition: elog.c:1042
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
#define RelationGetRelationName(relation)
Definition: rel.h:503
static int elevel
Definition: vacuumlazy.c:400
char * indname
Definition: vacuumlazy.c:338
#define InvalidOffsetNumber
Definition: off.h:26
static void restore_vacuum_error_info(LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel)
Definition: vacuumlazy.c:4322
#define ereport(elevel,...)
Definition: elog.h:157
int message_level
Definition: genam.h:50
double num_heap_tuples
Definition: genam.h:51
#define Assert(condition)
Definition: c.h:804
#define InvalidBlockNumber
Definition: block.h:33
BufferAccessStrategy bstrategy
Definition: vacuumlazy.c:319
int errmsg(const char *fmt,...)
Definition: elog.c:909
bool estimated_count
Definition: genam.h:49

◆ lazy_scan_heap()

static void lazy_scan_heap ( LVRelState vacrel,
VacuumParams params,
bool  aggressive 
)
static

Definition at line 887 of file vacuumlazy.c.

References _, LVPagePruneState::all_frozen, LVPagePruneState::all_visible, appendStringInfo(), Assert, LVRelState::blkno, LVRelState::bstrategy, buf, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage, BufferIsValid, ConditionalLockBufferForCleanup(), StringInfoData::data, LVRelState::dead_tuples, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, elevel, elog, END_CRIT_SECTION, ereport, errdetail_internal(), errmsg(), FORCE_CHECK_PAGE, FreeSpaceMapVacuumRange(), LVRelState::frozenskipped_pages, GetRecordedFreeSpace(), GlobalVisTestFor(), LVPagePruneState::has_lpdead_items, LVPagePruneState::hastup, LVRelState::indstats, initStringInfo(), InvalidBlockNumber, InvalidBuffer, InvalidOffsetNumber, InvalidTransactionId, InvalidXLogRecPtr, lazy_check_needs_freeze(), lazy_check_wraparound_failsafe(), lazy_cleanup_all_indexes(), lazy_scan_prune(), lazy_space_alloc(), lazy_space_free(), lazy_vacuum(), lazy_vacuum_heap_page(), LVRelState::live_tuples, LVRelState::lock_waiter_detected, LockBuffer(), LockBufferForCleanup(), log_newpage_buffer(), LVRelState::lpdead_item_pages, LVRelState::lpdead_items, MAIN_FORKNUM, MarkBufferDirty(), Max, LVDeadTuples::max_tuples, MaxHeapTuplesPerPage, LVRelState::new_dead_tuples, LVRelState::new_live_tuples, LVRelState::new_rel_tuples, ngettext, LVRelState::nindexes, LVRelState::nonempty_pages, LVRelState::num_index_scans, LVDeadTuples::num_tuples, LVRelState::num_tuples, VacuumParams::nworkers, LVRelState::OldestXmin, VacuumParams::options, PageClearAllVisible, PageGetHeapFreeSpace(), PageGetLSN, PageIsAllVisible, PageIsEmpty, PageIsNew, LVRelState::pages_removed, PageSetAllVisible, palloc0(), pfree(), pg_rusage_init(), pg_rusage_show(), pgstat_progress_update_multi_param(), pgstat_progress_update_param(), LVRelState::pinskipped_pages, PROGRESS_VACUUM_HEAP_BLKS_SCANNED, PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, PROGRESS_VACUUM_MAX_DEAD_TUPLES, PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_SCAN_HEAP, PROGRESS_VACUUM_TOTAL_HEAP_BLKS, RBM_NORMAL, ReadBufferExtended(), RecordPageWithFreeSpace(), LVRelState::rel, LVRelState::rel_pages, RelationGetNumberOfBlocks, RelationNeedsWAL, ReleaseBuffer(), LVRelState::relname, LVRelState::relnamespace, LVRelState::scanned_pages, SizeOfPageHeaderData, SKIP_PAGES_THRESHOLD, START_CRIT_SECTION, LVRelState::tupcount_pages, LVRelState::tuples_deleted, UnlockReleaseBuffer(), update_index_statistics(), update_vacuum_error_info(), vac_estimate_reltuples(), VACOPT_DISABLE_PAGE_SKIPPING, vacuum_delay_point(), VACUUM_ERRCB_PHASE_SCAN_HEAP, VACUUM_FSM_EVERY_PAGES, LVPagePruneState::visibility_cutoff_xid, VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_clear(), visibilitymap_get_status(), visibilitymap_pin(), visibilitymap_set(), VISIBILITYMAP_VALID_BITS, VM_ALL_FROZEN, VM_ALL_VISIBLE, and WARNING.

Referenced by heap_vacuum_rel().

888 {
889  LVDeadTuples *dead_tuples;
890  BlockNumber nblocks,
891  blkno,
892  next_unskippable_block,
893  next_fsm_block_to_vacuum;
894  PGRUsage ru0;
895  Buffer vmbuffer = InvalidBuffer;
896  bool skipping_blocks,
897  have_vacuumed_indexes = false;
899  const int initprog_index[] = {
903  };
904  int64 initprog_val[3];
905  GlobalVisState *vistest;
906 
907  pg_rusage_init(&ru0);
908 
909  if (aggressive)
910  ereport(elevel,
911  (errmsg("aggressively vacuuming \"%s.%s\"",
912  vacrel->relnamespace,
913  vacrel->relname)));
914  else
915  ereport(elevel,
916  (errmsg("vacuuming \"%s.%s\"",
917  vacrel->relnamespace,
918  vacrel->relname)));
919 
920  nblocks = RelationGetNumberOfBlocks(vacrel->rel);
921  next_unskippable_block = 0;
922  next_fsm_block_to_vacuum = 0;
923  vacrel->rel_pages = nblocks;
924  vacrel->scanned_pages = 0;
925  vacrel->pinskipped_pages = 0;
926  vacrel->frozenskipped_pages = 0;
927  vacrel->tupcount_pages = 0;
928  vacrel->pages_removed = 0;
929  vacrel->lpdead_item_pages = 0;
930  vacrel->nonempty_pages = 0;
931  vacrel->lock_waiter_detected = false;
932 
933  /* Initialize instrumentation counters */
934  vacrel->num_index_scans = 0;
935  vacrel->tuples_deleted = 0;
936  vacrel->lpdead_items = 0;
937  vacrel->new_dead_tuples = 0;
938  vacrel->num_tuples = 0;
939  vacrel->live_tuples = 0;
940 
941  vistest = GlobalVisTestFor(vacrel->rel);
942 
943  vacrel->indstats = (IndexBulkDeleteResult **)
944  palloc0(vacrel->nindexes * sizeof(IndexBulkDeleteResult *));
945 
946  /*
947  * Before beginning scan, check if it's already necessary to apply
948  * failsafe
949  */
951 
952  /*
953  * Allocate the space for dead tuples. Note that this handles parallel
954  * VACUUM initialization as part of allocating shared memory space used
955  * for dead_tuples.
956  */
957  lazy_space_alloc(vacrel, params->nworkers, nblocks);
958  dead_tuples = vacrel->dead_tuples;
959 
960  /* Report that we're scanning the heap, advertising total # of blocks */
961  initprog_val[0] = PROGRESS_VACUUM_PHASE_SCAN_HEAP;
962  initprog_val[1] = nblocks;
963  initprog_val[2] = dead_tuples->max_tuples;
964  pgstat_progress_update_multi_param(3, initprog_index, initprog_val);
965 
966  /*
967  * Except when aggressive is set, we want to skip pages that are
968  * all-visible according to the visibility map, but only when we can skip
969  * at least SKIP_PAGES_THRESHOLD consecutive pages. Since we're reading
970  * sequentially, the OS should be doing readahead for us, so there's no
971  * gain in skipping a page now and then; that's likely to disable
972  * readahead and so be counterproductive. Also, skipping even a single
973  * page means that we can't update relfrozenxid, so we only want to do it
974  * if we can skip a goodly number of pages.
975  *
976  * When aggressive is set, we can't skip pages just because they are
977  * all-visible, but we can still skip pages that are all-frozen, since
978  * such pages do not need freezing and do not affect the value that we can
979  * safely set for relfrozenxid or relminmxid.
980  *
981  * Before entering the main loop, establish the invariant that
982  * next_unskippable_block is the next block number >= blkno that we can't
983  * skip based on the visibility map, either all-visible for a regular scan
984  * or all-frozen for an aggressive scan. We set it to nblocks if there's
985  * no such block. We also set up the skipping_blocks flag correctly at
986  * this stage.
987  *
988  * Note: The value returned by visibilitymap_get_status could be slightly
989  * out-of-date, since we make this test before reading the corresponding
990  * heap page or locking the buffer. This is OK. If we mistakenly think
991  * that the page is all-visible or all-frozen when in fact the flag's just
992  * been cleared, we might fail to vacuum the page. It's easy to see that
993  * skipping a page when aggressive is not set is not a very big deal; we
994  * might leave some dead tuples lying around, but the next vacuum will
995  * find them. But even when aggressive *is* set, it's still OK if we miss
996  * a page whose all-frozen marking has just been cleared. Any new XIDs
997  * just added to that page are necessarily newer than the GlobalXmin we
998  * computed, so they'll have no effect on the value to which we can safely
999  * set relfrozenxid. A similar argument applies for MXIDs and relminmxid.
1000  *
1001  * We will scan the table's last page, at least to the extent of
1002  * determining whether it has tuples or not, even if it should be skipped
1003  * according to the above rules; except when we've already determined that
1004  * it's not worth trying to truncate the table. This avoids having
1005  * lazy_truncate_heap() take access-exclusive lock on the table to attempt
1006  * a truncation that just fails immediately because there are tuples in
1007  * the last page. This is worth avoiding mainly because such a lock must
1008  * be replayed on any hot standby, where it can be disruptive.
1009  */
1010  if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
1011  {
1012  while (next_unskippable_block < nblocks)
1013  {
1014  uint8 vmstatus;
1015 
1016  vmstatus = visibilitymap_get_status(vacrel->rel,
1017  next_unskippable_block,
1018  &vmbuffer);
1019  if (aggressive)
1020  {
1021  if ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0)
1022  break;
1023  }
1024  else
1025  {
1026  if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0)
1027  break;
1028  }
1030  next_unskippable_block++;
1031  }
1032  }
1033 
1034  if (next_unskippable_block >= SKIP_PAGES_THRESHOLD)
1035  skipping_blocks = true;
1036  else
1037  skipping_blocks = false;
1038 
1039  for (blkno = 0; blkno < nblocks; blkno++)
1040  {
1041  Buffer buf;
1042  Page page;
1043  bool all_visible_according_to_vm = false;
1044  LVPagePruneState prunestate;
1045 
1046  /*
1047  * Consider need to skip blocks. See note above about forcing
1048  * scanning of last page.
1049  */
1050 #define FORCE_CHECK_PAGE() \
1051  (blkno == nblocks - 1 && should_attempt_truncation(vacrel, params))
1052 
1054 
1056  blkno, InvalidOffsetNumber);
1057 
1058  if (blkno == next_unskippable_block)
1059  {
1060  /* Time to advance next_unskippable_block */
1061  next_unskippable_block++;
1062  if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
1063  {
1064  while (next_unskippable_block < nblocks)
1065  {
1066  uint8 vmskipflags;
1067 
1068  vmskipflags = visibilitymap_get_status(vacrel->rel,
1069  next_unskippable_block,
1070  &vmbuffer);
1071  if (aggressive)
1072  {
1073  if ((vmskipflags & VISIBILITYMAP_ALL_FROZEN) == 0)
1074  break;
1075  }
1076  else
1077  {
1078  if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0)
1079  break;
1080  }
1082  next_unskippable_block++;
1083  }
1084  }
1085 
1086  /*
1087  * We know we can't skip the current block. But set up
1088  * skipping_blocks to do the right thing at the following blocks.
1089  */
1090  if (next_unskippable_block - blkno > SKIP_PAGES_THRESHOLD)
1091  skipping_blocks = true;
1092  else
1093  skipping_blocks = false;
1094 
1095  /*
1096  * Normally, the fact that we can't skip this block must mean that
1097  * it's not all-visible. But in an aggressive vacuum we know only
1098  * that it's not all-frozen, so it might still be all-visible.
1099  */
1100  if (aggressive && VM_ALL_VISIBLE(vacrel->rel, blkno, &vmbuffer))
1101  all_visible_according_to_vm = true;
1102  }
1103  else
1104  {
1105  /*
1106  * The current block is potentially skippable; if we've seen a
1107  * long enough run of skippable blocks to justify skipping it, and
1108  * we're not forced to check it, then go ahead and skip.
1109  * Otherwise, the page must be at least all-visible if not
1110  * all-frozen, so we can set all_visible_according_to_vm = true.
1111  */
1112  if (skipping_blocks && !FORCE_CHECK_PAGE())
1113  {
1114  /*
1115  * Tricky, tricky. If this is in aggressive vacuum, the page
1116  * must have been all-frozen at the time we checked whether it
1117  * was skippable, but it might not be any more. We must be
1118  * careful to count it as a skipped all-frozen page in that
1119  * case, or else we'll think we can't update relfrozenxid and
1120  * relminmxid. If it's not an aggressive vacuum, we don't
1121  * know whether it was all-frozen, so we have to recheck; but
1122  * in this case an approximate answer is OK.
1123  */
1124  if (aggressive || VM_ALL_FROZEN(vacrel->rel, blkno, &vmbuffer))
1125  vacrel->frozenskipped_pages++;
1126  continue;
1127  }
1128  all_visible_according_to_vm = true;
1129  }
1130 
1132 
1133  /*
1134  * Consider if we definitely have enough space to process TIDs on page
1135  * already. If we are close to overrunning the available space for
1136  * dead-tuple TIDs, pause and do a cycle of vacuuming before we tackle
1137  * this page.
1138  */
1139  if ((dead_tuples->max_tuples - dead_tuples->num_tuples) < MaxHeapTuplesPerPage &&
1140  dead_tuples->num_tuples > 0)
1141  {
1142  /*
1143  * Before beginning index vacuuming, we release any pin we may
1144  * hold on the visibility map page. This isn't necessary for
1145  * correctness, but we do it anyway to avoid holding the pin
1146  * across a lengthy, unrelated operation.
1147  */
1148  if (BufferIsValid(vmbuffer))
1149  {
1150  ReleaseBuffer(vmbuffer);
1151  vmbuffer = InvalidBuffer;
1152  }
1153 
1154  /* Remove the collected garbage tuples from table and indexes */
1155  lazy_vacuum(vacrel, false);
1156  have_vacuumed_indexes = true;
1157 
1158  /*
1159  * Vacuum the Free Space Map to make newly-freed space visible on
1160  * upper-level FSM pages. Note we have not yet processed blkno.
1161  */
1162  FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
1163  blkno);
1164  next_fsm_block_to_vacuum = blkno;
1165 
1166  /* Report that we are once again scanning the heap */
1169  }
1170 
1171  /*
1172  * Set up visibility map page as needed.
1173  *
1174  * Pin the visibility map page in case we need to mark the page
1175  * all-visible. In most cases this will be very cheap, because we'll
1176  * already have the correct page pinned anyway. However, it's
1177  * possible that (a) next_unskippable_block is covered by a different
1178  * VM page than the current block or (b) we released our pin and did a
1179  * cycle of index vacuuming.
1180  */
1181  visibilitymap_pin(vacrel->rel, blkno, &vmbuffer);
1182 
1183  buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno,
1184  RBM_NORMAL, vacrel->bstrategy);
1185 
1186  /*
1187  * We need buffer cleanup lock so that we can prune HOT chains and
1188  * defragment the page.
1189  */
1191  {
1192  bool hastup;
1193 
1194  /*
1195  * If we're not performing an aggressive scan to guard against XID
1196  * wraparound, and we don't want to forcibly check the page, then
1197  * it's OK to skip vacuuming pages we get a lock conflict on. They
1198  * will be dealt with in some future vacuum.
1199  */
1200  if (!aggressive && !FORCE_CHECK_PAGE())
1201  {
1202  ReleaseBuffer(buf);
1203  vacrel->pinskipped_pages++;
1204  continue;
1205  }
1206 
1207  /*
1208  * Read the page with share lock to see if any xids on it need to
1209  * be frozen. If not we just skip the page, after updating our
1210  * scan statistics. If there are some, we wait for cleanup lock.
1211  *
1212  * We could defer the lock request further by remembering the page
1213  * and coming back to it later, or we could even register
1214  * ourselves for multiple buffers and then service whichever one
1215  * is received first. For now, this seems good enough.
1216  *
1217  * If we get here with aggressive false, then we're just forcibly
1218  * checking the page, and so we don't want to insist on getting
1219  * the lock; we only need to know if the page contains tuples, so
1220  * that we can update nonempty_pages correctly. It's convenient
1221  * to use lazy_check_needs_freeze() for both situations, though.
1222  */
1224  if (!lazy_check_needs_freeze(buf, &hastup, vacrel))
1225  {
1226  UnlockReleaseBuffer(buf);
1227  vacrel->scanned_pages++;
1228  vacrel->pinskipped_pages++;
1229  if (hastup)
1230  vacrel->nonempty_pages = blkno + 1;
1231  continue;
1232  }
1233  if (!aggressive)
1234  {
1235  /*
1236  * Here, we must not advance scanned_pages; that would amount
1237  * to claiming that the page contains no freezable tuples.
1238  */
1239  UnlockReleaseBuffer(buf);
1240  vacrel->pinskipped_pages++;
1241  if (hastup)
1242  vacrel->nonempty_pages = blkno + 1;
1243  continue;
1244  }
1246  LockBufferForCleanup(buf);
1247  /* drop through to normal processing */
1248  }
1249 
1250  /*
1251  * By here we definitely have enough dead_tuples space for whatever
1252  * LP_DEAD tids are on this page, we have the visibility map page set
1253  * up in case we need to set this page's all_visible/all_frozen bit,
1254  * and we have a super-exclusive lock. Any tuples on this page are
1255  * now sure to be "counted" by this VACUUM.
1256  *
1257  * One last piece of preamble needs to take place before we can prune:
1258  * we need to consider new and empty pages.
1259  */
1260  vacrel->scanned_pages++;
1261  vacrel->tupcount_pages++;
1262 
1263  page = BufferGetPage(buf);
1264 
1265  if (PageIsNew(page))
1266  {
1267  /*
1268  * All-zeroes pages can be left over if either a backend extends
1269  * the relation by a single page, but crashes before the newly
1270  * initialized page has been written out, or when bulk-extending
1271  * the relation (which creates a number of empty pages at the tail
1272  * end of the relation, but enters them into the FSM).
1273  *
1274  * Note we do not enter the page into the visibilitymap. That has
1275  * the downside that we repeatedly visit this page in subsequent
1276  * vacuums, but otherwise we'll never not discover the space on a
1277  * promoted standby. The harm of repeated checking ought to
1278  * normally not be too bad - the space usually should be used at
1279  * some point, otherwise there wouldn't be any regular vacuums.
1280  *
1281  * Make sure these pages are in the FSM, to ensure they can be
1282  * reused. Do that by testing if there's any space recorded for
1283  * the page. If not, enter it. We do so after releasing the lock
1284  * on the heap page, the FSM is approximate, after all.
1285  */
1286  UnlockReleaseBuffer(buf);
1287 
1288  if (GetRecordedFreeSpace(vacrel->rel, blkno) == 0)
1289  {
1290  Size freespace = BLCKSZ - SizeOfPageHeaderData;
1291 
1292  RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
1293  }
1294  continue;
1295  }
1296 
1297  if (PageIsEmpty(page))
1298  {
1299  Size freespace = PageGetHeapFreeSpace(page);
1300 
1301  /*
1302  * Empty pages are always all-visible and all-frozen (note that
1303  * the same is currently not true for new pages, see above).
1304  */
1305  if (!PageIsAllVisible(page))
1306  {
1308 
1309  /* mark buffer dirty before writing a WAL record */
1310  MarkBufferDirty(buf);
1311 
1312  /*
1313  * It's possible that another backend has extended the heap,
1314  * initialized the page, and then failed to WAL-log the page
1315  * due to an ERROR. Since heap extension is not WAL-logged,
1316  * recovery might try to replay our record setting the page
1317  * all-visible and find that the page isn't initialized, which
1318  * will cause a PANIC. To prevent that, check whether the
1319  * page has been previously WAL-logged, and if not, do that
1320  * now.
1321  */
1322  if (RelationNeedsWAL(vacrel->rel) &&
1323  PageGetLSN(page) == InvalidXLogRecPtr)
1324  log_newpage_buffer(buf, true);
1325 
1326  PageSetAllVisible(page);
1327  visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
1328  vmbuffer, InvalidTransactionId,
1330  END_CRIT_SECTION();
1331  }
1332 
1333  UnlockReleaseBuffer(buf);
1334  RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
1335  continue;
1336  }
1337 
1338  /*
1339  * Prune and freeze tuples.
1340  *
1341  * Accumulates details of remaining LP_DEAD line pointers on page in
1342  * dead tuple list. This includes LP_DEAD line pointers that we
1343  * pruned ourselves, as well as existing LP_DEAD line pointers that
1344  * were pruned some time earlier. Also considers freezing XIDs in the
1345  * tuple headers of remaining items with storage.
1346  */
1347  lazy_scan_prune(vacrel, buf, blkno, page, vistest, &prunestate);
1348 
1349  Assert(!prunestate.all_visible || !prunestate.has_lpdead_items);
1350 
1351  /* Remember the location of the last page with nonremovable tuples */
1352  if (prunestate.hastup)
1353  vacrel->nonempty_pages = blkno + 1;
1354 
1355  if (vacrel->nindexes == 0)
1356  {
1357  /*
1358  * Consider the need to do page-at-a-time heap vacuuming when
1359  * using the one-pass strategy now.
1360  *
1361  * The one-pass strategy will never call lazy_vacuum(). The steps
1362  * performed here can be thought of as the one-pass equivalent of
1363  * a call to lazy_vacuum().
1364  */
1365  if (prunestate.has_lpdead_items)
1366  {
1367  Size freespace;
1368 
1369  lazy_vacuum_heap_page(vacrel, blkno, buf, 0, &vmbuffer);
1370 
1371  /* Forget the now-vacuumed tuples */
1372  dead_tuples->num_tuples = 0;
1373 
1374  /*
1375  * Periodically perform FSM vacuuming to make newly-freed
1376  * space visible on upper FSM pages. Note we have not yet
1377  * performed FSM processing for blkno.
1378  *
1379  * Call lazy_check_wraparound_failsafe() here, too, since we
1380  * also don't want to do that too frequently, or too
1381  * infrequently.
1382  */
1383  if (blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES)
1384  {
1385  FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
1386  blkno);
1387  next_fsm_block_to_vacuum = blkno;
1389  }
1390 
1391  /*
1392  * Now perform FSM processing for blkno, and move on to next
1393  * page.
1394  *
1395  * Our call to lazy_vacuum_heap_page() will have considered if
1396  * it's possible to set all_visible/all_frozen independently
1397  * of lazy_scan_prune(). Note that prunestate was invalidated
1398  * by lazy_vacuum_heap_page() call.
1399  */
1400  freespace = PageGetHeapFreeSpace(page);
1401 
1402  UnlockReleaseBuffer(buf);
1403  RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
1404  continue;
1405  }
1406 
1407  /*
1408  * There was no call to lazy_vacuum_heap_page() because pruning
1409  * didn't encounter/create any LP_DEAD items that needed to be
1410  * vacuumed. Prune state has not been invalidated, so proceed
1411  * with prunestate-driven visibility map and FSM steps (just like
1412  * the two-pass strategy).
1413  */
1414  Assert(dead_tuples->num_tuples == 0);
1415  }
1416 
1417  /*
1418  * Handle setting visibility map bit based on what the VM said about
1419  * the page before pruning started, and using prunestate
1420  */
1421  if (!all_visible_according_to_vm && prunestate.all_visible)
1422  {
1424 
1425  if (prunestate.all_frozen)
1426  flags |= VISIBILITYMAP_ALL_FROZEN;
1427 
1428  /*
1429  * It should never be the case that the visibility map page is set
1430  * while the page-level bit is clear, but the reverse is allowed
1431  * (if checksums are not enabled). Regardless, set both bits so
1432  * that we get back in sync.
1433  *
1434  * NB: If the heap page is all-visible but the VM bit is not set,
1435  * we don't need to dirty the heap page. However, if checksums
1436  * are enabled, we do need to make sure that the heap page is
1437  * dirtied before passing it to visibilitymap_set(), because it
1438  * may be logged. Given that this situation should only happen in
1439  * rare cases after a crash, it is not worth optimizing.
1440  */
1441  PageSetAllVisible(page);
1442  MarkBufferDirty(buf);
1443  visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
1444  vmbuffer, prunestate.visibility_cutoff_xid,
1445  flags);
1446  }
1447 
1448  /*
1449  * As of PostgreSQL 9.2, the visibility map bit should never be set if
1450  * the page-level bit is clear. However, it's possible that the bit
1451  * got cleared after we checked it and before we took the buffer
1452  * content lock, so we must recheck before jumping to the conclusion
1453  * that something bad has happened.
1454  */
1455  else if (all_visible_according_to_vm && !PageIsAllVisible(page)
1456  && VM_ALL_VISIBLE(vacrel->rel, blkno, &vmbuffer))
1457  {
1458  elog(WARNING, "page is not marked all-visible but visibility map bit is set in relation \"%s\" page %u",
1459  vacrel->relname, blkno);
1460  visibilitymap_clear(vacrel->rel, blkno, vmbuffer,
1462  }
1463 
1464  /*
1465  * It's possible for the value returned by
1466  * GetOldestNonRemovableTransactionId() to move backwards, so it's not
1467  * wrong for us to see tuples that appear to not be visible to
1468  * everyone yet, while PD_ALL_VISIBLE is already set. The real safe
1469  * xmin value never moves backwards, but
1470  * GetOldestNonRemovableTransactionId() is conservative and sometimes
1471  * returns a value that's unnecessarily small, so if we see that
1472  * contradiction it just means that the tuples that we think are not
1473  * visible to everyone yet actually are, and the PD_ALL_VISIBLE flag
1474  * is correct.
1475  *
1476  * There should never be dead tuples on a page with PD_ALL_VISIBLE
1477  * set, however.
1478  */
1479  else if (prunestate.has_lpdead_items && PageIsAllVisible(page))
1480  {
1481  elog(WARNING, "page containing dead tuples is marked as all-visible in relation \"%s\" page %u",
1482  vacrel->relname, blkno);
1483  PageClearAllVisible(page);
1484  MarkBufferDirty(buf);
1485  visibilitymap_clear(vacrel->rel, blkno, vmbuffer,
1487  }
1488 
1489  /*
1490  * If the all-visible page is all-frozen but not marked as such yet,
1491  * mark it as all-frozen. Note that all_frozen is only valid if
1492  * all_visible is true, so we must check both.
1493  */
1494  else if (all_visible_according_to_vm && prunestate.all_visible &&
1495  prunestate.all_frozen &&
1496  !VM_ALL_FROZEN(vacrel->rel, blkno, &vmbuffer))
1497  {
1498  /*
1499  * We can pass InvalidTransactionId as the cutoff XID here,
1500  * because setting the all-frozen bit doesn't cause recovery
1501  * conflicts.
1502  */
1503  visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
1504  vmbuffer, InvalidTransactionId,
1506  }
1507 
1508  /*
1509  * Final steps for block: drop super-exclusive lock, record free space
1510  * in the FSM
1511  */
1512  if (prunestate.has_lpdead_items && vacrel->do_index_vacuuming)
1513  {
1514  /*
1515  * Wait until lazy_vacuum_heap_rel() to save free space. This
1516  * doesn't just save us some cycles; it also allows us to record
1517  * any additional free space that lazy_vacuum_heap_page() will
1518  * make available in cases where it's possible to truncate the
1519  * page's line pointer array.
1520  *
1521  * Note: It's not in fact 100% certain that we really will call
1522  * lazy_vacuum_heap_rel() -- lazy_vacuum() might yet opt to skip
1523  * index vacuuming (and so must skip heap vacuuming). This is
1524  * deemed okay because it only happens in emergencies, or when
1525  * there is very little free space anyway. (Besides, we start
1526  * recording free space in the FSM once index vacuuming has been
1527  * abandoned.)
1528  *
1529  * Note: The one-pass (no indexes) case is only supposed to make
1530  * it this far when there were no LP_DEAD items during pruning.
1531  */
1532  Assert(vacrel->nindexes > 0);
1533  UnlockReleaseBuffer(buf);
1534  }
1535  else
1536  {
1537  Size freespace = PageGetHeapFreeSpace(page);
1538 
1539  UnlockReleaseBuffer(buf);
1540  RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
1541  }
1542  }
1543 
1544  /* report that everything is now scanned */
1546 
1547  /* Clear the block number information */
1548  vacrel->blkno = InvalidBlockNumber;
1549 
1550  /* now we can compute the new value for pg_class.reltuples */
1551  vacrel->new_live_tuples = vac_estimate_reltuples(vacrel->rel, nblocks,
1552  vacrel->tupcount_pages,
1553  vacrel->live_tuples);
1554 
1555  /*
1556  * Also compute the total number of surviving heap entries. In the
1557  * (unlikely) scenario that new_live_tuples is -1, take it as zero.
1558  */
1559  vacrel->new_rel_tuples =
1560  Max(vacrel->new_live_tuples, 0) + vacrel->new_dead_tuples;
1561 
1562  /*
1563  * Release any remaining pin on visibility map page.
1564  */
1565  if (BufferIsValid(vmbuffer))
1566  {
1567  ReleaseBuffer(vmbuffer);
1568  vmbuffer = InvalidBuffer;
1569  }
1570 
1571  /* If any tuples need to be deleted, perform final vacuum cycle */
1572  if (dead_tuples->num_tuples > 0)
1573  lazy_vacuum(vacrel, !have_vacuumed_indexes);
1574 
1575  /*
1576  * Vacuum the remainder of the Free Space Map. We must do this whether or
1577  * not there were indexes, and whether or not we bypassed index vacuuming.
1578  */
1579  if (blkno > next_fsm_block_to_vacuum)
1580  FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum, blkno);
1581 
1582  /* report all blocks vacuumed */
1584 
1585  /* Do post-vacuum cleanup */
1586  if (vacrel->nindexes > 0 && vacrel->do_index_cleanup)
1587  lazy_cleanup_all_indexes(vacrel);
1588 
1589  /*
1590  * Free resources managed by lazy_space_alloc(). (We must end parallel
1591  * mode/free shared memory before updating index statistics. We cannot
1592  * write while in parallel mode.)
1593  */
1594  lazy_space_free(vacrel);
1595 
1596  /* Update index statistics */
1597  if (vacrel->nindexes > 0 && vacrel->do_index_cleanup)
1598  update_index_statistics(vacrel);
1599 
1600  /*
1601  * If table has no indexes and at least one heap pages was vacuumed, make
1602  * log report that lazy_vacuum_heap_rel would've made had there been
1603  * indexes (having indexes implies using the two pass strategy).
1604  *
1605  * We deliberately don't do this in the case where there are indexes but
1606  * index vacuuming was bypassed. We make a similar report at the point
1607  * that index vacuuming is bypassed, but that's actually quite different
1608  * in one important sense: it shows information about work we _haven't_
1609  * done.
1610  *
1611  * log_autovacuum output does things differently; it consistently presents
1612  * information about LP_DEAD items for the VACUUM as a whole. We always
1613  * report on each round of index and heap vacuuming separately, though.
1614  */
1615  if (vacrel->nindexes == 0 && vacrel->lpdead_item_pages > 0)
1616  ereport(elevel,
1617  (errmsg("\"%s\": removed %lld dead item identifiers in %u pages",
1618  vacrel->relname, (long long) vacrel->lpdead_items,
1619  vacrel->lpdead_item_pages)));
1620 
1621  initStringInfo(&buf);
1622  appendStringInfo(&buf,
1623  _("%lld dead row versions cannot be removed yet, oldest xmin: %u\n"),
1624  (long long) vacrel->new_dead_tuples, vacrel->OldestXmin);
1625  appendStringInfo(&buf, ngettext("%u page removed.\n",
1626  "%u pages removed.\n",
1627  vacrel->pages_removed),
1628  vacrel->pages_removed);
1629  appendStringInfo(&buf, ngettext("Skipped %u page due to buffer pins, ",
1630  "Skipped %u pages due to buffer pins, ",
1631  vacrel->pinskipped_pages),
1632  vacrel->pinskipped_pages);
1633  appendStringInfo(&buf, ngettext("%u frozen page.\n",
1634  "%u frozen pages.\n",
1635  vacrel->frozenskipped_pages),
1636  vacrel->frozenskipped_pages);
1637  appendStringInfo(&buf, _("%s."), pg_rusage_show(&ru0));
1638 
1639  ereport(elevel,
1640  (errmsg("\"%s\": found %lld removable, %lld nonremovable row versions in %u out of %u pages",
1641  vacrel->relname,
1642  (long long) vacrel->tuples_deleted,
1643  (long long) vacrel->num_tuples, vacrel->scanned_pages,
1644  nblocks),
1645  errdetail_internal("%s", buf.data)));
1646  pfree(buf.data);
1647 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
void LockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:4080
#define PROGRESS_VACUUM_HEAP_BLKS_VACUUMED
Definition: progress.h:24
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
static bool lazy_check_wraparound_failsafe(LVRelState *vacrel)
Definition: vacuumlazy.c:2581
#define PageIsEmpty(page)
Definition: bufpage.h:222
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4303
static void lazy_scan_prune(LVRelState *vacrel, Buffer buf, BlockNumber blkno, Page page, GlobalVisState *vistest, LVPagePruneState *prunestate)
Definition: vacuumlazy.c:1670
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1090
double vac_estimate_reltuples(Relation relation, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples)
Definition: vacuum.c:1209
bool do_index_vacuuming
Definition: vacuumlazy.c:313
#define PageIsAllVisible(page)
Definition: bufpage.h:385
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition: vacuum.h:185
int num_index_scans
Definition: vacuumlazy.c:364
void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail)
Definition: freespace.c:181
#define PROGRESS_VACUUM_MAX_DEAD_TUPLES
Definition: progress.h:26
void visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, uint8 flags)
static void lazy_space_alloc(LVRelState *vacrel, int nworkers, BlockNumber relblocks)
Definition: vacuumlazy.c:3470
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:744
int nindexes
Definition: vacuumlazy.c:311
#define END_CRIT_SECTION()
Definition: miscadmin.h:149
#define MaxHeapTuplesPerPage
Definition: htup_details.h:573
#define VM_ALL_FROZEN(r, b, v)
Definition: visibilitymap.h:34
unsigned char uint8
Definition: c.h:439
#define PROGRESS_VACUUM_HEAP_BLKS_SCANNED
Definition: progress.h:23
bool do_index_cleanup
Definition: vacuumlazy.c:314
#define InvalidBuffer
Definition: buf.h:25
static void lazy_vacuum(LVRelState *vacrel, bool onecall)
Definition: vacuumlazy.c:2058
static void update_index_statistics(LVRelState *vacrel)
Definition: vacuumlazy.c:3792
#define PROGRESS_VACUUM_TOTAL_HEAP_BLKS
Definition: progress.h:22
#define START_CRIT_SECTION()
Definition: miscadmin.h:147
int64 live_tuples
Definition: vacuumlazy.c:370
static bool lazy_check_needs_freeze(Buffer buf, bool *hastup, LVRelState *vacrel)
Definition: vacuumlazy.c:2505
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3784
int64 lpdead_items
Definition: vacuumlazy.c:366
BlockNumber frozenskipped_pages
Definition: vacuumlazy.c:350
#define SizeOfPageHeaderData
Definition: bufpage.h:216
double new_live_tuples
Definition: vacuumlazy.c:359
TransactionId visibility_cutoff_xid
Definition: vacuumlazy.c:388
static void lazy_space_free(LVRelState *vacrel)
Definition: vacuumlazy.c:3518
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1069
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
#define VISIBILITYMAP_VALID_BITS
Definition: visibilitymap.h:28
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
#define FORCE_CHECK_PAGE()
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
GlobalVisState * GlobalVisTestFor(Relation rel)
Definition: procarray.c:3963
bits32 options
Definition: vacuum.h:208
void pfree(void *pointer)
Definition: mcxt.c:1169
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
bool visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
#define VACUUM_FSM_EVERY_PAGES
Definition: vacuumlazy.c:125
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3807
bool ConditionalLockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:4257
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:984
BlockNumber scanned_pages
Definition: vacuumlazy.c:348
static char * buf
Definition: pg_test_fsync.c:68
#define PageSetAllVisible(page)
Definition: bufpage.h:387
static int lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer, int tupindex, Buffer *vmbuffer)
Definition: vacuumlazy.c:2383
BlockNumber nonempty_pages
Definition: vacuumlazy.c:354
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
#define InvalidTransactionId
Definition: transam.h:31
BlockNumber pages_removed
Definition: vacuumlazy.c:352
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
BlockNumber pinskipped_pages
Definition: vacuumlazy.c:349
#define SKIP_PAGES_THRESHOLD
Definition: vacuumlazy.c:139
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
#define WARNING
Definition: elog.h:40
TransactionId OldestXmin
Definition: vacuumlazy.c:330
static int elevel
Definition: vacuumlazy.c:400
#define ngettext(s, p, n)
Definition: c.h:1182
Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk)
Definition: freespace.c:230
void * palloc0(Size size)
Definition: mcxt.c:1093
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4023
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:213
#define InvalidOffsetNumber
Definition: off.h:26
int64 tuples_deleted
Definition: vacuumlazy.c:365
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:337
#define Max(x, y)
Definition: c.h:980
double new_rel_tuples
Definition: vacuumlazy.c:358
#define PageClearAllVisible(page)
Definition: bufpage.h:389
bool lock_waiter_detected
Definition: vacuumlazy.c:355
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
BlockNumber lpdead_item_pages
Definition: vacuumlazy.c:353
#define VM_ALL_VISIBLE(r, b, v)
Definition: visibilitymap.h:32
size_t Size
Definition: c.h:540
int64 new_dead_tuples
Definition: vacuumlazy.c:367
static void lazy_cleanup_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2966
#define PROGRESS_VACUUM_PHASE_SCAN_HEAP
Definition: progress.h:30
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
int nworkers
Definition: vacuum.h:229
#define InvalidBlockNumber
Definition: block.h:33
BlockNumber tupcount_pages
Definition: vacuumlazy.c:351
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
BufferAccessStrategy bstrategy
Definition: vacuumlazy.c:319
#define RelationNeedsWAL(relation)
Definition: rel.h:582
#define VISIBILITYMAP_ALL_VISIBLE
Definition: visibilitymap.h:26
#define PageGetLSN(page)
Definition: bufpage.h:366
Relation rel
Definition: vacuumlazy.c:309
BlockNumber blkno
Definition: vacuumlazy.c:339
BlockNumber rel_pages
Definition: vacuumlazy.c:347
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:909
uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *buf)
char * relnamespace
Definition: vacuumlazy.c:336
#define elog(elevel,...)
Definition: elog.h:232
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:97
void vacuum_delay_point(void)
Definition: vacuum.c:2116
int64 num_tuples
Definition: vacuumlazy.c:369
int Buffer
Definition: buf.h:23
#define _(x)
Definition: elog.c:89
void FreeSpaceMapVacuumRange(Relation rel, BlockNumber start, BlockNumber end)
Definition: freespace.c:354
Pointer Page
Definition: bufpage.h:78

◆ lazy_scan_prune()

static void lazy_scan_prune ( LVRelState vacrel,
Buffer  buf,
BlockNumber  blkno,
Page  page,
GlobalVisState vistest,
LVPagePruneState prunestate 
)
static

Definition at line 1670 of file vacuumlazy.c.

References LVPagePruneState::all_frozen, LVPagePruneState::all_visible, Assert, LVRelState::dead_tuples, elog, END_CRIT_SECTION, ERROR, FirstOffsetNumber, LVRelState::FreezeLimit, FrozenTransactionId, LVPagePruneState::has_lpdead_items, LVPagePruneState::hastup, heap_execute_freeze_tuple(), heap_page_is_all_visible(), heap_page_prune(), heap_prepare_freeze_tuple(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetXmin, HeapTupleHeaderXminCommitted, HeapTupleSatisfiesVacuum(), i, InvalidOffsetNumber, InvalidTransactionId, ItemIdGetLength, ItemIdIsDead, ItemIdIsNormal, ItemIdIsRedirected, ItemIdIsUsed, ItemPointerSet, ItemPointerSetBlockNumber, ItemPointerSetOffsetNumber, LVDeadTuples::itemptrs, LVRelState::live_tuples, log_heap_freeze(), LVRelState::lpdead_item_pages, LVRelState::lpdead_items, MarkBufferDirty(), LVDeadTuples::max_tuples, MaxHeapTuplesPerPage, LVRelState::MultiXactCutoff, LVRelState::new_dead_tuples, LVDeadTuples::num_tuples, LVRelState::num_tuples, LVRelState::offnum, xl_heap_freeze_tuple::offset, OffsetNumberNext, LVRelState::OldestXmin, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageSetLSN, pgstat_progress_update_param(), PROGRESS_VACUUM_NUM_DEAD_TUPLES, LVRelState::rel, RelationGetRelid, RelationNeedsWAL, LVRelState::relfrozenxid, LVRelState::relminmxid, START_CRIT_SECTION, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransactionIdFollows(), TransactionIdPrecedes(), LVRelState::tuples_deleted, unlikely, and LVPagePruneState::visibility_cutoff_xid.

Referenced by lazy_scan_heap().

1676 {
1677  Relation rel = vacrel->rel;
1678  OffsetNumber offnum,
1679  maxoff;
1680  ItemId itemid;
1681  HeapTupleData tuple;
1682  HTSV_Result res;
1683  int tuples_deleted,
1684  lpdead_items,
1685  new_dead_tuples,
1686  num_tuples,
1687  live_tuples;
1688  int nfrozen;
1689  OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
1691 
1692  maxoff = PageGetMaxOffsetNumber(page);
1693 
1694 retry:
1695 
1696  /* Initialize (or reset) page-level counters */
1697  tuples_deleted = 0;
1698  lpdead_items = 0;
1699  new_dead_tuples = 0;
1700  num_tuples = 0;
1701  live_tuples = 0;
1702 
1703  /*
1704  * Prune all HOT-update chains in this page.
1705  *
1706  * We count tuples removed by the pruning step as tuples_deleted. Its
1707  * final value can be thought of as the number of tuples that have been
1708  * deleted from the table. It should not be confused with lpdead_items;
1709  * lpdead_items's final value can be thought of as the number of tuples
1710  * that were deleted from indexes.
1711  */
1712  tuples_deleted = heap_page_prune(rel, buf, vistest,
1713  InvalidTransactionId, 0, false,
1714  &vacrel->offnum);
1715 
1716  /*
1717  * Now scan the page to collect LP_DEAD items and check for tuples
1718  * requiring freezing among remaining tuples with storage
1719  */
1720  prunestate->hastup = false;
1721  prunestate->has_lpdead_items = false;
1722  prunestate->all_visible = true;
1723  prunestate->all_frozen = true;
1725  nfrozen = 0;
1726 
1727  for (offnum = FirstOffsetNumber;
1728  offnum <= maxoff;
1729  offnum = OffsetNumberNext(offnum))
1730  {
1731  bool tuple_totally_frozen;
1732 
1733  /*
1734  * Set the offset number so that we can display it along with any
1735  * error that occurred while processing this tuple.
1736  */
1737  vacrel->offnum = offnum;
1738  itemid = PageGetItemId(page, offnum);
1739 
1740  if (!ItemIdIsUsed(itemid))
1741  continue;
1742 
1743  /* Redirect items mustn't be touched */
1744  if (ItemIdIsRedirected(itemid))
1745  {
1746  prunestate->hastup = true; /* page won't be truncatable */
1747  continue;
1748  }
1749 
1750  /*
1751  * LP_DEAD items are processed outside of the loop.
1752  *
1753  * Note that we deliberately don't set hastup=true in the case of an
1754  * LP_DEAD item here, which is not how lazy_check_needs_freeze() or
1755  * count_nondeletable_pages() do it -- they only consider pages empty
1756  * when they only have LP_UNUSED items, which is important for
1757  * correctness.
1758  *
1759  * Our assumption is that any LP_DEAD items we encounter here will
1760  * become LP_UNUSED inside lazy_vacuum_heap_page() before we actually
1761  * call count_nondeletable_pages(). In any case our opinion of
1762  * whether or not a page 'hastup' (which is how our caller sets its
1763  * vacrel->nonempty_pages value) is inherently race-prone. It must be
1764  * treated as advisory/unreliable, so we might as well be slightly
1765  * optimistic.
1766  */
1767  if (ItemIdIsDead(itemid))
1768  {
1769  deadoffsets[lpdead_items++] = offnum;
1770  prunestate->all_visible = false;
1771  prunestate->has_lpdead_items = true;
1772  continue;
1773  }
1774 
1775  Assert(ItemIdIsNormal(itemid));
1776 
1777  ItemPointerSet(&(tuple.t_self), blkno, offnum);
1778  tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
1779  tuple.t_len = ItemIdGetLength(itemid);
1780  tuple.t_tableOid = RelationGetRelid(rel);
1781 
1782  /*
1783  * DEAD tuples are almost always pruned into LP_DEAD line pointers by
1784  * heap_page_prune(), but it's possible that the tuple state changed
1785  * since heap_page_prune() looked. Handle that here by restarting.
1786  * (See comments at the top of function for a full explanation.)
1787  */
1788  res = HeapTupleSatisfiesVacuum(&tuple, vacrel->OldestXmin, buf);
1789 
1790  if (unlikely(res == HEAPTUPLE_DEAD))
1791  goto retry;
1792 
1793  /*
1794  * The criteria for counting a tuple as live in this block need to
1795  * match what analyze.c's acquire_sample_rows() does, otherwise VACUUM
1796  * and ANALYZE may produce wildly different reltuples values, e.g.
1797  * when there are many recently-dead tuples.
1798  *
1799  * The logic here is a bit simpler than acquire_sample_rows(), as
1800  * VACUUM can't run inside a transaction block, which makes some cases
1801  * impossible (e.g. in-progress insert from the same transaction).
1802  *
1803  * We treat LP_DEAD items a little differently, too -- we don't count
1804  * them as dead_tuples at all (we only consider new_dead_tuples). The
1805  * outcome is no different because we assume that any LP_DEAD items we
1806  * encounter here will become LP_UNUSED inside lazy_vacuum_heap_page()
1807  * before we report anything to the stats collector. (Cases where we
1808  * bypass index vacuuming will violate our assumption, but the overall
1809  * impact of that should be negligible.)
1810  */
1811  switch (res)
1812  {
1813  case HEAPTUPLE_LIVE:
1814 
1815  /*
1816  * Count it as live. Not only is this natural, but it's also
1817  * what acquire_sample_rows() does.
1818  */
1819  live_tuples++;
1820 
1821  /*
1822  * Is the tuple definitely visible to all transactions?
1823  *
1824  * NB: Like with per-tuple hint bits, we can't set the
1825  * PD_ALL_VISIBLE flag if the inserter committed
1826  * asynchronously. See SetHintBits for more info. Check that
1827  * the tuple is hinted xmin-committed because of that.
1828  */
1829  if (prunestate->all_visible)
1830  {
1831  TransactionId xmin;
1832 
1834  {
1835  prunestate->all_visible = false;
1836  break;
1837  }
1838 
1839  /*
1840  * The inserter definitely committed. But is it old enough
1841  * that everyone sees it as committed?
1842  */
1843  xmin = HeapTupleHeaderGetXmin(tuple.t_data);
1844  if (!TransactionIdPrecedes(xmin, vacrel->OldestXmin))
1845  {
1846  prunestate->all_visible = false;
1847  break;
1848  }
1849 
1850  /* Track newest xmin on page. */
1851  if (TransactionIdFollows(xmin, prunestate->visibility_cutoff_xid))
1852  prunestate->visibility_cutoff_xid = xmin;
1853  }
1854  break;
1856 
1857  /*
1858  * If tuple is recently deleted then we must not remove it
1859  * from relation. (We only remove items that are LP_DEAD from
1860  * pruning.)
1861  */
1862  new_dead_tuples++;
1863  prunestate->all_visible = false;
1864  break;
1866 
1867  /*
1868  * We do not count these rows as live, because we expect the
1869  * inserting transaction to update the counters at commit, and
1870  * we assume that will happen only after we report our
1871  * results. This assumption is a bit shaky, but it is what
1872  * acquire_sample_rows() does, so be consistent.
1873  */
1874  prunestate->all_visible = false;
1875  break;
1877  /* This is an expected case during concurrent vacuum */
1878  prunestate->all_visible = false;
1879 
1880  /*
1881  * Count such rows as live. As above, we assume the deleting
1882  * transaction will commit and update the counters after we
1883  * report.
1884  */
1885  live_tuples++;
1886  break;
1887  default:
1888  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1889  break;
1890  }
1891 
1892  /*
1893  * Non-removable tuple (i.e. tuple with storage).
1894  *
1895  * Check tuple left behind after pruning to see if needs to be frozen
1896  * now.
1897  */
1898  num_tuples++;
1899  prunestate->hastup = true;
1901  vacrel->relfrozenxid,
1902  vacrel->relminmxid,
1903  vacrel->FreezeLimit,
1904  vacrel->MultiXactCutoff,
1905  &frozen[nfrozen],
1906  &tuple_totally_frozen))
1907  {
1908  /* Will execute freeze below */
1909  frozen[nfrozen++].offset = offnum;
1910  }
1911 
1912  /*
1913  * If tuple is not frozen (and not about to become frozen) then caller
1914  * had better not go on to set this page's VM bit
1915  */
1916  if (!tuple_totally_frozen)
1917  prunestate->all_frozen = false;
1918  }
1919 
1920  /*
1921  * We have now divided every item on the page into either an LP_DEAD item
1922  * that will need to be vacuumed in indexes later, or a LP_NORMAL tuple
1923  * that remains and needs to be considered for freezing now (LP_UNUSED and
1924  * LP_REDIRECT items also remain, but are of no further interest to us).
1925  */
1926  vacrel->offnum = InvalidOffsetNumber;
1927 
1928  /*
1929  * Consider the need to freeze any items with tuple storage from the page
1930  * first (arbitrary)
1931  */
1932  if (nfrozen > 0)
1933  {
1934  Assert(prunestate->hastup);
1935 
1936  /*
1937  * At least one tuple with storage needs to be frozen -- execute that
1938  * now.
1939  *
1940  * If we need to freeze any tuples we'll mark the buffer dirty, and
1941  * write a WAL record recording the changes. We must log the changes
1942  * to be crash-safe against future truncation of CLOG.
1943  */
1945 
1947 
1948  /* execute collected freezes */
1949  for (int i = 0; i < nfrozen; i++)
1950  {
1951  HeapTupleHeader htup;
1952 
1953  itemid = PageGetItemId(page, frozen[i].offset);
1954  htup = (HeapTupleHeader) PageGetItem(page, itemid);
1955 
1956  heap_execute_freeze_tuple(htup, &frozen[i]);
1957  }
1958 
1959  /* Now WAL-log freezing if necessary */
1960  if (RelationNeedsWAL(vacrel->rel))
1961  {
1962  XLogRecPtr recptr;
1963 
1964  recptr = log_heap_freeze(vacrel->rel, buf, vacrel->FreezeLimit,
1965  frozen, nfrozen);
1966  PageSetLSN(page, recptr);
1967  }
1968 
1969  END_CRIT_SECTION();
1970  }
1971 
1972  /*
1973  * The second pass over the heap can also set visibility map bits, using
1974  * the same approach. This is important when the table frequently has a
1975  * few old LP_DEAD items on each page by the time we get to it (typically
1976  * because past opportunistic pruning operations freed some non-HOT
1977  * tuples).
1978  *
1979  * VACUUM will call heap_page_is_all_visible() during the second pass over
1980  * the heap to determine all_visible and all_frozen for the page -- this
1981  * is a specialized version of the logic from this function. Now that
1982  * we've finished pruning and freezing, make sure that we're in total
1983  * agreement with heap_page_is_all_visible() using an assertion.
1984  */
1985 #ifdef USE_ASSERT_CHECKING
1986  /* Note that all_frozen value does not matter when !all_visible */
1987  if (prunestate->all_visible)
1988  {
1989  TransactionId cutoff;
1990  bool all_frozen;
1991 
1992  if (!heap_page_is_all_visible(vacrel, buf, &cutoff, &all_frozen))
1993  Assert(false);
1994 
1995  Assert(lpdead_items == 0);
1996  Assert(prunestate->all_frozen == all_frozen);
1997 
1998  /*
1999  * It's possible that we froze tuples and made the page's XID cutoff
2000  * (for recovery conflict purposes) FrozenTransactionId. This is okay
2001  * because visibility_cutoff_xid will be logged by our caller in a
2002  * moment.
2003  */
2004  Assert(cutoff == FrozenTransactionId ||
2005  cutoff == prunestate->visibility_cutoff_xid);
2006  }
2007 #endif
2008 
2009  /*
2010  * Now save details of the LP_DEAD items from the page in the dead_tuples
2011  * array. Also record that page has dead items in per-page prunestate.
2012  */
2013  if (lpdead_items > 0)
2014  {
2015  LVDeadTuples *dead_tuples = vacrel->dead_tuples;
2016  ItemPointerData tmp;
2017 
2018  Assert(!prunestate->all_visible);
2019  Assert(prunestate->has_lpdead_items);
2020 
2021  vacrel->lpdead_item_pages++;
2022 
2023  ItemPointerSetBlockNumber(&tmp, blkno);
2024 
2025  for (int i = 0; i < lpdead_items; i++)
2026  {
2027  ItemPointerSetOffsetNumber(&tmp, deadoffsets[i]);
2028  dead_tuples->itemptrs[dead_tuples->num_tuples++] = tmp;
2029  }
2030 
2031  Assert(dead_tuples->num_tuples <= dead_tuples->max_tuples);
2033  dead_tuples->num_tuples);
2034  }
2035 
2036  /* Finally, add page-local counts to whole-VACUUM counts */
2037  vacrel->tuples_deleted += tuples_deleted;
2038  vacrel->lpdead_items += lpdead_items;
2039  vacrel->new_dead_tuples += new_dead_tuples;
2040  vacrel->num_tuples += num_tuples;
2041  vacrel->live_tuples += live_tuples;
2042 }
XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer, TransactionId cutoff_xid, xl_heap_freeze_tuple *tuples, int ntuples)
Definition: heapam.c:8001
OffsetNumber offset
Definition: heapam_xlog.h:327
ItemPointerData itemptrs[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:186
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:334
uint32 TransactionId
Definition: c.h:587
OffsetNumber offnum
Definition: vacuumlazy.c:340
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define END_CRIT_SECTION()
Definition: miscadmin.h:149
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define MaxHeapTuplesPerPage
Definition: htup_details.h:573
#define START_CRIT_SECTION()
Definition: miscadmin.h:147
MultiXactId MultiXactCutoff
Definition: vacuumlazy.c:333
int64 live_tuples
Definition: vacuumlazy.c:370
int64 lpdead_items
Definition: vacuumlazy.c:366
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
TransactionId visibility_cutoff_xid
Definition: vacuumlazy.c:388
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
static bool heap_page_is_all_visible(LVRelState *vacrel, Buffer buf, TransactionId *visibility_cutoff_xid, bool *all_frozen)
Definition: vacuumlazy.c:3605
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:68
bool heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, TransactionId cutoff_xid, TransactionId cutoff_multi, xl_heap_freeze_tuple *frz, bool *totally_frozen_p)
Definition: heapam.c:6470
int heap_page_prune(Relation relation, Buffer buffer, GlobalVisState *vistest, TransactionId old_snap_xmin, TimestampTz old_snap_ts, bool report_stats, OffsetNumber *off_loc)
Definition: pruneheap.c:219
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define HeapTupleHeaderXminCommitted(tup)
Definition: htup_details.h:324
uint32 t_len
Definition: htup.h:64
void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
Definition: heapam.c:6699
static char * buf
Definition: pg_test_fsync.c:68
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidTransactionId
Definition: transam.h:31
Oid t_tableOid
Definition: htup.h:66
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
#define PROGRESS_VACUUM_NUM_DEAD_TUPLES
Definition: progress.h:27
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
TransactionId FreezeLimit
Definition: vacuumlazy.c:332
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
TransactionId OldestXmin
Definition: vacuumlazy.c:330
MultiXactId relminmxid
Definition: vacuumlazy.c:327
#define InvalidOffsetNumber
Definition: off.h:26
int64 tuples_deleted
Definition: vacuumlazy.c:365
TransactionId relfrozenxid
Definition: vacuumlazy.c:326
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
#define FrozenTransactionId
Definition: transam.h:33
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
BlockNumber lpdead_item_pages
Definition: vacuumlazy.c:353
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:313
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
int64 new_dead_tuples
Definition: vacuumlazy.c:367
#define ItemPointerSetBlockNumber(pointer, blockNumber)
Definition: itemptr.h:138
#define RelationNeedsWAL(relation)
Definition: rel.h:582
#define ItemPointerSetOffsetNumber(pointer, offsetNumber)
Definition: itemptr.h:148
Relation rel
Definition: vacuumlazy.c:309
#define elog(elevel,...)
Definition: elog.h:232
int i
#define unlikely(x)
Definition: c.h:273
HTSV_Result
Definition: heapam.h:93
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int64 num_tuples
Definition: vacuumlazy.c:369
#define RelationGetRelid(relation)
Definition: rel.h:469
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:127

◆ lazy_space_alloc()

static void lazy_space_alloc ( LVRelState vacrel,
int  nworkers,
BlockNumber  relblocks 
)
static

Definition at line 3470 of file vacuumlazy.c.

References begin_parallel_vacuum(), compute_max_dead_tuples(), LVRelState::dead_tuples, LVRelState::do_index_vacuuming, ereport, errmsg(), LVRelState::lps, LVDeadTuples::max_tuples, LVRelState::nindexes, LVDeadTuples::num_tuples, palloc(), ParallelVacuumIsActive, LVRelState::rel, RelationUsesLocalBuffers, LVRelState::relname, SizeOfDeadTuples, and WARNING.

Referenced by lazy_scan_heap().

3471 {
3472  LVDeadTuples *dead_tuples;
3473  long maxtuples;
3474 
3475  /*
3476  * Initialize state for a parallel vacuum. As of now, only one worker can
3477  * be used for an index, so we invoke parallelism only if there are at
3478  * least two indexes on a table.
3479  */
3480  if (nworkers >= 0 && vacrel->nindexes > 1 && vacrel->do_index_vacuuming)
3481  {
3482  /*
3483  * Since parallel workers cannot access data in temporary tables, we
3484  * can't perform parallel vacuum on them.
3485  */
3486  if (RelationUsesLocalBuffers(vacrel->rel))
3487  {
3488  /*
3489  * Give warning only if the user explicitly tries to perform a
3490  * parallel vacuum on the temporary table.
3491  */
3492  if (nworkers > 0)
3493  ereport(WARNING,
3494  (errmsg("disabling parallel option of vacuum on \"%s\" --- cannot vacuum temporary tables in parallel",
3495  vacrel->relname)));
3496  }
3497  else
3498  vacrel->lps = begin_parallel_vacuum(vacrel, nblocks, nworkers);
3499 
3500  /* If parallel mode started, we're done */
3501  if (ParallelVacuumIsActive(vacrel))
3502  return;
3503  }
3504 
3505  maxtuples = compute_max_dead_tuples(nblocks, vacrel->nindexes > 0);
3506 
3507  dead_tuples = (LVDeadTuples *) palloc(SizeOfDeadTuples(maxtuples));
3508  dead_tuples->num_tuples = 0;
3509  dead_tuples->max_tuples = (int) maxtuples;
3510 
3511  vacrel->dead_tuples = dead_tuples;
3512 }
LVParallelState * lps
Definition: vacuumlazy.c:320
bool do_index_vacuuming
Definition: vacuumlazy.c:313
#define SizeOfDeadTuples(cnt)
Definition: vacuumlazy.c:191
int nindexes
Definition: vacuumlazy.c:311
static LVParallelState * begin_parallel_vacuum(LVRelState *vacrel, BlockNumber nblocks, int nrequested)
Definition: vacuumlazy.c:3826
static long compute_max_dead_tuples(BlockNumber relblocks, bool hasindex)
Definition: vacuumlazy.c:3438
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:162
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
#define WARNING
Definition: elog.h:40
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:337
#define RelationUsesLocalBuffers(relation)
Definition: rel.h:591
Relation rel
Definition: vacuumlazy.c:309
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909

◆ lazy_space_free()

static void lazy_space_free ( LVRelState vacrel)
static

Definition at line 3518 of file vacuumlazy.c.

References end_parallel_vacuum(), and ParallelVacuumIsActive.

Referenced by lazy_scan_heap().

3519 {
3520  if (!ParallelVacuumIsActive(vacrel))
3521  return;
3522 
3523  /*
3524  * End parallel mode before updating index statistics as we cannot write
3525  * during parallel mode.
3526  */
3527  end_parallel_vacuum(vacrel);
3528 }
static void end_parallel_vacuum(LVRelState *vacrel)
Definition: vacuumlazy.c:4025
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:162

◆ lazy_tid_reaped()

static bool lazy_tid_reaped ( ItemPointer  itemptr,
void *  state 
)
static

Definition at line 3538 of file vacuumlazy.c.

References itemptr_encode(), LVDeadTuples::itemptrs, LVDeadTuples::num_tuples, and vac_cmp_itemptr().

Referenced by lazy_vacuum_one_index().

3539 {
3540  LVDeadTuples *dead_tuples = (LVDeadTuples *) state;
3541  int64 litem,
3542  ritem,
3543  item;
3544  ItemPointer res;
3545 
3546  litem = itemptr_encode(&dead_tuples->itemptrs[0]);
3547  ritem = itemptr_encode(&dead_tuples->itemptrs[dead_tuples->num_tuples - 1]);
3548  item = itemptr_encode(itemptr);
3549 
3550  /*
3551  * Doing a simple bound check before bsearch() is useful to avoid the
3552  * extra cost of bsearch(), especially if dead tuples on the heap are
3553  * concentrated in a certain range. Since this function is called for
3554  * every index tuple, it pays to be really fast.
3555  */
3556  if (item < litem || item > ritem)
3557  return false;
3558 
3559  res = (ItemPointer) bsearch((void *) itemptr,
3560  (void *) dead_tuples->itemptrs,
3561  dead_tuples->num_tuples,
3562  sizeof(ItemPointerData),
3563  vac_cmp_itemptr);
3564 
3565  return (res != NULL);
3566 }
ItemPointerData itemptrs[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:186
ItemPointerData * ItemPointer
Definition: itemptr.h:49
static int64 itemptr_encode(ItemPointer itemptr)
Definition: index.h:185
Definition: regguts.h:317
static int vac_cmp_itemptr(const void *left, const void *right)
Definition: vacuumlazy.c:3572

◆ lazy_truncate_heap()

static void lazy_truncate_heap ( LVRelState vacrel)
static

Definition at line 3169 of file vacuumlazy.c.

References AccessExclusiveLock, LVRelState::blkno, CHECK_FOR_INTERRUPTS, ConditionalLockRelation(), count_nondeletable_pages(), elevel, ereport, errdetail_internal(), errmsg(), LVRelState::lock_waiter_detected, LVRelState::nonempty_pages, LVRelState::pages_removed, pg_rusage_init(), pg_rusage_show(), pg_usleep(), pgstat_progress_update_param(), PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_TRUNCATE, LVRelState::rel, LVRelState::rel_pages, RelationGetNumberOfBlocks, RelationTruncate(), LVRelState::relname, UnlockRelation(), VACUUM_TRUNCATE_LOCK_TIMEOUT, and VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL.

Referenced by heap_vacuum_rel().

3170 {
3171  BlockNumber old_rel_pages = vacrel->rel_pages;
3172  BlockNumber new_rel_pages;
3173  int lock_retry;
3174 
3175  /* Report that we are now truncating */
3178 
3179  /*
3180  * Loop until no more truncating can be done.
3181  */
3182  do
3183  {
3184  PGRUsage ru0;
3185 
3186  pg_rusage_init(&ru0);
3187 
3188  /*
3189  * We need full exclusive lock on the relation in order to do
3190  * truncation. If we can't get it, give up rather than waiting --- we
3191  * don't want to block other backends, and we don't want to deadlock
3192  * (which is quite possible considering we already hold a lower-grade
3193  * lock).
3194  */
3195  vacrel->lock_waiter_detected = false;
3196  lock_retry = 0;
3197  while (true)
3198  {
3200  break;
3201 
3202  /*
3203  * Check for interrupts while trying to (re-)acquire the exclusive
3204  * lock.
3205  */
3207 
3208  if (++lock_retry > (VACUUM_TRUNCATE_LOCK_TIMEOUT /
3210  {
3211  /*
3212  * We failed to establish the lock in the specified number of
3213  * retries. This means we give up truncating.
3214  */
3215  vacrel->lock_waiter_detected = true;
3216  ereport(elevel,
3217  (errmsg("\"%s\": stopping truncate due to conflicting lock request",
3218  vacrel->relname)));
3219  return;
3220  }
3221 
3223  }
3224 
3225  /*
3226  * Now that we have exclusive lock, look to see if the rel has grown
3227  * whilst we were vacuuming with non-exclusive lock. If so, give up;
3228  * the newly added pages presumably contain non-deletable tuples.
3229  */
3230  new_rel_pages = RelationGetNumberOfBlocks(vacrel->rel);
3231  if (new_rel_pages != old_rel_pages)
3232  {
3233  /*
3234  * Note: we intentionally don't update vacrel->rel_pages with the
3235  * new rel size here. If we did, it would amount to assuming that
3236  * the new pages are empty, which is unlikely. Leaving the numbers
3237  * alone amounts to assuming that the new pages have the same
3238  * tuple density as existing ones, which is less unlikely.
3239  */
3241  return;
3242  }
3243 
3244  /*
3245  * Scan backwards from the end to verify that the end pages actually
3246  * contain no tuples. This is *necessary*, not optional, because
3247  * other backends could have added tuples to these pages whilst we
3248  * were vacuuming.
3249  */
3250  new_rel_pages = count_nondeletable_pages(vacrel);
3251  vacrel->blkno = new_rel_pages;
3252 
3253  if (new_rel_pages >= old_rel_pages)
3254  {
3255  /* can't do anything after all */
3257  return;
3258  }
3259 
3260  /*
3261  * Okay to truncate.
3262  */
3263  RelationTruncate(vacrel->rel, new_rel_pages);
3264 
3265  /*
3266  * We can release the exclusive lock as soon as we have truncated.
3267  * Other backends can't safely access the relation until they have
3268  * processed the smgr invalidation that smgrtruncate sent out ... but
3269  * that should happen as part of standard invalidation processing once
3270  * they acquire lock on the relation.
3271  */
3273 
3274  /*
3275  * Update statistics. Here, it *is* correct to adjust rel_pages
3276  * without also touching reltuples, since the tuple count wasn't
3277  * changed by the truncation.
3278  */
3279  vacrel->pages_removed += old_rel_pages - new_rel_pages;
3280  vacrel->rel_pages = new_rel_pages;
3281 
3282  ereport(elevel,
3283  (errmsg("\"%s\": truncated %u to %u pages",
3284  vacrel->relname,
3285  old_rel_pages, new_rel_pages),
3286  errdetail_internal("%s",
3287  pg_rusage_show(&ru0))));
3288  old_rel_pages = new_rel_pages;
3289  } while (new_rel_pages > vacrel->nonempty_pages &&
3290  vacrel->lock_waiter_detected);
3291 }
void UnlockRelation(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:283
#define VACUUM_TRUNCATE_LOCK_TIMEOUT
Definition: vacuumlazy.c:104
uint32 BlockNumber
Definition: block.h:31
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1069
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
static BlockNumber count_nondeletable_pages(LVRelState *vacrel)
Definition: vacuumlazy.c:3299
#define PROGRESS_VACUUM_PHASE_TRUNCATE
Definition: progress.h:34
void pg_usleep(long microsec)
Definition: signal.c:53
BlockNumber nonempty_pages
Definition: vacuumlazy.c:354
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:248
BlockNumber pages_removed
Definition: vacuumlazy.c:352
#define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL
Definition: vacuumlazy.c:103
static int elevel
Definition: vacuumlazy.c:400
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:213
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:337
bool lock_waiter_detected
Definition: vacuumlazy.c:355
void pgstat_progress_update_param(int index, int64 val)
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
Relation rel
Definition: vacuumlazy.c:309
#define AccessExclusiveLock
Definition: lockdefs.h:45
BlockNumber blkno
Definition: vacuumlazy.c:339
BlockNumber rel_pages
Definition: vacuumlazy.c:347
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:120
void RelationTruncate(Relation rel, BlockNumber nblocks)
Definition: storage.c:277

◆ lazy_vacuum()

static void lazy_vacuum ( LVRelState vacrel,
bool  onecall 
)
static

Definition at line 2058 of file vacuumlazy.c.

References Assert, BYPASS_THRESHOLD_PAGES, LVRelState::dead_tuples, LVRelState::do_failsafe, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, elevel, ereport, errmsg(), IsParallelWorker, lazy_vacuum_all_indexes(), lazy_vacuum_heap_rel(), LVRelState::lpdead_item_pages, LVRelState::lpdead_items, MAXDEADTUPLES, LVRelState::nindexes, LVRelState::num_index_scans, LVDeadTuples::num_tuples, LVRelState::rel_pages, and LVRelState::relname.

Referenced by lazy_scan_heap().

2059 {
2060  bool do_bypass_optimization;
2061 
2062  /* Should not end up here with no indexes */
2063  Assert(vacrel->nindexes > 0);
2065  Assert(vacrel->lpdead_item_pages > 0);
2066 
2067  if (!vacrel->do_index_vacuuming)
2068  {
2069  Assert(!vacrel->do_index_cleanup);
2070  vacrel->dead_tuples->num_tuples = 0;
2071  return;
2072  }
2073 
2074  /*
2075  * Consider bypassing index vacuuming (and heap vacuuming) entirely.
2076  *
2077  * We currently only do this in cases where the number of LP_DEAD items
2078  * for the entire VACUUM operation is close to zero. This avoids sharp
2079  * discontinuities in the duration and overhead of successive VACUUM
2080  * operations that run against the same table with a fixed workload.
2081  * Ideally, successive VACUUM operations will behave as if there are
2082  * exactly zero LP_DEAD items in cases where there are close to zero.
2083  *
2084  * This is likely to be helpful with a table that is continually affected
2085  * by UPDATEs that can mostly apply the HOT optimization, but occasionally
2086  * have small aberrations that lead to just a few heap pages retaining
2087  * only one or two LP_DEAD items. This is pretty common; even when the
2088  * DBA goes out of their way to make UPDATEs use HOT, it is practically
2089  * impossible to predict whether HOT will be applied in 100% of cases.
2090  * It's far easier to ensure that 99%+ of all UPDATEs against a table use
2091  * HOT through careful tuning.
2092  */
2093  do_bypass_optimization = false;
2094  if (onecall && vacrel->rel_pages > 0)
2095  {
2096  BlockNumber threshold;
2097 
2098  Assert(vacrel->num_index_scans == 0);
2099  Assert(vacrel->lpdead_items == vacrel->dead_tuples->num_tuples);
2100  Assert(vacrel->do_index_vacuuming);
2101  Assert(vacrel->do_index_cleanup);
2102 
2103  /*
2104  * This crossover point at which we'll start to do index vacuuming is
2105  * expressed as a percentage of the total number of heap pages in the
2106  * table that are known to have at least one LP_DEAD item. This is
2107  * much more important than the total number of LP_DEAD items, since
2108  * it's a proxy for the number of heap pages whose visibility map bits
2109  * cannot be set on account of bypassing index and heap vacuuming.
2110  *
2111  * We apply one further precautionary test: the space currently used
2112  * to store the TIDs (TIDs that now all point to LP_DEAD items) must
2113  * not exceed 32MB. This limits the risk that we will bypass index
2114  * vacuuming again and again until eventually there is a VACUUM whose
2115  * dead_tuples space is not CPU cache resident.
2116  *
2117  * We don't take any special steps to remember the LP_DEAD items (such
2118  * as counting them in new_dead_tuples report to the stats collector)
2119  * when the optimization is applied. Though the accounting used in
2120  * analyze.c's acquire_sample_rows() will recognize the same LP_DEAD
2121  * items as dead rows in its own stats collector report, that's okay.
2122  * The discrepancy should be negligible. If this optimization is ever
2123  * expanded to cover more cases then this may need to be reconsidered.
2124  */
2125  threshold = (double) vacrel->rel_pages * BYPASS_THRESHOLD_PAGES;
2126  do_bypass_optimization =
2127  (vacrel->lpdead_item_pages < threshold &&
2128  vacrel->lpdead_items < MAXDEADTUPLES(32L * 1024L * 1024L));
2129  }
2130 
2131  if (do_bypass_optimization)
2132  {
2133  /*
2134  * There are almost zero TIDs. Behave as if there were precisely
2135  * zero: bypass index vacuuming, but do index cleanup.
2136  *
2137  * We expect that the ongoing VACUUM operation will finish very
2138  * quickly, so there is no point in considering speeding up as a
2139  * failsafe against wraparound failure. (Index cleanup is expected to
2140  * finish very quickly in cases where there were no ambulkdelete()
2141  * calls.)
2142  */
2143  vacrel->do_index_vacuuming = false;
2144  ereport(elevel,
2145  (errmsg("\"%s\": index scan bypassed: %u pages from table (%.2f%% of total) have %lld dead item identifiers",
2146  vacrel->relname, vacrel->rel_pages,
2147  100.0 * vacrel->lpdead_item_pages / vacrel->rel_pages,
2148  (long long) vacrel->lpdead_items)));
2149  }
2150  else if (lazy_vacuum_all_indexes(vacrel))
2151  {
2152  /*
2153  * We successfully completed a round of index vacuuming. Do related
2154  * heap vacuuming now.
2155  */
2156  lazy_vacuum_heap_rel(vacrel);
2157  }
2158  else
2159  {
2160  /*
2161  * Failsafe case.
2162  *
2163  * we attempted index vacuuming, but didn't finish a full round/full
2164  * index scan. This happens when relfrozenxid or relminmxid is too
2165  * far in the past.
2166  *
2167  * From this point on the VACUUM operation will do no further index
2168  * vacuuming or heap vacuuming. This VACUUM operation won't end up
2169  * back here again.
2170  */
2171  Assert(vacrel->do_failsafe);
2172  }
2173 
2174  /*
2175  * Forget the LP_DEAD items that we just vacuumed (or just decided to not
2176  * vacuum)
2177  */
2178  vacrel->dead_tuples->num_tuples = 0;
2179 }
#define BYPASS_THRESHOLD_PAGES
Definition: vacuumlazy.c:110
bool do_index_vacuuming
Definition: vacuumlazy.c:313
int num_index_scans
Definition: vacuumlazy.c:364
int nindexes
Definition: vacuumlazy.c:311
bool do_index_cleanup
Definition: vacuumlazy.c:314
bool do_failsafe
Definition: vacuumlazy.c:316
uint32 BlockNumber
Definition: block.h:31
int64 lpdead_items
Definition: vacuumlazy.c:366
static void lazy_vacuum_heap_rel(LVRelState *vacrel)
Definition: vacuumlazy.c:2287
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
#define IsParallelWorker()
Definition: parallel.h:61
static int elevel
Definition: vacuumlazy.c:400
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:337
#define Assert(condition)
Definition: c.h:804
BlockNumber lpdead_item_pages
Definition: vacuumlazy.c:353
static bool lazy_vacuum_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2190
BlockNumber rel_pages
Definition: vacuumlazy.c:347
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define MAXDEADTUPLES(max_size)
Definition: vacuumlazy.c:194

◆ lazy_vacuum_all_indexes()

static bool lazy_vacuum_all_indexes ( LVRelState vacrel)
static

Definition at line 2190 of file vacuumlazy.c.

References Assert, LVRelState::dead_tuples, LVRelState::do_failsafe, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, do_parallel_lazy_vacuum_all_indexes(), idx(), LVRelState::indrels, LVRelState::indstats, IsParallelWorker, lazy_check_wraparound_failsafe(), lazy_vacuum_one_index(), LVRelState::lpdead_items, MultiXactIdIsValid, LVRelState::nindexes, LVRelState::num_index_scans, LVDeadTuples::num_tuples, LVRelState::old_live_tuples, ParallelVacuumIsActive, pgstat_progress_update_param(), PROGRESS_VACUUM_NUM_INDEX_VACUUMS, PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_VACUUM_INDEX, LVRelState::relfrozenxid, LVRelState::relminmxid, and TransactionIdIsNormal.

Referenced by lazy_vacuum().

2191 {
2192  bool allindexes = true;
2193 
2195  Assert(vacrel->nindexes > 0);
2196  Assert(vacrel->do_index_vacuuming);
2197  Assert(vacrel->do_index_cleanup);
2200 
2201  /* Precheck for XID wraparound emergencies */
2202  if (lazy_check_wraparound_failsafe(vacrel))
2203  {
2204  /* Wraparound emergency -- don't even start an index scan */
2205  return false;
2206  }
2207 
2208  /* Report that we are now vacuuming indexes */
2211 
2212  if (!ParallelVacuumIsActive(vacrel))
2213  {
2214  for (int idx = 0; idx < vacrel->nindexes; idx++)
2215  {
2216  Relation indrel = vacrel->indrels[idx];
2217  IndexBulkDeleteResult *istat = vacrel->indstats[idx];
2218 
2219  vacrel->indstats[idx] =
2220  lazy_vacuum_one_index(indrel, istat, vacrel->old_live_tuples,
2221  vacrel);
2222 
2223  if (lazy_check_wraparound_failsafe(vacrel))
2224  {
2225  /* Wraparound emergency -- end current index scan */
2226  allindexes = false;
2227  break;
2228  }
2229  }
2230  }
2231  else
2232  {
2233  /* Outsource everything to parallel variant */
2235 
2236  /*
2237  * Do a postcheck to consider applying wraparound failsafe now. Note
2238  * that parallel VACUUM only gets the precheck and this postcheck.
2239  */
2240  if (lazy_check_wraparound_failsafe(vacrel))
2241  allindexes = false;
2242  }
2243 
2244  /*
2245  * We delete all LP_DEAD items from the first heap pass in all indexes on
2246  * each call here (except calls where we choose to do the failsafe). This
2247  * makes the next call to lazy_vacuum_heap_rel() safe (except in the event
2248  * of the failsafe triggering, which prevents the next call from taking
2249  * place).
2250  */
2251  Assert(vacrel->num_index_scans > 0 ||
2252  vacrel->dead_tuples->num_tuples == vacrel->lpdead_items);
2253  Assert(allindexes || vacrel->do_failsafe);
2254 
2255  /*
2256  * Increase and report the number of index scans.
2257  *
2258  * We deliberately include the case where we started a round of bulk
2259  * deletes that we weren't able to finish due to the failsafe triggering.
2260  */
2261  vacrel->num_index_scans++;
2263  vacrel->num_index_scans);
2264 
2265  return allindexes;
2266 }
static bool lazy_check_wraparound_failsafe(LVRelState *vacrel)
Definition: vacuumlazy.c:2581
Relation * indrels
Definition: vacuumlazy.c:310
bool do_index_vacuuming
Definition: vacuumlazy.c:313
int num_index_scans
Definition: vacuumlazy.c:364
#define PROGRESS_VACUUM_PHASE_VACUUM_INDEX
Definition: progress.h:31
int nindexes
Definition: vacuumlazy.c:311
bool do_index_cleanup
Definition: vacuumlazy.c:314
bool do_failsafe
Definition: vacuumlazy.c:316
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
int64 lpdead_items
Definition: vacuumlazy.c:366
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:361
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:162
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
#define IsParallelWorker()
Definition: parallel.h:61
static IndexBulkDeleteResult * lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, double reltuples, LVRelState *vacrel)
Definition: vacuumlazy.c:3010
static void do_parallel_lazy_vacuum_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2626
MultiXactId relminmxid
Definition: vacuumlazy.c:327
TransactionId relfrozenxid
Definition: vacuumlazy.c:326
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
#define PROGRESS_VACUUM_NUM_INDEX_VACUUMS
Definition: progress.h:25
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
double old_live_tuples
Definition: vacuumlazy.c:324

◆ lazy_vacuum_heap_page()

static int lazy_vacuum_heap_page ( LVRelState vacrel,
BlockNumber  blkno,
Buffer  buffer,
int  tupindex,
Buffer vmbuffer 
)
static

Definition at line 2383 of file vacuumlazy.c.

References Assert, BufferGetPage, BufferIsValid, LVRelState::dead_tuples, LVRelState::do_index_vacuuming, END_CRIT_SECTION, heap_page_is_all_visible(), InvalidOffsetNumber, InvalidXLogRecPtr, ItemIdHasStorage, ItemIdIsDead, ItemIdSetUnused, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, LVDeadTuples::itemptrs, MarkBufferDirty(), MaxHeapTuplesPerPage, LVRelState::nindexes, LVDeadTuples::num_tuples, xl_heap_vacuum::nunused, PageGetItemId, PageIsAllVisible, PageSetAllVisible, PageSetLSN, PageTruncateLinePointerArray(), pgstat_progress_update_param(), PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, REGBUF_STANDARD, LVRelState::rel, RelationNeedsWAL, restore_vacuum_error_info(), SizeOfHeapVacuum, START_CRIT_SECTION, update_vacuum_error_info(), VACUUM_ERRCB_PHASE_VACUUM_HEAP, VISIBILITYMAP_ALL_FROZEN, VISIBILITYMAP_ALL_VISIBLE, visibilitymap_get_status(), visibilitymap_set(), XLOG_HEAP2_VACUUM, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by lazy_scan_heap(), and lazy_vacuum_heap_rel().

2385 {
2386  LVDeadTuples *dead_tuples = vacrel->dead_tuples;
2387  Page page = BufferGetPage(buffer);
2389  int uncnt = 0;
2390  TransactionId visibility_cutoff_xid;
2391  bool all_frozen;
2392  LVSavedErrInfo saved_err_info;
2393 
2394  Assert(vacrel->nindexes == 0 || vacrel->do_index_vacuuming);
2395 
2397 
2398  /* Update error traceback information */
2399  update_vacuum_error_info(vacrel, &saved_err_info,
2402 
2404 
2405  for (; tupindex < dead_tuples->num_tuples; tupindex++)
2406  {
2407  BlockNumber tblk;
2408  OffsetNumber toff;
2409  ItemId itemid;
2410 
2411  tblk = ItemPointerGetBlockNumber(&dead_tuples->itemptrs[tupindex]);
2412  if (tblk != blkno)
2413  break; /* past end of tuples for this block */
2414  toff = ItemPointerGetOffsetNumber(&dead_tuples->itemptrs[tupindex]);
2415  itemid = PageGetItemId(page, toff);
2416 
2417  Assert(ItemIdIsDead(itemid) && !ItemIdHasStorage(itemid));
2418  ItemIdSetUnused(itemid);
2419  unused[uncnt++] = toff;
2420  }
2421 
2422  Assert(uncnt > 0);
2423 
2424  /* Attempt to truncate line pointer array now */
2426 
2427  /*
2428  * Mark buffer dirty before we write WAL.
2429  */
2430  MarkBufferDirty(buffer);
2431 
2432  /* XLOG stuff */
2433  if (RelationNeedsWAL(vacrel->rel))
2434  {
2435  xl_heap_vacuum xlrec;
2436  XLogRecPtr recptr;
2437 
2438  xlrec.nunused = uncnt;
2439 
2440  XLogBeginInsert();
2441  XLogRegisterData((char *) &xlrec, SizeOfHeapVacuum);
2442 
2443  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
2444  XLogRegisterBufData(0, (char *) unused, uncnt * sizeof(OffsetNumber));
2445 
2446  recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_VACUUM);
2447 
2448  PageSetLSN(page, recptr);
2449  }
2450 
2451  /*
2452  * End critical section, so we safely can do visibility tests (which
2453  * possibly need to perform IO and allocate memory!). If we crash now the
2454  * page (including the corresponding vm bit) might not be marked all
2455  * visible, but that's fine. A later vacuum will fix that.
2456  */
2457  END_CRIT_SECTION();
2458 
2459  /*
2460  * Now that we have removed the LD_DEAD items from the page, once again
2461  * check if the page has become all-visible. The page is already marked
2462  * dirty, exclusively locked, and, if needed, a full page image has been
2463  * emitted.
2464  */
2465  if (heap_page_is_all_visible(vacrel, buffer, &visibility_cutoff_xid,
2466  &all_frozen))
2467  PageSetAllVisible(page);
2468 
2469  /*
2470  * All the changes to the heap page have been done. If the all-visible
2471  * flag is now set, also set the VM all-visible bit (and, if possible, the
2472  * all-frozen bit) unless this has already been done previously.
2473  */
2474  if (PageIsAllVisible(page))
2475  {
2476  uint8 flags = 0;
2477  uint8 vm_status = visibilitymap_get_status(vacrel->rel,
2478  blkno, vmbuffer);
2479 
2480  /* Set the VM all-frozen bit to flag, if needed */
2481  if ((vm_status & VISIBILITYMAP_ALL_VISIBLE) == 0)
2482  flags |= VISIBILITYMAP_ALL_VISIBLE;
2483  if ((vm_status & VISIBILITYMAP_ALL_FROZEN) == 0 && all_frozen)
2484  flags |= VISIBILITYMAP_ALL_FROZEN;
2485 
2486  Assert(BufferIsValid(*vmbuffer));
2487  if (flags != 0)
2488  visibilitymap_set(vacrel->rel, blkno, buffer, InvalidXLogRecPtr,
2489  *vmbuffer, visibility_cutoff_xid, flags);
2490  }
2491 
2492  /* Revert to the previous phase information for error traceback */
2493  restore_vacuum_error_info(vacrel, &saved_err_info);
2494  return tupindex;
2495 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
#define PROGRESS_VACUUM_HEAP_BLKS_VACUUMED
Definition: progress.h:24
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4303
ItemPointerData itemptrs[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:186
bool do_index_vacuuming
Definition: vacuumlazy.c:313
#define PageIsAllVisible(page)
Definition: bufpage.h:385
uint32 TransactionId
Definition: c.h:587
void visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid, uint8 flags)
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1562
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define VISIBILITYMAP_ALL_FROZEN
Definition: visibilitymap.h:27
int nindexes
Definition: vacuumlazy.c:311
#define END_CRIT_SECTION()
Definition: miscadmin.h:149
#define MaxHeapTuplesPerPage
Definition: htup_details.h:573
unsigned char uint8
Definition: c.h:439
#define START_CRIT_SECTION()
Definition: miscadmin.h:147
uint32 BlockNumber
Definition: block.h:31
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
static bool heap_page_is_all_visible(LVRelState *vacrel, Buffer buf, TransactionId *visibility_cutoff_xid, bool *all_frozen)
Definition: vacuumlazy.c:3605
uint16 OffsetNumber
Definition: off.h:24
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:346
#define PageSetAllVisible(page)
Definition: bufpage.h:387
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define XLOG_HEAP2_VACUUM
Definition: heapam_xlog.h:55
#define SizeOfHeapVacuum
Definition: heapam_xlog.h:265
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define InvalidOffsetNumber
Definition: off.h:26
static void restore_vacuum_error_info(LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel)
Definition: vacuumlazy.c:4322
#define ItemIdHasStorage(itemId)
Definition: itemid.h:120
void PageTruncateLinePointerArray(Page page)
Definition: bufpage.c:828
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define RelationNeedsWAL(relation)
Definition: rel.h:582
#define VISIBILITYMAP_ALL_VISIBLE
Definition: visibilitymap.h:26
Relation rel
Definition: vacuumlazy.c:309
uint8 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *buf)
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define ItemIdSetUnused(itemId)
Definition: itemid.h:128
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
Pointer Page
Definition: bufpage.h:78

◆ lazy_vacuum_heap_rel()