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_EVERY_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))
 

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)
 
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)
 
static void lazy_truncate_heap (LVRelState *vacrel)
 
static BlockNumber count_nondeletable_pages (LVRelState *vacrel, bool *lock_waiter_detected)
 
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_EVERY_PAGES

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

Definition at line 115 of file vacuumlazy.c.

Referenced by lazy_scan_heap().

◆ FORCE_CHECK_PAGE

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

Referenced by lazy_scan_heap().

◆ GetSharedIndStats

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

Definition at line 267 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 269 of file vacuumlazy.c.

Referenced by parallel_stats_for_idx().

◆ LAZY_ALLOC_TUPLES

#define LAZY_ALLOC_TUPLES   MaxHeapTuplesPerPage

Definition at line 132 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 193 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 154 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 152 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 153 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 151 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 155 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 144 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 190 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum(), and lazy_space_alloc().

◆ SizeOfLVShared

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

Definition at line 266 of file vacuumlazy.c.

Referenced by begin_parallel_vacuum().

◆ SKIP_PAGES_THRESHOLD

#define SKIP_PAGES_THRESHOLD   ((BlockNumber) 32)

Definition at line 138 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 124 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 164 of file vacuumlazy.c.

Function Documentation

◆ begin_parallel_vacuum()

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

Definition at line 3831 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, LVShared::idx, 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().

3833 {
3834  LVParallelState *lps = NULL;
3835  Relation *indrels = vacrel->indrels;
3836  int nindexes = vacrel->nindexes;
3837  ParallelContext *pcxt;
3838  LVShared *shared;
3839  LVDeadTuples *dead_tuples;
3840  BufferUsage *buffer_usage;
3841  WalUsage *wal_usage;
3842  bool *can_parallel_vacuum;
3843  long maxtuples;
3844  Size est_shared;
3845  Size est_deadtuples;
3846  int nindexes_mwm = 0;
3847  int parallel_workers = 0;
3848  int querylen;
3849 
3850  /*
3851  * A parallel vacuum must be requested and there must be indexes on the
3852  * relation
3853  */
3854  Assert(nrequested >= 0);
3855  Assert(nindexes > 0);
3856 
3857  /*
3858  * Compute the number of parallel vacuum workers to launch
3859  */
3860  can_parallel_vacuum = (bool *) palloc0(sizeof(bool) * nindexes);
3861  parallel_workers = compute_parallel_vacuum_workers(vacrel,
3862  nrequested,
3863  can_parallel_vacuum);
3864 
3865  /* Can't perform vacuum in parallel */
3866  if (parallel_workers <= 0)
3867  {
3868  pfree(can_parallel_vacuum);
3869  return lps;
3870  }
3871 
3872  lps = (LVParallelState *) palloc0(sizeof(LVParallelState));
3873 
3875  pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
3876  parallel_workers);
3877  Assert(pcxt->nworkers > 0);
3878  lps->pcxt = pcxt;
3879 
3880  /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
3881  est_shared = MAXALIGN(add_size(SizeOfLVShared, BITMAPLEN(nindexes)));
3882  for (int idx = 0; idx < nindexes; idx++)
3883  {
3884  Relation indrel = indrels[idx];
3885  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
3886 
3887  /*
3888  * Cleanup option should be either disabled, always performing in
3889  * parallel or conditionally performing in parallel.
3890  */
3891  Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
3892  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
3893  Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
3894 
3895  /* Skip indexes that don't participate in parallel vacuum */
3896  if (!can_parallel_vacuum[idx])
3897  continue;
3898 
3899  if (indrel->rd_indam->amusemaintenanceworkmem)
3900  nindexes_mwm++;
3901 
3902  est_shared = add_size(est_shared, sizeof(LVSharedIndStats));
3903 
3904  /*
3905  * Remember the number of indexes that support parallel operation for
3906  * each phase.
3907  */
3908  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
3910  if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
3912  if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
3914  }
3915  shm_toc_estimate_chunk(&pcxt->estimator, est_shared);
3916  shm_toc_estimate_keys(&pcxt->estimator, 1);
3917 
3918  /* Estimate size for dead tuples -- PARALLEL_VACUUM_KEY_DEAD_TUPLES */
3919  maxtuples = compute_max_dead_tuples(nblocks, true);
3920  est_deadtuples = MAXALIGN(SizeOfDeadTuples(maxtuples));
3921  shm_toc_estimate_chunk(&pcxt->estimator, est_deadtuples);
3922  shm_toc_estimate_keys(&pcxt->estimator, 1);
3923 
3924  /*
3925  * Estimate space for BufferUsage and WalUsage --
3926  * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
3927  *
3928  * If there are no extensions loaded that care, we could skip this. We
3929  * have no way of knowing whether anyone's looking at pgBufferUsage or
3930  * pgWalUsage, so do it unconditionally.
3931  */
3933  mul_size(sizeof(BufferUsage), pcxt->nworkers));
3934  shm_toc_estimate_keys(&pcxt->estimator, 1);
3936  mul_size(sizeof(WalUsage), pcxt->nworkers));
3937  shm_toc_estimate_keys(&pcxt->estimator, 1);
3938 
3939  /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
3940  if (debug_query_string)
3941  {
3942  querylen = strlen(debug_query_string);
3943  shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
3944  shm_toc_estimate_keys(&pcxt->estimator, 1);
3945  }
3946  else
3947  querylen = 0; /* keep compiler quiet */
3948 
3949  InitializeParallelDSM(pcxt);
3950 
3951  /* Prepare shared information */
3952  shared = (LVShared *) shm_toc_allocate(pcxt->toc, est_shared);
3953  MemSet(shared, 0, est_shared);
3954  shared->relid = RelationGetRelid(vacrel->rel);
3955  shared->elevel = elevel;
3956  shared->maintenance_work_mem_worker =
3957  (nindexes_mwm > 0) ?
3958  maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
3960 
3961  pg_atomic_init_u32(&(shared->cost_balance), 0);
3962  pg_atomic_init_u32(&(shared->active_nworkers), 0);
3963  pg_atomic_init_u32(&(shared->idx), 0);
3964  shared->offset = MAXALIGN(add_size(SizeOfLVShared, BITMAPLEN(nindexes)));
3965 
3966  /*
3967  * Initialize variables for shared index statistics, set NULL bitmap and
3968  * the size of stats for each index.
3969  */
3970  memset(shared->bitmap, 0x00, BITMAPLEN(nindexes));
3971  for (int idx = 0; idx < nindexes; idx++)
3972  {
3973  if (!can_parallel_vacuum[idx])
3974  continue;
3975 
3976  /* Set NOT NULL as this index does support parallelism */
3977  shared->bitmap[idx >> 3] |= 1 << (idx & 0x07);
3978  }
3979 
3981  lps->lvshared = shared;
3982 
3983  /* Prepare the dead tuple space */
3984  dead_tuples = (LVDeadTuples *) shm_toc_allocate(pcxt->toc, est_deadtuples);
3985  dead_tuples->max_tuples = maxtuples;
3986  dead_tuples->num_tuples = 0;
3987  MemSet(dead_tuples->itemptrs, 0, sizeof(ItemPointerData) * maxtuples);
3988  shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_DEAD_TUPLES, dead_tuples);
3989  vacrel->dead_tuples = dead_tuples;
3990 
3991  /*
3992  * Allocate space for each worker's BufferUsage and WalUsage; no need to
3993  * initialize
3994  */
3995  buffer_usage = shm_toc_allocate(pcxt->toc,
3996  mul_size(sizeof(BufferUsage), pcxt->nworkers));
3997  shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, buffer_usage);
3998  lps->buffer_usage = buffer_usage;
3999  wal_usage = shm_toc_allocate(pcxt->toc,
4000  mul_size(sizeof(WalUsage), pcxt->nworkers));
4002  lps->wal_usage = wal_usage;
4003 
4004  /* Store query string for workers */
4005  if (debug_query_string)
4006  {
4007  char *sharedquery;
4008 
4009  sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
4010  memcpy(sharedquery, debug_query_string, querylen + 1);
4011  sharedquery[querylen] = '\0';
4012  shm_toc_insert(pcxt->toc,
4013  PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
4014  }
4015 
4016  pfree(can_parallel_vacuum);
4017  return lps;
4018 }
uint8 amparallelvacuumoptions
Definition: amapi.h:248
struct IndexAmRoutine * rd_indam
Definition: rel.h:201
WalUsage * wal_usage
Definition: vacuumlazy.c:294
Relation * indrels
Definition: vacuumlazy.c:309
ItemPointerData itemptrs[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:185
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:152
Oid relid
Definition: vacuumlazy.c:206
#define SizeOfDeadTuples(cnt)
Definition: vacuumlazy.c:190
BufferUsage * buffer_usage
Definition: vacuumlazy.c:291
shm_toc_estimator estimator
Definition: parallel.h:42
#define Min(x, y)
Definition: c.h:986
int nindexes
Definition: vacuumlazy.c:310
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:3443
#define BITMAPLEN(NATTS)
Definition: htup_details.h:546
int nindexes_parallel_bulkdel
Definition: vacuumlazy.c:300
int maintenance_work_mem_worker
Definition: vacuumlazy.c:238
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
int nindexes_parallel_condcleanup
Definition: vacuumlazy.c:302
#define PARALLEL_VACUUM_KEY_QUERY_TEXT
Definition: vacuumlazy.c:153
ParallelContext * pcxt
Definition: vacuumlazy.c:285
#define SizeOfLVShared
Definition: vacuumlazy.c:266
pg_atomic_uint32 cost_balance
Definition: vacuumlazy.c:245
void pfree(void *pointer)
Definition: mcxt.c:1169
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:348
int elevel
Definition: vacuumlazy.c:207
pg_atomic_uint32 idx
Definition: vacuumlazy.c:259
const char * debug_query_string
Definition: postgres.c:89
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:202
static int elevel
Definition: vacuumlazy.c:401
int nindexes_parallel_cleanup
Definition: vacuumlazy.c:301
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:151
pg_atomic_uint32 active_nworkers
Definition: vacuumlazy.c:252
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:3737
#define PARALLEL_VACUUM_KEY_BUFFER_USAGE
Definition: vacuumlazy.c:154
#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:261
#define PARALLEL_VACUUM_KEY_WAL_USAGE
Definition: vacuumlazy.c:155
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:288
#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:308
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:260
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:60
#define RelationGetRelid(relation)
Definition: rel.h:477
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 3443 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().

3444 {
3445  long maxtuples;
3446  int vac_work_mem = IsAutoVacuumWorkerProcess() &&
3447  autovacuum_work_mem != -1 ?
3449 
3450  if (hasindex)
3451  {
3452  maxtuples = MAXDEADTUPLES(vac_work_mem * 1024L);
3453  maxtuples = Min(maxtuples, INT_MAX);
3454  maxtuples = Min(maxtuples, MAXDEADTUPLES(MaxAllocSize));
3455 
3456  /* curious coding here to ensure the multiplication can't overflow */
3457  if ((BlockNumber) (maxtuples / LAZY_ALLOC_TUPLES) > relblocks)
3458  maxtuples = relblocks * LAZY_ALLOC_TUPLES;
3459 
3460  /* stay sane if small maintenance_work_mem */
3461  maxtuples = Max(maxtuples, MaxHeapTuplesPerPage);
3462  }
3463  else
3464  maxtuples = MaxHeapTuplesPerPage;
3465 
3466  return maxtuples;
3467 }
int autovacuum_work_mem
Definition: autovacuum.c:116
#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:3410
#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:193
#define LAZY_ALLOC_TUPLES
Definition: vacuumlazy.c:132

◆ compute_parallel_vacuum_workers()

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

Definition at line 3737 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().

3739 {
3740  int nindexes_parallel = 0;
3741  int nindexes_parallel_bulkdel = 0;
3742  int nindexes_parallel_cleanup = 0;
3743  int parallel_workers;
3744 
3745  /*
3746  * We don't allow performing parallel operation in standalone backend or
3747  * when parallelism is disabled.
3748  */
3750  return 0;
3751 
3752  /*
3753  * Compute the number of indexes that can participate in parallel vacuum.
3754  */
3755  for (int idx = 0; idx < vacrel->nindexes; idx++)
3756  {
3757  Relation indrel = vacrel->indrels[idx];
3758  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
3759 
3760  if (vacoptions == VACUUM_OPTION_NO_PARALLEL ||
3762  continue;
3763 
3764  can_parallel_vacuum[idx] = true;
3765 
3766  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
3767  nindexes_parallel_bulkdel++;
3768  if (((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0) ||
3769  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0))
3770  nindexes_parallel_cleanup++;
3771  }
3772 
3773  nindexes_parallel = Max(nindexes_parallel_bulkdel,
3774  nindexes_parallel_cleanup);
3775 
3776  /* The leader process takes one index */
3777  nindexes_parallel--;
3778 
3779  /* No index supports parallel vacuum */
3780  if (nindexes_parallel <= 0)
3781  return 0;
3782 
3783  /* Compute the parallel degree */
3784  parallel_workers = (nrequested > 0) ?
3785  Min(nrequested, nindexes_parallel) : nindexes_parallel;
3786 
3787  /* Cap by max_parallel_maintenance_workers */
3788  parallel_workers = Min(parallel_workers, max_parallel_maintenance_workers);
3789 
3790  return parallel_workers;
3791 }
uint8 amparallelvacuumoptions
Definition: amapi.h:248
struct IndexAmRoutine * rd_indam
Definition: rel.h:201
Relation * indrels
Definition: vacuumlazy.c:309
#define Min(x, y)
Definition: c.h:986
int nindexes
Definition: vacuumlazy.c:310
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:212
#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,
bool lock_waiter_detected 
)
static

Definition at line 3305 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, 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().

3306 {
3307  BlockNumber blkno;
3308  BlockNumber prefetchedUntil;
3309  instr_time starttime;
3310 
3311  /* Initialize the starttime if we check for conflicting lock requests */
3312  INSTR_TIME_SET_CURRENT(starttime);
3313 
3314  /*
3315  * Start checking blocks at what we believe relation end to be and move
3316  * backwards. (Strange coding of loop control is needed because blkno is
3317  * unsigned.) To make the scan faster, we prefetch a few blocks at a time
3318  * in forward direction, so that OS-level readahead can kick in.
3319  */
3320  blkno = vacrel->rel_pages;
3322  "prefetch size must be power of 2");
3323  prefetchedUntil = InvalidBlockNumber;
3324  while (blkno > vacrel->nonempty_pages)
3325  {
3326  Buffer buf;
3327  Page page;
3328  OffsetNumber offnum,
3329  maxoff;
3330  bool hastup;
3331 
3332  /*
3333  * Check if another process requests a lock on our relation. We are
3334  * holding an AccessExclusiveLock here, so they will be waiting. We
3335  * only do this once per VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL, and we
3336  * only check if that interval has elapsed once every 32 blocks to
3337  * keep the number of system calls and actual shared lock table
3338  * lookups to a minimum.
3339  */
3340  if ((blkno % 32) == 0)
3341  {
3342  instr_time currenttime;
3343  instr_time elapsed;
3344 
3345  INSTR_TIME_SET_CURRENT(currenttime);
3346  elapsed = currenttime;
3347  INSTR_TIME_SUBTRACT(elapsed, starttime);
3348  if ((INSTR_TIME_GET_MICROSEC(elapsed) / 1000)
3350  {
3352  {
3353  ereport(elevel,
3354  (errmsg("table \"%s\": suspending truncate due to conflicting lock request",
3355  vacrel->relname)));
3356 
3357  *lock_waiter_detected = true;
3358  return blkno;
3359  }
3360  starttime = currenttime;
3361  }
3362  }
3363 
3364  /*
3365  * We don't insert a vacuum delay point here, because we have an
3366  * exclusive lock on the table which we want to hold for as short a
3367  * time as possible. We still need to check for interrupts however.
3368  */
3370 
3371  blkno--;
3372 
3373  /* If we haven't prefetched this lot yet, do so now. */
3374  if (prefetchedUntil > blkno)
3375  {
3376  BlockNumber prefetchStart;
3377  BlockNumber pblkno;
3378 
3379  prefetchStart = blkno & ~(PREFETCH_SIZE - 1);
3380  for (pblkno = prefetchStart; pblkno <= blkno; pblkno++)
3381  {
3382  PrefetchBuffer(vacrel->rel, MAIN_FORKNUM, pblkno);
3384  }
3385  prefetchedUntil = prefetchStart;
3386  }
3387 
3388  buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
3389  vacrel->bstrategy);
3390 
3391  /* In this phase we only need shared access to the buffer */
3393 
3394  page = BufferGetPage(buf);
3395 
3396  if (PageIsNew(page) || PageIsEmpty(page))
3397  {
3398  UnlockReleaseBuffer(buf);
3399  continue;
3400  }
3401 
3402  hastup = false;
3403  maxoff = PageGetMaxOffsetNumber(page);
3404  for (offnum = FirstOffsetNumber;
3405  offnum <= maxoff;
3406  offnum = OffsetNumberNext(offnum))
3407  {
3408  ItemId itemid;
3409 
3410  itemid = PageGetItemId(page, offnum);
3411 
3412  /*
3413  * Note: any non-unused item should be taken as a reason to keep
3414  * this page. Even an LP_DEAD item makes truncation unsafe, since
3415  * we must not have cleaned out its index entries.
3416  */
3417  if (ItemIdIsUsed(itemid))
3418  {
3419  hastup = true;
3420  break; /* can stop scanning */
3421  }
3422  } /* scan along page */
3423 
3424  UnlockReleaseBuffer(buf);
3425 
3426  /* Done scanning if we found a tuple here */
3427  if (hastup)
3428  return blkno + 1;
3429  }
3430 
3431  /*
3432  * If we fall out of the loop, all the previously-thought-to-be-empty
3433  * pages still are; we need not bother to look at the last known-nonempty
3434  * page.
3435  */
3436  return vacrel->nonempty_pages;
3437 }
#define PageIsEmpty(page)
Definition: bufpage.h:222
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:741
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:144
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3791
#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:356
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
static int elevel
Definition: vacuumlazy.c:401
#define VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL
Definition: vacuumlazy.c:102
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4007
#define ereport(elevel,...)
Definition: elog.h:157
bool LockHasWaitersRelation(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:346
char * relname
Definition: vacuumlazy.c:339
#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:323
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
Relation rel
Definition: vacuumlazy.c:308
#define AccessExclusiveLock
Definition: lockdefs.h:45
BlockNumber rel_pages
Definition: vacuumlazy.c:349
#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 2653 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().

2654 {
2655  int nworkers;
2656 
2657  /*
2658  * If parallel vacuum is active we perform index cleanup with parallel
2659  * workers.
2660  *
2661  * Tell parallel workers to do index cleanup.
2662  */
2663  vacrel->lps->lvshared->for_cleanup = true;
2664  vacrel->lps->lvshared->first_time = (vacrel->num_index_scans == 0);
2665 
2666  /*
2667  * Now we can provide a better estimate of total number of surviving
2668  * tuples (we assume indexes are more interested in that than in the
2669  * number of nominally live tuples).
2670  */
2671  vacrel->lps->lvshared->reltuples = vacrel->new_rel_tuples;
2672  vacrel->lps->lvshared->estimated_count =
2673  (vacrel->tupcount_pages < vacrel->rel_pages);
2674 
2675  /* Determine the number of parallel workers to launch */
2676  if (vacrel->lps->lvshared->first_time)
2677  nworkers = vacrel->lps->nindexes_parallel_cleanup +
2679  else
2680  nworkers = vacrel->lps->nindexes_parallel_cleanup;
2681 
2682  do_parallel_vacuum_or_cleanup(vacrel, nworkers);
2683 }
LVParallelState * lps
Definition: vacuumlazy.c:324
bool estimated_count
Definition: vacuumlazy.c:228
int num_index_scans
Definition: vacuumlazy.c:365
int nindexes_parallel_condcleanup
Definition: vacuumlazy.c:302
bool first_time
Definition: vacuumlazy.c:215
double reltuples
Definition: vacuumlazy.c:227
int nindexes_parallel_cleanup
Definition: vacuumlazy.c:301
double new_rel_tuples
Definition: vacuumlazy.c:359
BlockNumber tupcount_pages
Definition: vacuumlazy.c:353
LVShared * lvshared
Definition: vacuumlazy.c:288
bool for_cleanup
Definition: vacuumlazy.c:214
BlockNumber rel_pages
Definition: vacuumlazy.c:349
static void do_parallel_vacuum_or_cleanup(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:2692

◆ do_parallel_lazy_vacuum_all_indexes()

static void do_parallel_lazy_vacuum_all_indexes ( LVRelState vacrel)
static

Definition at line 2632 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().

2633 {
2634  /* Tell parallel workers to do index vacuuming */
2635  vacrel->lps->lvshared->for_cleanup = false;
2636  vacrel->lps->lvshared->first_time = false;
2637 
2638  /*
2639  * We can only provide an approximate value of num_heap_tuples in vacuum
2640  * cases.
2641  */
2642  vacrel->lps->lvshared->reltuples = vacrel->old_live_tuples;
2643  vacrel->lps->lvshared->estimated_count = true;
2644 
2646  vacrel->lps->nindexes_parallel_bulkdel);
2647 }
LVParallelState * lps
Definition: vacuumlazy.c:324
bool estimated_count
Definition: vacuumlazy.c:228
int nindexes_parallel_bulkdel
Definition: vacuumlazy.c:300
bool first_time
Definition: vacuumlazy.c:215
double reltuples
Definition: vacuumlazy.c:227
LVShared * lvshared
Definition: vacuumlazy.c:288
bool for_cleanup
Definition: vacuumlazy.c:214
double old_live_tuples
Definition: vacuumlazy.c:329
static void do_parallel_vacuum_or_cleanup(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:2692

◆ do_parallel_processing()

static void do_parallel_processing ( LVRelState vacrel,
LVShared lvshared 
)
static

Definition at line 2805 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().

2806 {
2807  /*
2808  * Increment the active worker count if we are able to launch any worker.
2809  */
2812 
2813  /* Loop until all indexes are vacuumed */
2814  for (;;)
2815  {
2816  int idx;
2817  LVSharedIndStats *shared_istat;
2818  Relation indrel;
2819  IndexBulkDeleteResult *istat;
2820 
2821  /* Get an index number to process */
2822  idx = pg_atomic_fetch_add_u32(&(lvshared->idx), 1);
2823 
2824  /* Done for all indexes? */
2825  if (idx >= vacrel->nindexes)
2826  break;
2827 
2828  /* Get the index statistics of this index from DSM */
2829  shared_istat = parallel_stats_for_idx(lvshared, idx);
2830 
2831  /* Skip indexes not participating in parallelism */
2832  if (shared_istat == NULL)
2833  continue;
2834 
2835  indrel = vacrel->indrels[idx];
2836 
2837  /*
2838  * Skip processing indexes that are unsafe for workers (these are
2839  * processed in do_serial_processing_for_unsafe_indexes() by leader)
2840  */
2841  if (!parallel_processing_is_safe(indrel, lvshared))
2842  continue;
2843 
2844  /* Do vacuum or cleanup of the index */
2845  istat = vacrel->indstats[idx];
2846  vacrel->indstats[idx] = parallel_process_one_index(indrel, istat,
2847  lvshared,
2848  shared_istat,
2849  vacrel);
2850  }
2851 
2852  /*
2853  * We have completed the index vacuum so decrement the active worker
2854  * count.
2855  */
2858 }
Relation * indrels
Definition: vacuumlazy.c:309
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:310
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:2918
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:362
pg_atomic_uint32 idx
Definition: vacuumlazy.c:259
static bool parallel_processing_is_safe(Relation indrel, LVShared *lvshared)
Definition: vacuumlazy.c:4097
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4073
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 2692 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().

2693 {
2694  LVParallelState *lps = vacrel->lps;
2695 
2697  Assert(ParallelVacuumIsActive(vacrel));
2698  Assert(vacrel->nindexes > 0);
2699 
2700  /* The leader process will participate */
2701  nworkers--;
2702 
2703  /*
2704  * It is possible that parallel context is initialized with fewer workers
2705  * than the number of indexes that need a separate worker in the current
2706  * phase, so we need to consider it. See compute_parallel_vacuum_workers.
2707  */
2708  nworkers = Min(nworkers, lps->pcxt->nworkers);
2709 
2710  /* Setup the shared cost-based vacuum delay and launch workers */
2711  if (nworkers > 0)
2712  {
2713  if (vacrel->num_index_scans > 0)
2714  {
2715  /* Reset the parallel index processing counter */
2716  pg_atomic_write_u32(&(lps->lvshared->idx), 0);
2717 
2718  /* Reinitialize the parallel context to relaunch parallel workers */
2720  }
2721 
2722  /*
2723  * Set up shared cost balance and the number of active workers for
2724  * vacuum delay. We need to do this before launching workers as
2725  * otherwise, they might not see the updated values for these
2726  * parameters.
2727  */
2730 
2731  /*
2732  * The number of workers can vary between bulkdelete and cleanup
2733  * phase.
2734  */
2735  ReinitializeParallelWorkers(lps->pcxt, nworkers);
2736 
2738 
2739  if (lps->pcxt->nworkers_launched > 0)
2740  {
2741  /*
2742  * Reset the local cost values for leader backend as we have
2743  * already accumulated the remaining balance of heap.
2744  */
2745  VacuumCostBalance = 0;
2747 
2748  /* Enable shared cost balance for leader backend */
2751  }
2752 
2753  if (lps->lvshared->for_cleanup)
2754  ereport(elevel,
2755  (errmsg(ngettext("launched %d parallel vacuum worker for index cleanup (planned: %d)",
2756  "launched %d parallel vacuum workers for index cleanup (planned: %d)",
2757  lps->pcxt->nworkers_launched),
2758  lps->pcxt->nworkers_launched, nworkers)));
2759  else
2760  ereport(elevel,
2761  (errmsg(ngettext("launched %d parallel vacuum worker for index vacuuming (planned: %d)",
2762  "launched %d parallel vacuum workers for index vacuuming (planned: %d)",
2763  lps->pcxt->nworkers_launched),
2764  lps->pcxt->nworkers_launched, nworkers)));
2765  }
2766 
2767  /* Process the indexes that can be processed by only leader process */
2769 
2770  /*
2771  * Join as a parallel worker. The leader process alone processes all the
2772  * indexes in the case where no workers are launched.
2773  */
2774  do_parallel_processing(vacrel, lps->lvshared);
2775 
2776  /*
2777  * Next, accumulate buffer and WAL usage. (This must wait for the workers
2778  * to finish, or we might get incomplete data.)
2779  */
2780  if (nworkers > 0)
2781  {
2782  /* Wait for all vacuum workers to finish */
2784 
2785  for (int i = 0; i < lps->pcxt->nworkers_launched; i++)
2787  }
2788 
2789  /*
2790  * Carry the shared balance value to heap scan and disable shared costing
2791  */
2793  {
2795  VacuumSharedCostBalance = NULL;
2796  VacuumActiveNWorkers = NULL;
2797  }
2798 }
WalUsage * wal_usage
Definition: vacuumlazy.c:294
LVParallelState * lps
Definition: vacuumlazy.c:324
pg_atomic_uint32 * VacuumActiveNWorkers
Definition: vacuum.c:79
int VacuumCostBalance
Definition: globals.c:151
int num_index_scans
Definition: vacuumlazy.c:365
BufferUsage * buffer_usage
Definition: vacuumlazy.c:291
#define Min(x, y)
Definition: c.h:986
int nindexes
Definition: vacuumlazy.c:310
void ReinitializeParallelWorkers(ParallelContext *pcxt, int nworkers_to_launch)
Definition: parallel.c:525
ParallelContext * pcxt
Definition: vacuumlazy.c:285
pg_atomic_uint32 cost_balance
Definition: vacuumlazy.c:245
void WaitForParallelWorkersToFinish(ParallelContext *pcxt)
Definition: parallel.c:762
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
static void do_serial_processing_for_unsafe_indexes(LVRelState *vacrel, LVShared *lvshared)
Definition: vacuumlazy.c:2865
pg_atomic_uint32 idx
Definition: vacuumlazy.c:259
int nworkers_launched
Definition: parallel.h:38
void LaunchParallelWorkers(ParallelContext *pcxt)
Definition: parallel.c:539
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:2805
int VacuumCostBalanceLocal
Definition: vacuum.c:80
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:78
static int elevel
Definition: vacuumlazy.c:401
#define ngettext(s, p, n)
Definition: c.h:1182
void ReinitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:475
pg_atomic_uint32 active_nworkers
Definition: vacuumlazy.c:252
#define ereport(elevel,...)
Definition: elog.h:157
#define Assert(condition)
Definition: c.h:804
LVShared * lvshared
Definition: vacuumlazy.c:288
bool for_cleanup
Definition: vacuumlazy.c:214
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 2865 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().

2866 {
2868 
2869  /*
2870  * Increment the active worker count if we are able to launch any worker.
2871  */
2874 
2875  for (int idx = 0; idx < vacrel->nindexes; idx++)
2876  {
2877  LVSharedIndStats *shared_istat;
2878  Relation indrel;
2879  IndexBulkDeleteResult *istat;
2880 
2881  shared_istat = parallel_stats_for_idx(lvshared, idx);
2882 
2883  /* Skip already-complete indexes */
2884  if (shared_istat != NULL)
2885  continue;
2886 
2887  indrel = vacrel->indrels[idx];
2888 
2889  /*
2890  * We're only here for the unsafe indexes
2891  */
2892  if (parallel_processing_is_safe(indrel, lvshared))
2893  continue;
2894 
2895  /* Do vacuum or cleanup of the index */
2896  istat = vacrel->indstats[idx];
2897  vacrel->indstats[idx] = parallel_process_one_index(indrel, istat,
2898  lvshared,
2899  shared_istat,
2900  vacrel);
2901  }
2902 
2903  /*
2904  * We have completed the index vacuum so decrement the active worker
2905  * count.
2906  */
2909 }
Relation * indrels
Definition: vacuumlazy.c:309
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:310
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:2918
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:362
static bool parallel_processing_is_safe(Relation indrel, LVShared *lvshared)
Definition: vacuumlazy.c:4097
#define IsParallelWorker()
Definition: parallel.h:61
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4073
#define Assert(condition)
Definition: c.h:804

◆ end_parallel_vacuum()

static void end_parallel_vacuum ( LVRelState vacrel)
static

Definition at line 4030 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().

4031 {
4032  IndexBulkDeleteResult **indstats = vacrel->indstats;
4033  LVParallelState *lps = vacrel->lps;
4034  int nindexes = vacrel->nindexes;
4035 
4037 
4038  /* Copy the updated statistics */
4039  for (int idx = 0; idx < nindexes; idx++)
4040  {
4041  LVSharedIndStats *shared_istat;
4042 
4043  shared_istat = parallel_stats_for_idx(lps->lvshared, idx);
4044 
4045  /*
4046  * Skip unused slot. The statistics of this index are already stored
4047  * in local memory.
4048  */
4049  if (shared_istat == NULL)
4050  continue;
4051 
4052  if (shared_istat->updated)
4053  {
4054  indstats[idx] = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
4055  memcpy(indstats[idx], &shared_istat->istat, sizeof(IndexBulkDeleteResult));
4056  }
4057  else
4058  indstats[idx] = NULL;
4059  }
4060 
4062  ExitParallelMode();
4063 
4064  /* Deactivate parallel vacuum */
4065  pfree(lps);
4066  vacrel->lps = NULL;
4067 }
LVParallelState * lps
Definition: vacuumlazy.c:324
int nindexes
Definition: vacuumlazy.c:310
IndexBulkDeleteResult istat
Definition: vacuumlazy.c:279
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
ParallelContext * pcxt
Definition: vacuumlazy.c:285
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:362
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:916
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:4073
void * palloc0(Size size)
Definition: mcxt.c:1093
#define Assert(condition)
Definition: c.h:804
LVShared * lvshared
Definition: vacuumlazy.c:288

◆ 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 3610 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().

3613 {
3614  Page page = BufferGetPage(buf);
3616  OffsetNumber offnum,
3617  maxoff;
3618  bool all_visible = true;
3619 
3620  *visibility_cutoff_xid = InvalidTransactionId;
3621  *all_frozen = true;
3622 
3623  /*
3624  * This is a stripped down version of the line pointer scan in
3625  * lazy_scan_heap(). So if you change anything here, also check that code.
3626  */
3627  maxoff = PageGetMaxOffsetNumber(page);
3628  for (offnum = FirstOffsetNumber;
3629  offnum <= maxoff && all_visible;
3630  offnum = OffsetNumberNext(offnum))
3631  {
3632  ItemId itemid;
3633  HeapTupleData tuple;
3634 
3635  /*
3636  * Set the offset number so that we can display it along with any
3637  * error that occurred while processing this tuple.
3638  */
3639  vacrel->offnum = offnum;
3640  itemid = PageGetItemId(page, offnum);
3641 
3642  /* Unused or redirect line pointers are of no interest */
3643  if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid))
3644  continue;
3645 
3646  ItemPointerSet(&(tuple.t_self), blockno, offnum);
3647 
3648  /*
3649  * Dead line pointers can have index pointers pointing to them. So
3650  * they can't be treated as visible
3651  */
3652  if (ItemIdIsDead(itemid))
3653  {
3654  all_visible = false;
3655  *all_frozen = false;
3656  break;
3657  }
3658 
3659  Assert(ItemIdIsNormal(itemid));
3660 
3661  tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
3662  tuple.t_len = ItemIdGetLength(itemid);
3663  tuple.t_tableOid = RelationGetRelid(vacrel->rel);
3664 
3665  switch (HeapTupleSatisfiesVacuum(&tuple, vacrel->OldestXmin, buf))
3666  {
3667  case HEAPTUPLE_LIVE:
3668  {
3669  TransactionId xmin;
3670 
3671  /* Check comments in lazy_scan_heap. */
3673  {
3674  all_visible = false;
3675  *all_frozen = false;
3676  break;
3677  }
3678 
3679  /*
3680  * The inserter definitely committed. But is it old enough
3681  * that everyone sees it as committed?
3682  */
3683  xmin = HeapTupleHeaderGetXmin(tuple.t_data);
3684  if (!TransactionIdPrecedes(xmin, vacrel->OldestXmin))
3685  {
3686  all_visible = false;
3687  *all_frozen = false;
3688  break;
3689  }
3690 
3691  /* Track newest xmin on page. */
3692  if (TransactionIdFollows(xmin, *visibility_cutoff_xid))
3693  *visibility_cutoff_xid = xmin;
3694 
3695  /* Check whether this tuple is already frozen or not */
3696  if (all_visible && *all_frozen &&
3698  *all_frozen = false;
3699  }
3700  break;
3701 
3702  case HEAPTUPLE_DEAD:
3706  {
3707  all_visible = false;
3708  *all_frozen = false;
3709  break;
3710  }
3711  default:
3712  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
3713  break;
3714  }
3715  } /* scan along page */
3716 
3717  /* Clear the offset information once we have processed the given page. */
3718  vacrel->offnum = InvalidOffsetNumber;
3719 
3720  return all_visible;
3721 }
#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:342
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:7047
#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:332
#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:308
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2748
#define elog(elevel,...)
Definition: elog.h:232
#define RelationGetRelid(relation)
Definition: rel.h:477
#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 482 of file vacuumlazy.c.

References _, appendStringInfo(), appendStringInfoString(), ErrorContextCallback::arg, Assert, LVRelState::bstrategy, buf, ErrorContextCallback::callback, LVRelState::consider_bypass_optimization, StringInfoData::data, DEBUG2, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, LVRelState::do_rel_truncate, elevel, ereport, errmsg_internal(), error_context_stack, LVRelState::failsafe_active, 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::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_VERBOSE, VACOPTVALUE_AUTO, VACOPTVALUE_DISABLED, VACOPTVALUE_ENABLED, VACOPTVALUE_UNSPECIFIED, 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().

484 {
485  LVRelState *vacrel;
486  PGRUsage ru0;
487  TimestampTz starttime = 0;
488  WalUsage walusage_start = pgWalUsage;
489  WalUsage walusage = {0, 0, 0};
490  long secs;
491  int usecs;
492  double read_rate,
493  write_rate;
494  bool aggressive; /* should we scan all unfrozen pages? */
495  bool scanned_all_unfrozen; /* actually scanned all such pages? */
496  char **indnames = NULL;
497  TransactionId xidFullScanLimit;
498  MultiXactId mxactFullScanLimit;
499  BlockNumber new_rel_pages;
500  BlockNumber new_rel_allvisible;
501  double new_live_tuples;
502  TransactionId new_frozen_xid;
503  MultiXactId new_min_multi;
504  ErrorContextCallback errcallback;
505  PgStat_Counter startreadtime = 0;
506  PgStat_Counter startwritetime = 0;
507  TransactionId OldestXmin;
508  TransactionId FreezeLimit;
509  MultiXactId MultiXactCutoff;
510 
511  /* measure elapsed time iff autovacuum logging requires it */
512  if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
513  {
514  pg_rusage_init(&ru0);
515  starttime = GetCurrentTimestamp();
516  if (track_io_timing)
517  {
518  startreadtime = pgStatBlockReadTime;
519  startwritetime = pgStatBlockWriteTime;
520  }
521  }
522 
523  if (params->options & VACOPT_VERBOSE)
524  elevel = INFO;
525  else
526  elevel = DEBUG2;
527 
529  RelationGetRelid(rel));
530 
532  params->freeze_min_age,
533  params->freeze_table_age,
534  params->multixact_freeze_min_age,
536  &OldestXmin, &FreezeLimit, &xidFullScanLimit,
537  &MultiXactCutoff, &mxactFullScanLimit);
538 
539  /*
540  * We request an aggressive scan if the table's frozen Xid is now older
541  * than or equal to the requested Xid full-table scan limit; or if the
542  * table's minimum MultiXactId is older than or equal to the requested
543  * mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified.
544  */
545  aggressive = TransactionIdPrecedesOrEquals(rel->rd_rel->relfrozenxid,
546  xidFullScanLimit);
547  aggressive |= MultiXactIdPrecedesOrEquals(rel->rd_rel->relminmxid,
548  mxactFullScanLimit);
550  aggressive = true;
551 
552  vacrel = (LVRelState *) palloc0(sizeof(LVRelState));
553 
554  /* Set up high level stuff about rel */
555  vacrel->rel = rel;
556  vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes,
557  &vacrel->indrels);
558  vacrel->failsafe_active = false;
559  vacrel->consider_bypass_optimization = true;
560 
561  /*
562  * The index_cleanup param either disables index vacuuming and cleanup or
563  * forces it to go ahead when we would otherwise apply the index bypass
564  * optimization. The default is 'auto', which leaves the final decision
565  * up to lazy_vacuum().
566  *
567  * The truncate param allows user to avoid attempting relation truncation,
568  * though it can't force truncation to happen.
569  */
572  params->truncate != VACOPTVALUE_AUTO);
573  vacrel->do_index_vacuuming = true;
574  vacrel->do_index_cleanup = true;
575  vacrel->do_rel_truncate = (params->truncate != VACOPTVALUE_DISABLED);
576  if (params->index_cleanup == VACOPTVALUE_DISABLED)
577  {
578  /* Force disable index vacuuming up-front */
579  vacrel->do_index_vacuuming = false;
580  vacrel->do_index_cleanup = false;
581  }
582  else if (params->index_cleanup == VACOPTVALUE_ENABLED)
583  {
584  /* Force index vacuuming. Note that failsafe can still bypass. */
585  vacrel->consider_bypass_optimization = false;
586  }
587  else
588  {
589  /* Default/auto, make all decisions dynamically */
591  }
592 
593  vacrel->bstrategy = bstrategy;
594  vacrel->relfrozenxid = rel->rd_rel->relfrozenxid;
595  vacrel->relminmxid = rel->rd_rel->relminmxid;
596  vacrel->old_live_tuples = rel->rd_rel->reltuples;
597 
598  /* Set cutoffs for entire VACUUM */
599  vacrel->OldestXmin = OldestXmin;
600  vacrel->FreezeLimit = FreezeLimit;
601  vacrel->MultiXactCutoff = MultiXactCutoff;
602 
604  vacrel->relname = pstrdup(RelationGetRelationName(rel));
605  vacrel->indname = NULL;
607 
608  /* Save index names iff autovacuum logging requires it */
609  if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0 &&
610  vacrel->nindexes > 0)
611  {
612  indnames = palloc(sizeof(char *) * vacrel->nindexes);
613  for (int i = 0; i < vacrel->nindexes; i++)
614  indnames[i] =
616  }
617 
618  /*
619  * Setup error traceback support for ereport(). The idea is to set up an
620  * error context callback to display additional information on any error
621  * during a vacuum. During different phases of vacuum (heap scan, heap
622  * vacuum, index vacuum, index clean up, heap truncate), we update the
623  * error context callback to display appropriate information.
624  *
625  * Note that the index vacuum and heap vacuum phases may be called
626  * multiple times in the middle of the heap scan phase. So the old phase
627  * information is restored at the end of those phases.
628  */
629  errcallback.callback = vacuum_error_callback;
630  errcallback.arg = vacrel;
631  errcallback.previous = error_context_stack;
632  error_context_stack = &errcallback;
633 
634  /* Do the vacuuming */
635  lazy_scan_heap(vacrel, params, aggressive);
636 
637  /* Done with indexes */
638  vac_close_indexes(vacrel->nindexes, vacrel->indrels, NoLock);
639 
640  /*
641  * Compute whether we actually scanned the all unfrozen pages. If we did,
642  * we can adjust relfrozenxid and relminmxid.
643  *
644  * NB: We need to check this before truncating the relation, because that
645  * will change ->rel_pages.
646  */
647  if ((vacrel->scanned_pages + vacrel->frozenskipped_pages)
648  < vacrel->rel_pages)
649  {
650  Assert(!aggressive);
651  scanned_all_unfrozen = false;
652  }
653  else
654  scanned_all_unfrozen = true;
655 
656  /*
657  * Optionally truncate the relation.
658  */
659  if (should_attempt_truncation(vacrel))
660  {
661  /*
662  * Update error traceback information. This is the last phase during
663  * which we add context information to errors, so we don't need to
664  * revert to the previous phase.
665  */
667  vacrel->nonempty_pages,
669  lazy_truncate_heap(vacrel);
670  }
671 
672  /* Pop the error context stack */
673  error_context_stack = errcallback.previous;
674 
675  /* Report that we are now doing final cleanup */
678 
679  /*
680  * Update statistics in pg_class.
681  *
682  * In principle new_live_tuples could be -1 indicating that we (still)
683  * don't know the tuple count. In practice that probably can't happen,
684  * since we'd surely have scanned some pages if the table is new and
685  * nonempty.
686  *
687  * For safety, clamp relallvisible to be not more than what we're setting
688  * relpages to.
689  *
690  * Also, don't change relfrozenxid/relminmxid if we skipped any pages,
691  * since then we don't know for certain that all tuples have a newer xmin.
692  */
693  new_rel_pages = vacrel->rel_pages;
694  new_live_tuples = vacrel->new_live_tuples;
695 
696  visibilitymap_count(rel, &new_rel_allvisible, NULL);
697  if (new_rel_allvisible > new_rel_pages)
698  new_rel_allvisible = new_rel_pages;
699 
700  new_frozen_xid = scanned_all_unfrozen ? FreezeLimit : InvalidTransactionId;
701  new_min_multi = scanned_all_unfrozen ? MultiXactCutoff : InvalidMultiXactId;
702 
704  new_rel_pages,
705  new_live_tuples,
706  new_rel_allvisible,
707  vacrel->nindexes > 0,
708  new_frozen_xid,
709  new_min_multi,
710  false);
711 
712  /*
713  * Report results to the stats collector, too.
714  *
715  * Deliberately avoid telling the stats collector about LP_DEAD items that
716  * remain in the table due to VACUUM bypassing index and heap vacuuming.
717  * ANALYZE will consider the remaining LP_DEAD items to be dead tuples. It
718  * seems like a good idea to err on the side of not vacuuming again too
719  * soon in cases where the failsafe prevented significant amounts of heap
720  * vacuuming.
721  */
723  rel->rd_rel->relisshared,
724  Max(new_live_tuples, 0),
725  vacrel->new_dead_tuples);
727 
728  /* and log the action if appropriate */
729  if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
730  {
731  TimestampTz endtime = GetCurrentTimestamp();
732 
733  if (params->log_min_duration == 0 ||
734  TimestampDifferenceExceeds(starttime, endtime,
735  params->log_min_duration))
736  {
738  char *msgfmt;
739  BlockNumber orig_rel_pages;
740 
741  TimestampDifference(starttime, endtime, &secs, &usecs);
742 
743  memset(&walusage, 0, sizeof(WalUsage));
744  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
745 
746  read_rate = 0;
747  write_rate = 0;
748  if ((secs > 0) || (usecs > 0))
749  {
750  read_rate = (double) BLCKSZ * VacuumPageMiss / (1024 * 1024) /
751  (secs + usecs / 1000000.0);
752  write_rate = (double) BLCKSZ * VacuumPageDirty / (1024 * 1024) /
753  (secs + usecs / 1000000.0);
754  }
755 
756  /*
757  * This is pretty messy, but we split it up so that we can skip
758  * emitting individual parts of the message when not applicable.
759  */
760  initStringInfo(&buf);
761  if (params->is_wraparound)
762  {
763  /*
764  * While it's possible for a VACUUM to be both is_wraparound
765  * and !aggressive, that's just a corner-case -- is_wraparound
766  * implies aggressive. Produce distinct output for the corner
767  * case all the same, just in case.
768  */
769  if (aggressive)
770  msgfmt = _("automatic aggressive vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
771  else
772  msgfmt = _("automatic vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
773  }
774  else
775  {
776  if (aggressive)
777  msgfmt = _("automatic aggressive vacuum of table \"%s.%s.%s\": index scans: %d\n");
778  else
779  msgfmt = _("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n");
780  }
781  appendStringInfo(&buf, msgfmt,
783  vacrel->relnamespace,
784  vacrel->relname,
785  vacrel->num_index_scans);
786  appendStringInfo(&buf, _("pages: %u removed, %u remain, %u skipped due to pins, %u skipped frozen\n"),
787  vacrel->pages_removed,
788  vacrel->rel_pages,
789  vacrel->pinskipped_pages,
790  vacrel->frozenskipped_pages);
791  appendStringInfo(&buf,
792  _("tuples: %lld removed, %lld remain, %lld are dead but not yet removable, oldest xmin: %u\n"),
793  (long long) vacrel->tuples_deleted,
794  (long long) vacrel->new_rel_tuples,
795  (long long) vacrel->new_dead_tuples,
796  OldestXmin);
797  orig_rel_pages = vacrel->rel_pages + vacrel->pages_removed;
798  if (orig_rel_pages > 0)
799  {
800  if (vacrel->do_index_vacuuming)
801  {
802  if (vacrel->nindexes == 0 || vacrel->num_index_scans == 0)
803  appendStringInfoString(&buf, _("index scan not needed: "));
804  else
805  appendStringInfoString(&buf, _("index scan needed: "));
806 
807  msgfmt = _("%u pages from table (%.2f%% of total) had %lld dead item identifiers removed\n");
808  }
809  else
810  {
811  if (!vacrel->failsafe_active)
812  appendStringInfoString(&buf, _("index scan bypassed: "));
813  else
814  appendStringInfoString(&buf, _("index scan bypassed by failsafe: "));
815 
816  msgfmt = _("%u pages from table (%.2f%% of total) have %lld dead item identifiers\n");
817  }
818  appendStringInfo(&buf, msgfmt,
819  vacrel->lpdead_item_pages,
820  100.0 * vacrel->lpdead_item_pages / orig_rel_pages,
821  (long long) vacrel->lpdead_items);
822  }
823  for (int i = 0; i < vacrel->nindexes; i++)
824  {
825  IndexBulkDeleteResult *istat = vacrel->indstats[i];
826 
827  if (!istat)
828  continue;
829 
830  appendStringInfo(&buf,
831  _("index \"%s\": pages: %u in total, %u newly deleted, %u currently deleted, %u reusable\n"),
832  indnames[i],
833  istat->num_pages,
834  istat->pages_newly_deleted,
835  istat->pages_deleted,
836  istat->pages_free);
837  }
838  if (track_io_timing)
839  {
840  double read_ms = (double) (pgStatBlockReadTime - startreadtime) / 1000;
841  double write_ms = (double) (pgStatBlockWriteTime - startwritetime) / 1000;
842 
843  appendStringInfo(&buf, _("I/O timings: read: %.3f ms, write: %.3f ms\n"),
844  read_ms, write_ms);
845  }
846  appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
847  read_rate, write_rate);
848  appendStringInfo(&buf,
849  _("buffer usage: %lld hits, %lld misses, %lld dirtied\n"),
850  (long long) VacuumPageHit,
851  (long long) VacuumPageMiss,
852  (long long) VacuumPageDirty);
853  appendStringInfo(&buf,
854  _("WAL usage: %lld records, %lld full page images, %llu bytes\n"),
855  (long long) walusage.wal_records,
856  (long long) walusage.wal_fpi,
857  (unsigned long long) walusage.wal_bytes);
858  appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));
859 
860  ereport(LOG,
861  (errmsg_internal("%s", buf.data)));
862  pfree(buf.data);
863  }
864  }
865 
866  /* Cleanup index statistics and index names */
867  for (int i = 0; i < vacrel->nindexes; i++)
868  {
869  if (vacrel->indstats[i])
870  pfree(vacrel->indstats[i]);
871 
872  if (indnames && indnames[i])
873  pfree(indnames[i]);
874  }
875 }
VacErrPhase phase
Definition: vacuumlazy.c:343
static void vacuum_error_callback(void *arg)
Definition: vacuumlazy.c:4244
int multixact_freeze_table_age
Definition: vacuum.h:216
void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
Definition: vacuum.c:2128
int64 VacuumPageMiss
Definition: globals.c:148
int64 PgStat_Counter
Definition: pgstat.h:92
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4308
Relation * indrels
Definition: vacuumlazy.c:309
bool do_index_vacuuming
Definition: vacuumlazy.c:318
#define VACOPT_DISABLE_PAGE_SKIPPING
Definition: vacuum.h:185
uint32 TransactionId
Definition: c.h:587
int num_index_scans
Definition: vacuumlazy.c:365
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1580
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:310
int64 wal_fpi
Definition: instrument.h:50
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:274
bool do_index_cleanup
Definition: vacuumlazy.c:319
MultiXactId MultiXactCutoff
Definition: vacuumlazy.c:335
#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:957
int64 VacuumPageDirty
Definition: globals.c:149
uint32 BlockNumber
Definition: block.h:31
int64 lpdead_items
Definition: vacuumlazy.c:367
void visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen)
VacOptValue truncate
Definition: vacuum.h:223
BlockNumber frozenskipped_pages
Definition: vacuumlazy.c:352
#define LOG
Definition: elog.h:26
Form_pg_class rd_rel
Definition: rel.h:109
double new_live_tuples
Definition: vacuumlazy.c:360
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:213
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:362
bool failsafe_active
Definition: vacuumlazy.c:313
bits32 options
Definition: vacuum.h:211
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:212
BlockNumber num_pages
Definition: genam.h:76
static void lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
Definition: vacuumlazy.c:906
BlockNumber pages_free
Definition: genam.h:82
PgStat_Counter pgStatBlockWriteTime
Definition: pgstat.c:248
BlockNumber scanned_pages
Definition: vacuumlazy.c:350
bool is_wraparound
Definition: vacuum.h:218
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2113
#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:247
#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:356
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:511
BlockNumber pages_removed
Definition: vacuumlazy.c:354
BlockNumber pages_deleted
Definition: genam.h:81
static void lazy_truncate_heap(LVRelState *vacrel)
Definition: vacuumlazy.c:3172
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3410
BlockNumber pinskipped_pages
Definition: vacuumlazy.c:351
TransactionId FreezeLimit
Definition: vacuumlazy.c:334
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel)
Definition: vacuum.c:2085
TransactionId OldestXmin
Definition: vacuumlazy.c:332
VacOptValue index_cleanup
Definition: vacuum.h:222
static int elevel
Definition: vacuumlazy.c:401
char * indname
Definition: vacuumlazy.c:340
void * palloc0(Size size)
Definition: mcxt.c:1093
Oid MyDatabaseId
Definition: globals.c:88
#define InvalidMultiXactId
Definition: multixact.h:24
MultiXactId relminmxid
Definition: vacuumlazy.c:328
#define InvalidOffsetNumber
Definition: off.h:26
int64 tuples_deleted
Definition: vacuumlazy.c:366
#define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP
Definition: progress.h:35
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:339
TransactionId MultiXactId
Definition: c.h:597
int errmsg_internal(const char *fmt,...)
Definition: elog.c:996
TransactionId relfrozenxid
Definition: vacuumlazy.c:327
#define Max(x, y)
Definition: c.h:980
double new_rel_tuples
Definition: vacuumlazy.c:359
#define Assert(condition)
Definition: c.h:804
void pgstat_progress_update_param(int index, int64 val)
BlockNumber lpdead_item_pages
Definition: vacuumlazy.c:355
int64 new_dead_tuples
Definition: vacuumlazy.c:368
#define PROGRESS_VACUUM_PHASE
Definition: progress.h:21
int log_min_duration
Definition: vacuum.h:219
BufferAccessStrategy bstrategy
Definition: vacuumlazy.c:323
#define VACOPT_VERBOSE
Definition: vacuum.h:180
void pgstat_report_vacuum(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples)
Definition: pgstat.c:1578
int64 wal_records
Definition: instrument.h:49
Relation rel
Definition: vacuumlazy.c:308
BlockNumber rel_pages
Definition: vacuumlazy.c:349
void * palloc(Size size)
Definition: mcxt.c:1062
char * relnamespace
Definition: vacuumlazy.c:338
BlockNumber pages_newly_deleted
Definition: genam.h:80
bool MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3173
int i
static bool should_attempt_truncation(LVRelState *vacrel)
Definition: vacuumlazy.c:3151
uint64 wal_bytes
Definition: instrument.h:51
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:1306
double old_live_tuples
Definition: vacuumlazy.c:329
#define _(x)
Definition: elog.c:89
#define RelationGetRelid(relation)
Definition: rel.h:477
int multixact_freeze_min_age
Definition: vacuum.h:214
bool consider_bypass_optimization
Definition: vacuumlazy.c:315
bool track_io_timing
Definition: bufmgr.c:135
bool do_rel_truncate
Definition: vacuumlazy.c:320
#define RelationGetNamespace(relation)
Definition: rel.h:518

◆ lazy_check_needs_freeze()

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

Definition at line 2526 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().

2527 {
2528  Page page = BufferGetPage(buf);
2529  OffsetNumber offnum,
2530  maxoff;
2531  HeapTupleHeader tupleheader;
2532 
2533  *hastup = false;
2534 
2535  /*
2536  * New and empty pages, obviously, don't contain tuples. We could make
2537  * sure that the page is registered in the FSM, but it doesn't seem worth
2538  * waiting for a cleanup lock just for that, especially because it's
2539  * likely that the pin holder will do so.
2540  */
2541  if (PageIsNew(page) || PageIsEmpty(page))
2542  return false;
2543 
2544  maxoff = PageGetMaxOffsetNumber(page);
2545  for (offnum = FirstOffsetNumber;
2546  offnum <= maxoff;
2547  offnum = OffsetNumberNext(offnum))
2548  {
2549  ItemId itemid;
2550 
2551  /*
2552  * Set the offset number so that we can display it along with any
2553  * error that occurred while processing this tuple.
2554  */
2555  vacrel->offnum = offnum;
2556  itemid = PageGetItemId(page, offnum);
2557 
2558  /* this should match hastup test in count_nondeletable_pages() */
2559  if (ItemIdIsUsed(itemid))
2560  *hastup = true;
2561 
2562  /* dead and redirect items never need freezing */
2563  if (!ItemIdIsNormal(itemid))
2564  continue;
2565 
2566  tupleheader = (HeapTupleHeader) PageGetItem(page, itemid);
2567 
2568  if (heap_tuple_needs_freeze(tupleheader, vacrel->FreezeLimit,
2569  vacrel->MultiXactCutoff, buf))
2570  break;
2571  } /* scan along page */
2572 
2573  /* Clear the offset information once we have processed the given page. */
2574  vacrel->offnum = InvalidOffsetNumber;
2575 
2576  return (offnum <= maxoff);
2577 }
#define PageIsEmpty(page)
Definition: bufpage.h:222
OffsetNumber offnum
Definition: vacuumlazy.c:342
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
MultiXactId MultiXactCutoff
Definition: vacuumlazy.c:335
#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:7100
TransactionId FreezeLimit
Definition: vacuumlazy.c:334
#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 2592 of file vacuumlazy.c.

References LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, LVRelState::do_rel_truncate, ereport, errdetail(), errhint(), errmsg(), LVRelState::failsafe_active, get_database_name(), MyDatabaseId, LVRelState::num_index_scans, 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().

2593 {
2594  /* Don't warn more than once per VACUUM */
2595  if (vacrel->failsafe_active)
2596  return true;
2597 
2599  vacrel->relminmxid)))
2600  {
2601  vacrel->failsafe_active = true;
2602 
2603  /* Disable index vacuuming, index cleanup, and heap rel truncation */
2604  vacrel->do_index_vacuuming = false;
2605  vacrel->do_index_cleanup = false;
2606  vacrel->do_rel_truncate = false;
2607 
2608  ereport(WARNING,
2609  (errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans",
2611  vacrel->relnamespace,
2612  vacrel->relname,
2613  vacrel->num_index_scans),
2614  errdetail("The table's relfrozenxid or relminmxid is too far in the past."),
2615  errhint("Consider increasing configuration parameter \"maintenance_work_mem\" or \"autovacuum_work_mem\".\n"
2616  "You might also need to consider other ways for VACUUM to keep up with the allocation of transaction IDs.")));
2617 
2618  /* Stop applying cost limits from this point on */
2619  VacuumCostActive = false;
2620  VacuumCostBalance = 0;
2621 
2622  return true;
2623  }
2624 
2625  return false;
2626 }
int errhint(const char *fmt,...)
Definition: elog.c:1156
int VacuumCostBalance
Definition: globals.c:151
bool do_index_vacuuming
Definition: vacuumlazy.c:318
int num_index_scans
Definition: vacuumlazy.c:365
bool do_index_cleanup
Definition: vacuumlazy.c:319
bool failsafe_active
Definition: vacuumlazy.c:313
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2113
int errdetail(const char *fmt,...)
Definition: elog.c:1042
bool vacuum_xid_failsafe_check(TransactionId relfrozenxid, MultiXactId relminmxid)
Definition: vacuum.c:1163
#define WARNING
Definition: elog.h:40
Oid MyDatabaseId
Definition: globals.c:88
MultiXactId relminmxid
Definition: vacuumlazy.c:328
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:339
TransactionId relfrozenxid
Definition: vacuumlazy.c:327
int errmsg(const char *fmt,...)
Definition: elog.c:909
char * relnamespace
Definition: vacuumlazy.c:338
#define unlikely(x)
Definition: c.h:273
bool do_rel_truncate
Definition: vacuumlazy.c:320
bool VacuumCostActive
Definition: globals.c:152

◆ lazy_cleanup_all_indexes()

static void lazy_cleanup_all_indexes ( LVRelState vacrel)
static

Definition at line 2972 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().

2973 {
2975  Assert(vacrel->nindexes > 0);
2976 
2977  /* Report that we are now cleaning up indexes */
2980 
2981  if (!ParallelVacuumIsActive(vacrel))
2982  {
2983  double reltuples = vacrel->new_rel_tuples;
2984  bool estimated_count =
2985  vacrel->tupcount_pages < vacrel->rel_pages;
2986 
2987  for (int idx = 0; idx < vacrel->nindexes; idx++)
2988  {
2989  Relation indrel = vacrel->indrels[idx];
2990  IndexBulkDeleteResult *istat = vacrel->indstats[idx];
2991 
2992  vacrel->indstats[idx] =
2993  lazy_cleanup_one_index(indrel, istat, reltuples,
2994  estimated_count, vacrel);
2995  }
2996  }
2997  else
2998  {
2999  /* Outsource everything to parallel variant */
3001  }
3002 }
Relation * indrels
Definition: vacuumlazy.c:309
int nindexes
Definition: vacuumlazy.c:310
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:362
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
static IndexBulkDeleteResult * lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, double reltuples, bool estimated_count, LVRelState *vacrel)
Definition: vacuumlazy.c:3071
#define IsParallelWorker()
Definition: parallel.h:61
double new_rel_tuples
Definition: vacuumlazy.c:359
#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:353
BlockNumber rel_pages
Definition: vacuumlazy.c:349
static void do_parallel_lazy_cleanup_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2653

◆ 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 3071 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, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, pfree(), pg_rusage_init(), pg_rusage_show(), pstrdup(), RelationGetRelationName, IndexVacuumInfo::report_progress, restore_vacuum_error_info(), IndexVacuumInfo::strategy, IndexBulkDeleteResult::tuples_removed, update_vacuum_error_info(), and VACUUM_ERRCB_PHASE_INDEX_CLEANUP.

Referenced by lazy_cleanup_all_indexes(), and parallel_process_one_index().

3074 {
3075  IndexVacuumInfo ivinfo;
3076  PGRUsage ru0;
3077  LVSavedErrInfo saved_err_info;
3078 
3079  pg_rusage_init(&ru0);
3080 
3081  ivinfo.index = indrel;
3082  ivinfo.analyze_only = false;
3083  ivinfo.report_progress = false;
3084  ivinfo.estimated_count = estimated_count;
3085  ivinfo.message_level = elevel;
3086 
3087  ivinfo.num_heap_tuples = reltuples;
3088  ivinfo.strategy = vacrel->bstrategy;
3089 
3090  /*
3091  * Update error traceback information.
3092  *
3093  * The index name is saved during this phase and restored immediately
3094  * after this phase. See vacuum_error_callback.
3095  */
3096  Assert(vacrel->indname == NULL);
3097  vacrel->indname = pstrdup(RelationGetRelationName(indrel));
3098  update_vacuum_error_info(vacrel, &saved_err_info,
3101 
3102  istat = index_vacuum_cleanup(&ivinfo, istat);
3103 
3104  if (istat)
3105  {
3106  ereport(elevel,
3107  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
3108  RelationGetRelationName(indrel),
3109  istat->num_index_tuples,
3110  istat->num_pages),
3111  errdetail("%.0f index row versions were removed.\n"
3112  "%u index pages were newly deleted.\n"
3113  "%u index pages are currently deleted, of which %u are currently reusable.\n"
3114  "%s.",
3115  istat->tuples_removed,
3116  istat->pages_newly_deleted,
3117  istat->pages_deleted, istat->pages_free,
3118  pg_rusage_show(&ru0))));
3119  }
3120 
3121  /* Revert to the previous phase information for error traceback */
3122  restore_vacuum_error_info(vacrel, &saved_err_info);
3123  pfree(vacrel->indname);
3124  vacrel->indname = NULL;
3125 
3126  return istat;
3127 }
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4308
double tuples_removed
Definition: genam.h:79
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
BlockNumber num_pages
Definition: genam.h:76
BlockNumber pages_free
Definition: genam.h:82
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:511
BlockNumber pages_deleted
Definition: genam.h:81
static int elevel
Definition: vacuumlazy.c:401
char * indname
Definition: vacuumlazy.c:340
#define InvalidOffsetNumber
Definition: off.h:26
static void restore_vacuum_error_info(LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel)
Definition: vacuumlazy.c:4327
#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:323
int errmsg(const char *fmt,...)
Definition: elog.c:909
BlockNumber pages_newly_deleted
Definition: genam.h:80
double num_index_tuples
Definition: genam.h:78
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 906 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(), LVRelState::consider_bypass_optimization, StringInfoData::data, LVRelState::dead_tuples, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, elevel, elog, END_CRIT_SECTION, ereport, errdetail_internal(), errmsg(), FAILSAFE_EVERY_PAGES, 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, 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().

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

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

3476 {
3477  LVDeadTuples *dead_tuples;
3478  long maxtuples;
3479 
3480  /*
3481  * Initialize state for a parallel vacuum. As of now, only one worker can
3482  * be used for an index, so we invoke parallelism only if there are at
3483  * least two indexes on a table.
3484  */
3485  if (nworkers >= 0 && vacrel->nindexes > 1 && vacrel->do_index_vacuuming)
3486  {
3487  /*
3488  * Since parallel workers cannot access data in temporary tables, we
3489  * can't perform parallel vacuum on them.
3490  */
3491  if (RelationUsesLocalBuffers(vacrel->rel))
3492  {
3493  /*
3494  * Give warning only if the user explicitly tries to perform a
3495  * parallel vacuum on the temporary table.
3496  */
3497  if (nworkers > 0)
3498  ereport(WARNING,
3499  (errmsg("disabling parallel option of vacuum on \"%s\" --- cannot vacuum temporary tables in parallel",
3500  vacrel->relname)));
3501  }
3502  else
3503  vacrel->lps = begin_parallel_vacuum(vacrel, nblocks, nworkers);
3504 
3505  /* If parallel mode started, we're done */
3506  if (ParallelVacuumIsActive(vacrel))
3507  return;
3508  }
3509 
3510  maxtuples = compute_max_dead_tuples(nblocks, vacrel->nindexes > 0);
3511 
3512  dead_tuples = (LVDeadTuples *) palloc(SizeOfDeadTuples(maxtuples));
3513  dead_tuples->num_tuples = 0;
3514  dead_tuples->max_tuples = (int) maxtuples;
3515 
3516  vacrel->dead_tuples = dead_tuples;
3517 }
LVParallelState * lps
Definition: vacuumlazy.c:324
bool do_index_vacuuming
Definition: vacuumlazy.c:318
#define SizeOfDeadTuples(cnt)
Definition: vacuumlazy.c:190
int nindexes
Definition: vacuumlazy.c:310
static LVParallelState * begin_parallel_vacuum(LVRelState *vacrel, BlockNumber nblocks, int nrequested)
Definition: vacuumlazy.c:3831
static long compute_max_dead_tuples(BlockNumber relblocks, bool hasindex)
Definition: vacuumlazy.c:3443
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:348
#define WARNING
Definition: elog.h:40
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:339
#define RelationUsesLocalBuffers(relation)
Definition: rel.h:610
Relation rel
Definition: vacuumlazy.c:308
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 3523 of file vacuumlazy.c.

References end_parallel_vacuum(), and ParallelVacuumIsActive.

Referenced by lazy_scan_heap().

3524 {
3525  if (!ParallelVacuumIsActive(vacrel))
3526  return;
3527 
3528  /*
3529  * End parallel mode before updating index statistics as we cannot write
3530  * during parallel mode.
3531  */
3532  end_parallel_vacuum(vacrel);
3533 }
static void end_parallel_vacuum(LVRelState *vacrel)
Definition: vacuumlazy.c:4030
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161

◆ lazy_tid_reaped()

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

Definition at line 3543 of file vacuumlazy.c.

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

Referenced by lazy_vacuum_one_index().

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

◆ lazy_truncate_heap()

static void lazy_truncate_heap ( LVRelState vacrel)
static

Definition at line 3172 of file vacuumlazy.c.

References AccessExclusiveLock, LVRelState::blkno, CHECK_FOR_INTERRUPTS, ConditionalLockRelation(), count_nondeletable_pages(), elevel, ereport, errdetail_internal(), errmsg(), MyLatch, LVRelState::nonempty_pages, LVRelState::pages_removed, pg_rusage_init(), pg_rusage_show(), pgstat_progress_update_param(), PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_TRUNCATE, LVRelState::rel, LVRelState::rel_pages, RelationGetNumberOfBlocks, RelationTruncate(), LVRelState::relname, ResetLatch(), UnlockRelation(), VACUUM_TRUNCATE_LOCK_TIMEOUT, VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL, WAIT_EVENT_VACUUM_TRUNCATE, WaitLatch(), WL_EXIT_ON_PM_DEATH, WL_LATCH_SET, and WL_TIMEOUT.

Referenced by heap_vacuum_rel().

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

◆ lazy_vacuum()

static void lazy_vacuum ( LVRelState vacrel)
static

Definition at line 2079 of file vacuumlazy.c.

References Assert, BYPASS_THRESHOLD_PAGES, LVRelState::consider_bypass_optimization, LVRelState::dead_tuples, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, elevel, ereport, errmsg(), LVRelState::failsafe_active, 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().

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

◆ lazy_vacuum_all_indexes()

static bool lazy_vacuum_all_indexes ( LVRelState vacrel)
static

Definition at line 2210 of file vacuumlazy.c.

References Assert, LVRelState::dead_tuples, LVRelState::do_index_cleanup, LVRelState::do_index_vacuuming, do_parallel_lazy_vacuum_all_indexes(), LVRelState::failsafe_active, 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().

2211 {
2212  bool allindexes = true;
2213 
2215  Assert(vacrel->nindexes > 0);
2216  Assert(vacrel->do_index_vacuuming);
2217  Assert(vacrel->do_index_cleanup);
2220 
2221  /* Precheck for XID wraparound emergencies */
2222  if (lazy_check_wraparound_failsafe(vacrel))
2223  {
2224  /* Wraparound emergency -- don't even start an index scan */
2225  return false;
2226  }
2227 
2228  /* Report that we are now vacuuming indexes */
2231 
2232  if (!ParallelVacuumIsActive(vacrel))
2233  {
2234  for (int idx = 0; idx < vacrel->nindexes; idx++)
2235  {
2236  Relation indrel = vacrel->indrels[idx];
2237  IndexBulkDeleteResult *istat = vacrel->indstats[idx];
2238 
2239  vacrel->indstats[idx] =
2240  lazy_vacuum_one_index(indrel, istat, vacrel->old_live_tuples,
2241  vacrel);
2242 
2243  if (lazy_check_wraparound_failsafe(vacrel))
2244  {
2245  /* Wraparound emergency -- end current index scan */
2246  allindexes = false;
2247  break;
2248  }
2249  }
2250  }
2251  else
2252  {
2253  /* Outsource everything to parallel variant */
2255 
2256  /*
2257  * Do a postcheck to consider applying wraparound failsafe now. Note
2258  * that parallel VACUUM only gets the precheck and this postcheck.
2259  */
2260  if (lazy_check_wraparound_failsafe(vacrel))
2261  allindexes = false;
2262  }
2263 
2264  /*
2265  * We delete all LP_DEAD items from the first heap pass in all indexes on
2266  * each call here (except calls where we choose to do the failsafe). This
2267  * makes the next call to lazy_vacuum_heap_rel() safe (except in the event
2268  * of the failsafe triggering, which prevents the next call from taking
2269  * place).
2270  */
2271  Assert(vacrel->num_index_scans > 0 ||
2272  vacrel->dead_tuples->num_tuples == vacrel->lpdead_items);
2273  Assert(allindexes || vacrel->failsafe_active);
2274 
2275  /*
2276  * Increase and report the number of index scans.
2277  *
2278  * We deliberately include the case where we started a round of bulk
2279  * deletes that we weren't able to finish due to the failsafe triggering.
2280  */
2281  vacrel->num_index_scans++;
2283  vacrel->num_index_scans);
2284 
2285  return allindexes;
2286 }
static bool lazy_check_wraparound_failsafe(LVRelState *vacrel)
Definition: vacuumlazy.c:2592
Relation * indrels
Definition: vacuumlazy.c:309
bool do_index_vacuuming
Definition: vacuumlazy.c:318
int num_index_scans
Definition: vacuumlazy.c:365
#define PROGRESS_VACUUM_PHASE_VACUUM_INDEX
Definition: progress.h:31
int nindexes
Definition: vacuumlazy.c:310
bool do_index_cleanup
Definition: vacuumlazy.c:319
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
int64 lpdead_items
Definition: vacuumlazy.c:367
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:362
bool failsafe_active
Definition: vacuumlazy.c:313
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:348
#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:3016
static void do_parallel_lazy_vacuum_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2632
MultiXactId relminmxid
Definition: vacuumlazy.c:328
TransactionId relfrozenxid
Definition: vacuumlazy.c:327
#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:329

◆ lazy_vacuum_heap_page()

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

Definition at line 2404 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().

2406 {
2407  LVDeadTuples *dead_tuples = vacrel->dead_tuples;
2408  Page page = BufferGetPage(buffer);
2410  int uncnt = 0;
2411  TransactionId visibility_cutoff_xid;
2412  bool all_frozen;
2413  LVSavedErrInfo saved_err_info;
2414 
2415  Assert(vacrel->nindexes == 0 || vacrel->do_index_vacuuming);
2416 
2418 
2419  /* Update error traceback information */
2420  update_vacuum_error_info(vacrel, &saved_err_info,
2423 
2425 
2426  for (; tupindex < dead_tuples->num_tuples; tupindex++)
2427  {
2428  BlockNumber tblk;
2429  OffsetNumber toff;
2430  ItemId itemid;
2431 
2432  tblk = ItemPointerGetBlockNumber(&dead_tuples->itemptrs[tupindex]);
2433  if (tblk != blkno)
2434  break; /* past end of tuples for this block */
2435  toff = ItemPointerGetOffsetNumber(&dead_tuples->itemptrs[tupindex]);
2436  itemid = PageGetItemId(page, toff);
2437 
2438  Assert(ItemIdIsDead(itemid) && !ItemIdHasStorage(itemid));
2439  ItemIdSetUnused(itemid);
2440  unused[uncnt++] = toff;
2441  }
2442 
2443  Assert(uncnt > 0);
2444 
2445  /* Attempt to truncate line pointer array now */
2447 
2448  /*
2449  * Mark buffer dirty before we write WAL.
2450  */
2451  MarkBufferDirty(buffer);
2452 
2453  /* XLOG stuff */
2454  if (RelationNeedsWAL(vacrel->rel))
2455  {
2456  xl_heap_vacuum xlrec;
2457  XLogRecPtr recptr;
2458 
2459  xlrec.nunused = uncnt;
2460 
2461  XLogBeginInsert();
2462  XLogRegisterData((char *) &xlrec, SizeOfHeapVacuum);
2463 
2464  XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
2465  XLogRegisterBufData(0, (char *) unused, uncnt * sizeof(OffsetNumber));
2466 
2467  recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_VACUUM);
2468 
2469  PageSetLSN(page, recptr);
2470  }
2471 
2472  /*
2473  * End critical section, so we safely can do visibility tests (which
2474  * possibly need to perform IO and allocate memory!). If we crash now the
2475  * page (including the corresponding vm bit) might not be marked all
2476  * visible, but that's fine. A later vacuum will fix that.
2477  */
2478  END_CRIT_SECTION();
2479 
2480  /*
2481  * Now that we have removed the LD_DEAD items from the page, once again
2482  * check if the page has become all-visible. The page is already marked
2483  * dirty, exclusively locked, and, if needed, a full page image has been
2484  * emitted.
2485  */
2486  if (heap_page_is_all_visible(vacrel, buffer, &visibility_cutoff_xid,
2487  &all_frozen))
2488  PageSetAllVisible(page);
2489 
2490  /*
2491  * All the changes to the heap page have been done. If the all-visible
2492  * flag is now set, also set the VM all-visible bit (and, if possible, the
2493  * all-frozen bit) unless this has already been done previously.
2494  */
2495  if (PageIsAllVisible(page))
2496  {
2497  uint8 flags = 0;
2498  uint8 vm_status = visibilitymap_get_status(vacrel->rel,
2499  blkno, vmbuffer);
2500 
2501  /* Set the VM all-frozen bit to flag, if needed */
2502  if ((vm_status & VISIBILITYMAP_ALL_VISIBLE) == 0)
2503  flags |= VISIBILITYMAP_ALL_VISIBLE;
2504  if ((vm_status & VISIBILITYMAP_ALL_FROZEN) == 0 && all_frozen)
2505  flags |= VISIBILITYMAP_ALL_FROZEN;
2506 
2507  Assert(BufferIsValid(*vmbuffer));
2508  if (flags != 0)
2509  visibilitymap_set(vacrel->rel, blkno, buffer, InvalidXLogRecPtr,
2510  *vmbuffer, visibility_cutoff_xid, flags);
2511  }
2512 
2513  /* Revert to the previous phase information for error traceback */
2514  restore_vacuum_error_info(vacrel, &saved_err_info);
2515  return tupindex;
2516 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:378
#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:4308
ItemPointerData itemptrs[FLEXIBLE_ARRAY_MEMBER]
Definition: vacuumlazy.c:185
bool do_index_vacuuming
Definition: vacuumlazy.c:318
#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:1565
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:232
int nindexes
Definition: vacuumlazy.c:310
#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:3610
uint16 OffsetNumber
Definition: off.h:24
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:348
#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:340
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:434
#define InvalidOffsetNumber
Definition: off.h:26
static void restore_vacuum_error_info(LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel)
Definition: vacuumlazy.c:4327
#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 VISIBILITYMAP_ALL_FROZEN
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123