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 3845 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().

3847 {
3848  LVParallelState *lps = NULL;
3849  Relation *indrels = vacrel->indrels;
3850  int nindexes = vacrel->nindexes;
3851  ParallelContext *pcxt;
3852  LVShared *shared;
3853  LVDeadTuples *dead_tuples;
3854  BufferUsage *buffer_usage;
3855  WalUsage *wal_usage;
3856  bool *can_parallel_vacuum;
3857  long maxtuples;
3858  Size est_shared;
3859  Size est_deadtuples;
3860  int nindexes_mwm = 0;
3861  int parallel_workers = 0;
3862  int querylen;
3863 
3864  /*
3865  * A parallel vacuum must be requested and there must be indexes on the
3866  * relation
3867  */
3868  Assert(nrequested >= 0);
3869  Assert(nindexes > 0);
3870 
3871  /*
3872  * Compute the number of parallel vacuum workers to launch
3873  */
3874  can_parallel_vacuum = (bool *) palloc0(sizeof(bool) * nindexes);
3875  parallel_workers = compute_parallel_vacuum_workers(vacrel,
3876  nrequested,
3877  can_parallel_vacuum);
3878 
3879  /* Can't perform vacuum in parallel */
3880  if (parallel_workers <= 0)
3881  {
3882  pfree(can_parallel_vacuum);
3883  return lps;
3884  }
3885 
3886  lps = (LVParallelState *) palloc0(sizeof(LVParallelState));
3887 
3889  pcxt = CreateParallelContext("postgres", "parallel_vacuum_main",
3890  parallel_workers);
3891  Assert(pcxt->nworkers > 0);
3892  lps->pcxt = pcxt;
3893 
3894  /* Estimate size for shared information -- PARALLEL_VACUUM_KEY_SHARED */
3895  est_shared = MAXALIGN(add_size(SizeOfLVShared, BITMAPLEN(nindexes)));
3896  for (int idx = 0; idx < nindexes; idx++)
3897  {
3898  Relation indrel = indrels[idx];
3899  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
3900 
3901  /*
3902  * Cleanup option should be either disabled, always performing in
3903  * parallel or conditionally performing in parallel.
3904  */
3905  Assert(((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) == 0) ||
3906  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) == 0));
3907  Assert(vacoptions <= VACUUM_OPTION_MAX_VALID_VALUE);
3908 
3909  /* Skip indexes that don't participate in parallel vacuum */
3910  if (!can_parallel_vacuum[idx])
3911  continue;
3912 
3913  if (indrel->rd_indam->amusemaintenanceworkmem)
3914  nindexes_mwm++;
3915 
3916  est_shared = add_size(est_shared, sizeof(LVSharedIndStats));
3917 
3918  /*
3919  * Remember the number of indexes that support parallel operation for
3920  * each phase.
3921  */
3922  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
3924  if ((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0)
3926  if ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0)
3928  }
3929  shm_toc_estimate_chunk(&pcxt->estimator, est_shared);
3930  shm_toc_estimate_keys(&pcxt->estimator, 1);
3931 
3932  /* Estimate size for dead tuples -- PARALLEL_VACUUM_KEY_DEAD_TUPLES */
3933  maxtuples = compute_max_dead_tuples(nblocks, true);
3934  est_deadtuples = MAXALIGN(SizeOfDeadTuples(maxtuples));
3935  shm_toc_estimate_chunk(&pcxt->estimator, est_deadtuples);
3936  shm_toc_estimate_keys(&pcxt->estimator, 1);
3937 
3938  /*
3939  * Estimate space for BufferUsage and WalUsage --
3940  * PARALLEL_VACUUM_KEY_BUFFER_USAGE and PARALLEL_VACUUM_KEY_WAL_USAGE.
3941  *
3942  * If there are no extensions loaded that care, we could skip this. We
3943  * have no way of knowing whether anyone's looking at pgBufferUsage or
3944  * pgWalUsage, so do it unconditionally.
3945  */
3947  mul_size(sizeof(BufferUsage), pcxt->nworkers));
3948  shm_toc_estimate_keys(&pcxt->estimator, 1);
3950  mul_size(sizeof(WalUsage), pcxt->nworkers));
3951  shm_toc_estimate_keys(&pcxt->estimator, 1);
3952 
3953  /* Finally, estimate PARALLEL_VACUUM_KEY_QUERY_TEXT space */
3954  if (debug_query_string)
3955  {
3956  querylen = strlen(debug_query_string);
3957  shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
3958  shm_toc_estimate_keys(&pcxt->estimator, 1);
3959  }
3960  else
3961  querylen = 0; /* keep compiler quiet */
3962 
3963  InitializeParallelDSM(pcxt);
3964 
3965  /* Prepare shared information */
3966  shared = (LVShared *) shm_toc_allocate(pcxt->toc, est_shared);
3967  MemSet(shared, 0, est_shared);
3968  shared->relid = RelationGetRelid(vacrel->rel);
3969  shared->elevel = elevel;
3970  shared->maintenance_work_mem_worker =
3971  (nindexes_mwm > 0) ?
3972  maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
3974 
3975  pg_atomic_init_u32(&(shared->cost_balance), 0);
3976  pg_atomic_init_u32(&(shared->active_nworkers), 0);
3977  pg_atomic_init_u32(&(shared->idx), 0);
3978  shared->offset = MAXALIGN(add_size(SizeOfLVShared, BITMAPLEN(nindexes)));
3979 
3980  /*
3981  * Initialize variables for shared index statistics, set NULL bitmap and
3982  * the size of stats for each index.
3983  */
3984  memset(shared->bitmap, 0x00, BITMAPLEN(nindexes));
3985  for (int idx = 0; idx < nindexes; idx++)
3986  {
3987  if (!can_parallel_vacuum[idx])
3988  continue;
3989 
3990  /* Set NOT NULL as this index does support parallelism */
3991  shared->bitmap[idx >> 3] |= 1 << (idx & 0x07);
3992  }
3993 
3995  lps->lvshared = shared;
3996 
3997  /* Prepare the dead tuple space */
3998  dead_tuples = (LVDeadTuples *) shm_toc_allocate(pcxt->toc, est_deadtuples);
3999  dead_tuples->max_tuples = maxtuples;
4000  dead_tuples->num_tuples = 0;
4001  MemSet(dead_tuples->itemptrs, 0, sizeof(ItemPointerData) * maxtuples);
4002  shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_DEAD_TUPLES, dead_tuples);
4003  vacrel->dead_tuples = dead_tuples;
4004 
4005  /*
4006  * Allocate space for each worker's BufferUsage and WalUsage; no need to
4007  * initialize
4008  */
4009  buffer_usage = shm_toc_allocate(pcxt->toc,
4010  mul_size(sizeof(BufferUsage), pcxt->nworkers));
4011  shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_BUFFER_USAGE, buffer_usage);
4012  lps->buffer_usage = buffer_usage;
4013  wal_usage = shm_toc_allocate(pcxt->toc,
4014  mul_size(sizeof(WalUsage), pcxt->nworkers));
4016  lps->wal_usage = wal_usage;
4017 
4018  /* Store query string for workers */
4019  if (debug_query_string)
4020  {
4021  char *sharedquery;
4022 
4023  sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
4024  memcpy(sharedquery, debug_query_string, querylen + 1);
4025  sharedquery[querylen] = '\0';
4026  shm_toc_insert(pcxt->toc,
4027  PARALLEL_VACUUM_KEY_QUERY_TEXT, sharedquery);
4028  }
4029 
4030  pfree(can_parallel_vacuum);
4031  return lps;
4032 }
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:3457
#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:350
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:403
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:3751
#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 3457 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().

3458 {
3459  long maxtuples;
3460  int vac_work_mem = IsAutoVacuumWorkerProcess() &&
3461  autovacuum_work_mem != -1 ?
3463 
3464  if (hasindex)
3465  {
3466  maxtuples = MAXDEADTUPLES(vac_work_mem * 1024L);
3467  maxtuples = Min(maxtuples, INT_MAX);
3468  maxtuples = Min(maxtuples, MAXDEADTUPLES(MaxAllocSize));
3469 
3470  /* curious coding here to ensure the multiplication can't overflow */
3471  if ((BlockNumber) (maxtuples / LAZY_ALLOC_TUPLES) > relblocks)
3472  maxtuples = relblocks * LAZY_ALLOC_TUPLES;
3473 
3474  /* stay sane if small maintenance_work_mem */
3475  maxtuples = Max(maxtuples, MaxHeapTuplesPerPage);
3476  }
3477  else
3478  maxtuples = MaxHeapTuplesPerPage;
3479 
3480  return maxtuples;
3481 }
int autovacuum_work_mem
Definition: autovacuum.c:117
#define Min(x, y)
Definition: c.h:986
#define MaxHeapTuplesPerPage
Definition: htup_details.h:573
uint32 BlockNumber
Definition: block.h:31
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3454
#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 3751 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().

3753 {
3754  int nindexes_parallel = 0;
3755  int nindexes_parallel_bulkdel = 0;
3756  int nindexes_parallel_cleanup = 0;
3757  int parallel_workers;
3758 
3759  /*
3760  * We don't allow performing parallel operation in standalone backend or
3761  * when parallelism is disabled.
3762  */
3764  return 0;
3765 
3766  /*
3767  * Compute the number of indexes that can participate in parallel vacuum.
3768  */
3769  for (int idx = 0; idx < vacrel->nindexes; idx++)
3770  {
3771  Relation indrel = vacrel->indrels[idx];
3772  uint8 vacoptions = indrel->rd_indam->amparallelvacuumoptions;
3773 
3774  if (vacoptions == VACUUM_OPTION_NO_PARALLEL ||
3776  continue;
3777 
3778  can_parallel_vacuum[idx] = true;
3779 
3780  if ((vacoptions & VACUUM_OPTION_PARALLEL_BULKDEL) != 0)
3781  nindexes_parallel_bulkdel++;
3782  if (((vacoptions & VACUUM_OPTION_PARALLEL_CLEANUP) != 0) ||
3783  ((vacoptions & VACUUM_OPTION_PARALLEL_COND_CLEANUP) != 0))
3784  nindexes_parallel_cleanup++;
3785  }
3786 
3787  nindexes_parallel = Max(nindexes_parallel_bulkdel,
3788  nindexes_parallel_cleanup);
3789 
3790  /* The leader process takes one index */
3791  nindexes_parallel--;
3792 
3793  /* No index supports parallel vacuum */
3794  if (nindexes_parallel <= 0)
3795  return 0;
3796 
3797  /* Compute the parallel degree */
3798  parallel_workers = (nrequested > 0) ?
3799  Min(nrequested, nindexes_parallel) : nindexes_parallel;
3800 
3801  /* Cap by max_parallel_maintenance_workers */
3802  parallel_workers = Min(parallel_workers, max_parallel_maintenance_workers);
3803 
3804  return parallel_workers;
3805 }
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:213
#define Max(x, y)
Definition: c.h:980
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:52
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:45
#define VACUUM_OPTION_PARALLEL_CLEANUP
Definition: vacuum.h:60

◆ count_nondeletable_pages()

static BlockNumber count_nondeletable_pages ( LVRelState vacrel,
bool lock_waiter_detected 
)
static

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

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

2668 {
2669  int nworkers;
2670 
2671  /*
2672  * If parallel vacuum is active we perform index cleanup with parallel
2673  * workers.
2674  *
2675  * Tell parallel workers to do index cleanup.
2676  */
2677  vacrel->lps->lvshared->for_cleanup = true;
2678  vacrel->lps->lvshared->first_time = (vacrel->num_index_scans == 0);
2679 
2680  /*
2681  * Now we can provide a better estimate of total number of surviving
2682  * tuples (we assume indexes are more interested in that than in the
2683  * number of nominally live tuples).
2684  */
2685  vacrel->lps->lvshared->reltuples = vacrel->new_rel_tuples;
2686  vacrel->lps->lvshared->estimated_count =
2687  (vacrel->tupcount_pages < vacrel->rel_pages);
2688 
2689  /* Determine the number of parallel workers to launch */
2690  if (vacrel->lps->lvshared->first_time)
2691  nworkers = vacrel->lps->nindexes_parallel_cleanup +
2693  else
2694  nworkers = vacrel->lps->nindexes_parallel_cleanup;
2695 
2696  do_parallel_vacuum_or_cleanup(vacrel, nworkers);
2697 }
LVParallelState * lps
Definition: vacuumlazy.c:324
bool estimated_count
Definition: vacuumlazy.c:228
int num_index_scans
Definition: vacuumlazy.c:367
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:361
BlockNumber tupcount_pages
Definition: vacuumlazy.c:355
LVShared * lvshared
Definition: vacuumlazy.c:288
bool for_cleanup
Definition: vacuumlazy.c:214
BlockNumber rel_pages
Definition: vacuumlazy.c:351
static void do_parallel_vacuum_or_cleanup(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:2706

◆ do_parallel_lazy_vacuum_all_indexes()

static void do_parallel_lazy_vacuum_all_indexes ( LVRelState vacrel)
static

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

2647 {
2648  /* Tell parallel workers to do index vacuuming */
2649  vacrel->lps->lvshared->for_cleanup = false;
2650  vacrel->lps->lvshared->first_time = false;
2651 
2652  /*
2653  * We can only provide an approximate value of num_heap_tuples in vacuum
2654  * cases.
2655  */
2656  vacrel->lps->lvshared->reltuples = vacrel->old_live_tuples;
2657  vacrel->lps->lvshared->estimated_count = true;
2658 
2660  vacrel->lps->nindexes_parallel_bulkdel);
2661 }
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:328
static void do_parallel_vacuum_or_cleanup(LVRelState *vacrel, int nworkers)
Definition: vacuumlazy.c:2706

◆ do_parallel_processing()

static void do_parallel_processing ( LVRelState vacrel,
LVShared lvshared 
)
static

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

2820 {
2821  /*
2822  * Increment the active worker count if we are able to launch any worker.
2823  */
2826 
2827  /* Loop until all indexes are vacuumed */
2828  for (;;)
2829  {
2830  int idx;
2831  LVSharedIndStats *shared_istat;
2832  Relation indrel;
2833  IndexBulkDeleteResult *istat;
2834 
2835  /* Get an index number to process */
2836  idx = pg_atomic_fetch_add_u32(&(lvshared->idx), 1);
2837 
2838  /* Done for all indexes? */
2839  if (idx >= vacrel->nindexes)
2840  break;
2841 
2842  /* Get the index statistics of this index from DSM */
2843  shared_istat = parallel_stats_for_idx(lvshared, idx);
2844 
2845  /* Skip indexes not participating in parallelism */
2846  if (shared_istat == NULL)
2847  continue;
2848 
2849  indrel = vacrel->indrels[idx];
2850 
2851  /*
2852  * Skip processing indexes that are unsafe for workers (these are
2853  * processed in do_serial_processing_for_unsafe_indexes() by leader)
2854  */
2855  if (!parallel_processing_is_safe(indrel, lvshared))
2856  continue;
2857 
2858  /* Do vacuum or cleanup of the index */
2859  istat = (vacrel->indstats[idx]);
2860  vacrel->indstats[idx] = parallel_process_one_index(indrel, istat,
2861  lvshared,
2862  shared_istat,
2863  vacrel);
2864  }
2865 
2866  /*
2867  * We have completed the index vacuum so decrement the active worker
2868  * count.
2869  */
2872 }
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:2932
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:364
pg_atomic_uint32 idx
Definition: vacuumlazy.c:259
static bool parallel_processing_is_safe(Relation indrel, LVShared *lvshared)
Definition: vacuumlazy.c:4111
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4087
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 2706 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().

2707 {
2708  LVParallelState *lps = vacrel->lps;
2709 
2711  Assert(ParallelVacuumIsActive(vacrel));
2712  Assert(vacrel->nindexes > 0);
2713 
2714  /* The leader process will participate */
2715  nworkers--;
2716 
2717  /*
2718  * It is possible that parallel context is initialized with fewer workers
2719  * than the number of indexes that need a separate worker in the current
2720  * phase, so we need to consider it. See compute_parallel_vacuum_workers.
2721  */
2722  nworkers = Min(nworkers, lps->pcxt->nworkers);
2723 
2724  /* Setup the shared cost-based vacuum delay and launch workers */
2725  if (nworkers > 0)
2726  {
2727  if (vacrel->num_index_scans > 0)
2728  {
2729  /* Reset the parallel index processing counter */
2730  pg_atomic_write_u32(&(lps->lvshared->idx), 0);
2731 
2732  /* Reinitialize the parallel context to relaunch parallel workers */
2734  }
2735 
2736  /*
2737  * Set up shared cost balance and the number of active workers for
2738  * vacuum delay. We need to do this before launching workers as
2739  * otherwise, they might not see the updated values for these
2740  * parameters.
2741  */
2744 
2745  /*
2746  * The number of workers can vary between bulkdelete and cleanup
2747  * phase.
2748  */
2749  ReinitializeParallelWorkers(lps->pcxt, nworkers);
2750 
2752 
2753  if (lps->pcxt->nworkers_launched > 0)
2754  {
2755  /*
2756  * Reset the local cost values for leader backend as we have
2757  * already accumulated the remaining balance of heap.
2758  */
2759  VacuumCostBalance = 0;
2761 
2762  /* Enable shared cost balance for leader backend */
2765  }
2766 
2767  if (lps->lvshared->for_cleanup)
2768  ereport(elevel,
2769  (errmsg(ngettext("launched %d parallel vacuum worker for index cleanup (planned: %d)",
2770  "launched %d parallel vacuum workers for index cleanup (planned: %d)",
2771  lps->pcxt->nworkers_launched),
2772  lps->pcxt->nworkers_launched, nworkers)));
2773  else
2774  ereport(elevel,
2775  (errmsg(ngettext("launched %d parallel vacuum worker for index vacuuming (planned: %d)",
2776  "launched %d parallel vacuum workers for index vacuuming (planned: %d)",
2777  lps->pcxt->nworkers_launched),
2778  lps->pcxt->nworkers_launched, nworkers)));
2779  }
2780 
2781  /* Process the indexes that can be processed by only leader process */
2783 
2784  /*
2785  * Join as a parallel worker. The leader process alone processes all the
2786  * indexes in the case where no workers are launched.
2787  */
2788  do_parallel_processing(vacrel, lps->lvshared);
2789 
2790  /*
2791  * Next, accumulate buffer and WAL usage. (This must wait for the workers
2792  * to finish, or we might get incomplete data.)
2793  */
2794  if (nworkers > 0)
2795  {
2796  /* Wait for all vacuum workers to finish */
2798 
2799  for (int i = 0; i < lps->pcxt->nworkers_launched; i++)
2801  }
2802 
2803  /*
2804  * Carry the shared balance value to heap scan and disable shared costing
2805  */
2807  {
2809  VacuumSharedCostBalance = NULL;
2810  VacuumActiveNWorkers = NULL;
2811  }
2812 }
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:367
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:514
ParallelContext * pcxt
Definition: vacuumlazy.c:285
pg_atomic_uint32 cost_balance
Definition: vacuumlazy.c:245
void WaitForParallelWorkersToFinish(ParallelContext *pcxt)
Definition: parallel.c:751
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
static void do_serial_processing_for_unsafe_indexes(LVRelState *vacrel, LVShared *lvshared)
Definition: vacuumlazy.c:2879
pg_atomic_uint32 idx
Definition: vacuumlazy.c:259
int nworkers_launched
Definition: parallel.h:38
void LaunchParallelWorkers(ParallelContext *pcxt)
Definition: parallel.c:528
void InstrAccumParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition: instrument.c:218
#define IsParallelWorker()
Definition: parallel.h:61
static void do_parallel_processing(LVRelState *vacrel, LVShared *lvshared)
Definition: vacuumlazy.c:2819
int VacuumCostBalanceLocal
Definition: vacuum.c:80
pg_atomic_uint32 * VacuumSharedCostBalance
Definition: vacuum.c:78
static int elevel
Definition: vacuumlazy.c:403
#define ngettext(s, p, n)
Definition: c.h:1182
void ReinitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:464
pg_atomic_uint32 active_nworkers
Definition: vacuumlazy.c: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 2879 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().

2880 {
2882 
2883  /*
2884  * Increment the active worker count if we are able to launch any worker.
2885  */
2888 
2889  for (int idx = 0; idx < vacrel->nindexes; idx++)
2890  {
2891  LVSharedIndStats *shared_istat;
2892  Relation indrel;
2893  IndexBulkDeleteResult *istat;
2894 
2895  shared_istat = parallel_stats_for_idx(lvshared, idx);
2896 
2897  /* Skip already-complete indexes */
2898  if (shared_istat != NULL)
2899  continue;
2900 
2901  indrel = vacrel->indrels[idx];
2902 
2903  /*
2904  * We're only here for the unsafe indexes
2905  */
2906  if (parallel_processing_is_safe(indrel, lvshared))
2907  continue;
2908 
2909  /* Do vacuum or cleanup of the index */
2910  istat = (vacrel->indstats[idx]);
2911  vacrel->indstats[idx] = parallel_process_one_index(indrel, istat,
2912  lvshared,
2913  shared_istat,
2914  vacrel);
2915  }
2916 
2917  /*
2918  * We have completed the index vacuum so decrement the active worker
2919  * count.
2920  */
2923 }
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:2932
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:364
static bool parallel_processing_is_safe(Relation indrel, LVShared *lvshared)
Definition: vacuumlazy.c:4111
#define IsParallelWorker()
Definition: parallel.h:61
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4087
#define Assert(condition)
Definition: c.h:804

◆ end_parallel_vacuum()

static void end_parallel_vacuum ( LVRelState vacrel)
static

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

4045 {
4046  IndexBulkDeleteResult **indstats = vacrel->indstats;
4047  LVParallelState *lps = vacrel->lps;
4048  int nindexes = vacrel->nindexes;
4049 
4051 
4052  /* Copy the updated statistics */
4053  for (int idx = 0; idx < nindexes; idx++)
4054  {
4055  LVSharedIndStats *shared_istat;
4056 
4057  shared_istat = parallel_stats_for_idx(lps->lvshared, idx);
4058 
4059  /*
4060  * Skip unused slot. The statistics of this index are already stored
4061  * in local memory.
4062  */
4063  if (shared_istat == NULL)
4064  continue;
4065 
4066  if (shared_istat->updated)
4067  {
4068  indstats[idx] = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
4069  memcpy(indstats[idx], &(shared_istat->istat), sizeof(IndexBulkDeleteResult));
4070  }
4071  else
4072  indstats[idx] = NULL;
4073  }
4074 
4076  ExitParallelMode();
4077 
4078  /* Deactivate parallel vacuum */
4079  pfree(lps);
4080  vacrel->lps = NULL;
4081 }
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:364
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:905
void pfree(void *pointer)
Definition: mcxt.c:1169
void ExitParallelMode(void)
Definition: xact.c:992
#define IsParallelWorker()
Definition: parallel.h:61
static LVSharedIndStats * parallel_stats_for_idx(LVShared *lvshared, int getidx)
Definition: vacuumlazy.c:4087
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 3624 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().

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

References _, appendStringInfo(), appendStringInfoChar(), 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::old_rel_pages, LVRelState::OldestXmin, VacuumParams::options, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, LVRelState::pages_removed, palloc(), palloc0(), pfree(), pg_rusage_init(), pg_rusage_show(), pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), pgstat_report_vacuum(), pgStatBlockReadTime, pgStatBlockWriteTime, pgWalUsage, LVRelState::phase, LVRelState::pinskipped_pages, ErrorContextCallback::previous, PROGRESS_COMMAND_VACUUM, PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_FINAL_CLEANUP, pstrdup(), RelationData::rd_rel, LVRelState::rel, LVRelState::rel_pages, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, LVRelState::relfrozenxid, LVRelState::relminmxid, LVRelState::relname, LVRelState::relnamespace, RowExclusiveLock, LVRelState::scanned_pages, should_attempt_truncation(), TimestampDifference(), TimestampDifferenceExceeds(), track_io_timing, TransactionIdPrecedesOrEquals(), VacuumParams::truncate, LVRelState::tuples_deleted, update_vacuum_error_info(), vac_close_indexes(), vac_open_indexes(), vac_update_relstats(), VACOPT_DISABLE_PAGE_SKIPPING, VACOPT_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().

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

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

2607 {
2608  /* Don't warn more than once per VACUUM */
2609  if (vacrel->failsafe_active)
2610  return true;
2611 
2613  vacrel->relminmxid)))
2614  {
2615  vacrel->failsafe_active = true;
2616 
2617  /* Disable index vacuuming, index cleanup, and heap rel truncation */
2618  vacrel->do_index_vacuuming = false;
2619  vacrel->do_index_cleanup = false;
2620  vacrel->do_rel_truncate = false;
2621 
2622  ereport(WARNING,
2623  (errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans",
2625  vacrel->relnamespace,
2626  vacrel->relname,
2627  vacrel->num_index_scans),
2628  errdetail("table's relfrozenxid or relminmxid is too far in the past"),
2629  errhint("Consider increasing configuration parameter \"maintenance_work_mem\" or \"autovacuum_work_mem\".\n"
2630  "You might also need to consider other ways for VACUUM to keep up with the allocation of transaction IDs.")));
2631 
2632  /* Stop applying cost limits from this point on */
2633  VacuumCostActive = false;
2634  VacuumCostBalance = 0;
2635 
2636  return true;
2637  }
2638 
2639  return false;
2640 }
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:367
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:331
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:341
TransactionId relfrozenxid
Definition: vacuumlazy.c:330
int errmsg(const char *fmt,...)
Definition: elog.c:909
char * relnamespace
Definition: vacuumlazy.c:340
#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 2986 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().

2987 {
2989  Assert(vacrel->nindexes > 0);
2990 
2991  /* Report that we are now cleaning up indexes */
2994 
2995  if (!ParallelVacuumIsActive(vacrel))
2996  {
2997  double reltuples = vacrel->new_rel_tuples;
2998  bool estimated_count =
2999  vacrel->tupcount_pages < vacrel->rel_pages;
3000 
3001  for (int idx = 0; idx < vacrel->nindexes; idx++)
3002  {
3003  Relation indrel = vacrel->indrels[idx];
3004  IndexBulkDeleteResult *istat = vacrel->indstats[idx];
3005 
3006  vacrel->indstats[idx] =
3007  lazy_cleanup_one_index(indrel, istat, reltuples,
3008  estimated_count, vacrel);
3009  }
3010  }
3011  else
3012  {
3013  /* Outsource everything to parallel variant */
3015  }
3016 }
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:364
#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:3085
#define IsParallelWorker()
Definition: parallel.h:61
double new_rel_tuples
Definition: vacuumlazy.c:361
#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:355
BlockNumber rel_pages
Definition: vacuumlazy.c:351
static void do_parallel_lazy_cleanup_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2667

◆ 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 3085 of file vacuumlazy.c.

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

Referenced by lazy_cleanup_all_indexes(), and parallel_process_one_index().

3088 {
3089  IndexVacuumInfo ivinfo;
3090  PGRUsage ru0;
3091  LVSavedErrInfo saved_err_info;
3092 
3093  pg_rusage_init(&ru0);
3094 
3095  ivinfo.index = indrel;
3096  ivinfo.analyze_only = false;
3097  ivinfo.report_progress = false;
3098  ivinfo.estimated_count = estimated_count;
3099  ivinfo.message_level = elevel;
3100 
3101  ivinfo.num_heap_tuples = reltuples;
3102  ivinfo.strategy = vacrel->bstrategy;
3103 
3104  /*
3105  * Update error traceback information.
3106  *
3107  * The index name is saved during this phase and restored immediately
3108  * after this phase. See vacuum_error_callback.
3109  */
3110  Assert(vacrel->indname == NULL);
3111  vacrel->indname = pstrdup(RelationGetRelationName(indrel));
3112  update_vacuum_error_info(vacrel, &saved_err_info,
3115 
3116  istat = index_vacuum_cleanup(&ivinfo, istat);
3117 
3118  if (istat)
3119  {
3120  ereport(elevel,
3121  (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
3122  RelationGetRelationName(indrel),
3123  (istat)->num_index_tuples,
3124  (istat)->num_pages),
3125  errdetail("%.0f index row versions were removed.\n"
3126  "%u index pages were newly deleted.\n"
3127  "%u index pages are currently deleted, of which %u are currently reusable.\n"
3128  "%s.",
3129  (istat)->tuples_removed,
3130  (istat)->pages_newly_deleted,
3131  (istat)->pages_deleted, (istat)->pages_free,
3132  pg_rusage_show(&ru0))));
3133  }
3134 
3135  /* Revert to the previous phase information for error traceback */
3136  restore_vacuum_error_info(vacrel, &saved_err_info);
3137  pfree(vacrel->indname);
3138  vacrel->indname = NULL;
3139 
3140  return istat;
3141 }
static void update_vacuum_error_info(LVRelState *vacrel, LVSavedErrInfo *saved_vacrel, int phase, BlockNumber blkno, OffsetNumber offnum)
Definition: vacuumlazy.c:4322
IndexBulkDeleteResult * index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *istat)
Definition: indexam.c:712
char * pstrdup(const char *in)
Definition: mcxt.c:1299
bool analyze_only
Definition: genam.h:47
bool report_progress
Definition: genam.h:48
BufferAccessStrategy strategy
Definition: genam.h:52
Relation index
Definition: genam.h:46
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
void pfree(void *pointer)
Definition: mcxt.c:1169
int errdetail(const char *fmt,...)
Definition: elog.c:1042
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
#define RelationGetRelationName(relation)
Definition: rel.h:511
static int elevel
Definition: vacuumlazy.c:403
char * indname
Definition: vacuumlazy.c:342
#define InvalidOffsetNumber
Definition: off.h:26
static void restore_vacuum_error_info(LVRelState *vacrel, const LVSavedErrInfo *saved_vacrel)
Definition: vacuumlazy.c:4341
#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
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 913 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().

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

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

3490 {
3491  LVDeadTuples *dead_tuples;
3492  long maxtuples;
3493 
3494  /*
3495  * Initialize state for a parallel vacuum. As of now, only one worker can
3496  * be used for an index, so we invoke parallelism only if there are at
3497  * least two indexes on a table.
3498  */
3499  if (nworkers >= 0 && vacrel->nindexes > 1 && vacrel->do_index_vacuuming)
3500  {
3501  /*
3502  * Since parallel workers cannot access data in temporary tables, we
3503  * can't perform parallel vacuum on them.
3504  */
3505  if (RelationUsesLocalBuffers(vacrel->rel))
3506  {
3507  /*
3508  * Give warning only if the user explicitly tries to perform a
3509  * parallel vacuum on the temporary table.
3510  */
3511  if (nworkers > 0)
3512  ereport(WARNING,
3513  (errmsg("disabling parallel option of vacuum on \"%s\" --- cannot vacuum temporary tables in parallel",
3514  vacrel->relname)));
3515  }
3516  else
3517  vacrel->lps = begin_parallel_vacuum(vacrel, nblocks, nworkers);
3518 
3519  /* If parallel mode started, we're done */
3520  if (ParallelVacuumIsActive(vacrel))
3521  return;
3522  }
3523 
3524  maxtuples = compute_max_dead_tuples(nblocks, vacrel->nindexes > 0);
3525 
3526  dead_tuples = (LVDeadTuples *) palloc(SizeOfDeadTuples(maxtuples));
3527  dead_tuples->num_tuples = 0;
3528  dead_tuples->max_tuples = (int) maxtuples;
3529 
3530  vacrel->dead_tuples = dead_tuples;
3531 }
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:3845
static long compute_max_dead_tuples(BlockNumber relblocks, bool hasindex)
Definition: vacuumlazy.c:3457
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:350
#define WARNING
Definition: elog.h:40
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:341
#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 3537 of file vacuumlazy.c.

References end_parallel_vacuum(), and ParallelVacuumIsActive.

Referenced by lazy_scan_heap().

3538 {
3539  if (!ParallelVacuumIsActive(vacrel))
3540  return;
3541 
3542  /*
3543  * End parallel mode before updating index statistics as we cannot write
3544  * during parallel mode.
3545  */
3546  end_parallel_vacuum(vacrel);
3547 }
static void end_parallel_vacuum(LVRelState *vacrel)
Definition: vacuumlazy.c:4044
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161

◆ lazy_tid_reaped()

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

Definition at line 3557 of file vacuumlazy.c.

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

Referenced by lazy_vacuum_one_index().

3558 {
3559  LVDeadTuples *dead_tuples = (LVDeadTuples *) state;
3560  int64 litem,
3561  ritem,
3562  item;
3563  ItemPointer res;
3564 
3565  litem = itemptr_encode(&dead_tuples->itemptrs[0]);
3566  ritem = itemptr_encode(&dead_tuples->itemptrs[dead_tuples->num_tuples - 1]);
3567  item = itemptr_encode(itemptr);
3568 
3569  /*
3570  * Doing a simple bound check before bsearch() is useful to avoid the
3571  * extra cost of bsearch(), especially if dead tuples on the heap are
3572  * concentrated in a certain range. Since this function is called for
3573  * every index tuple, it pays to be really fast.
3574  */
3575  if (item < litem || item > ritem)
3576  return false;
3577 
3578  res = (ItemPointer) bsearch((void *) itemptr,
3579  (void *) dead_tuples->itemptrs,
3580  dead_tuples->num_tuples,
3581  sizeof(ItemPointerData),
3582  vac_cmp_itemptr);
3583 
3584  return (res != NULL);
3585 }
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:3591

◆ lazy_truncate_heap()

static void lazy_truncate_heap ( LVRelState vacrel)
static

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

3187 {
3188  BlockNumber old_rel_pages = vacrel->rel_pages;
3189  BlockNumber new_rel_pages;
3190  bool lock_waiter_detected;
3191  int lock_retry;
3192 
3193  /* Report that we are now truncating */
3196 
3197  /*
3198  * Loop until no more truncating can be done.
3199  */
3200  do
3201  {
3202  PGRUsage ru0;
3203 
3204  pg_rusage_init(&ru0);
3205 
3206  /*
3207  * We need full exclusive lock on the relation in order to do
3208  * truncation. If we can't get it, give up rather than waiting --- we
3209  * don't want to block other backends, and we don't want to deadlock
3210  * (which is quite possible considering we already hold a lower-grade
3211  * lock).
3212  */
3213  lock_waiter_detected = false;
3214  lock_retry = 0;
3215  while (true)
3216  {
3218  break;
3219 
3220  /*
3221  * Check for interrupts while trying to (re-)acquire the exclusive
3222  * lock.
3223  */
3225 
3226  if (++lock_retry > (VACUUM_TRUNCATE_LOCK_TIMEOUT /
3228  {
3229  /*
3230  * We failed to establish the lock in the specified number of
3231  * retries. This means we give up truncating.
3232  */
3233  ereport(elevel,
3234  (errmsg("\"%s\": stopping truncate due to conflicting lock request",
3235  vacrel->relname)));
3236  return;
3237  }
3238 
3239  (void) WaitLatch(MyLatch,
3244  }
3245 
3246  /*
3247  * Now that we have exclusive lock, look to see if the rel has grown
3248  * whilst we were vacuuming with non-exclusive lock. If so, give up;
3249  * the newly added pages presumably contain non-deletable tuples.
3250  */
3251  new_rel_pages = RelationGetNumberOfBlocks(vacrel->rel);
3252  if (new_rel_pages != old_rel_pages)
3253  {
3254  /*
3255  * Note: we intentionally don't update vacrel->rel_pages with the
3256  * new rel size here. If we did, it would amount to assuming that
3257  * the new pages are empty, which is unlikely. Leaving the numbers
3258  * alone amounts to assuming that the new pages have the same
3259  * tuple density as existing ones, which is less unlikely.
3260  */
3262  return;
3263  }
3264 
3265  /*
3266  * Scan backwards from the end to verify that the end pages actually
3267  * contain no tuples. This is *necessary*, not optional, because
3268  * other backends could have added tuples to these pages whilst we
3269  * were vacuuming.
3270  */
3271  new_rel_pages = count_nondeletable_pages(vacrel, &lock_waiter_detected);
3272  vacrel->blkno = new_rel_pages;
3273 
3274  if (new_rel_pages >= old_rel_pages)
3275  {
3276  /* can't do anything after all */
3278  return;
3279  }
3280 
3281  /*
3282  * Okay to truncate.
3283  */
3284  RelationTruncate(vacrel->rel, new_rel_pages);
3285 
3286  /*
3287  * We can release the exclusive lock as soon as we have truncated.
3288  * Other backends can't safely access the relation until they have
3289  * processed the smgr invalidation that smgrtruncate sent out ... but
3290  * that should happen as part of standard invalidation processing once
3291  * they acquire lock on the relation.
3292  */
3294 
3295  /*
3296  * Update statistics. Here, it *is* correct to adjust rel_pages
3297  * without also touching reltuples, since the tuple count wasn't
3298  * changed by the truncation.
3299  */
3300  vacrel->pages_removed += old_rel_pages - new_rel_pages;
3301  vacrel->rel_pages = new_rel_pages;
3302 
3303  ereport(elevel,
3304  (errmsg("\"%s\": truncated %u to %u pages",
3305  vacrel->relname,
3306  old_rel_pages, new_rel_pages),
3307  errdetail_internal("%s",
3308  pg_rusage_show(&ru0))));
3309  old_rel_pages = new_rel_pages;
3310  } while (new_rel_pages > vacrel->nonempty_pages && lock_waiter_detected);
3311 }
#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:3319
BlockNumber nonempty_pages
Definition: vacuumlazy.c:358
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:356
#define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL
Definition: vacuumlazy.c:103
static int elevel
Definition: vacuumlazy.c:403
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:213
#define ereport(elevel,...)
Definition: elog.h:157
char * relname
Definition: vacuumlazy.c:341
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:343
BlockNumber rel_pages
Definition: vacuumlazy.c:351
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 2094 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().

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

2226 {
2227  bool allindexes = true;
2228 
2230  Assert(vacrel->nindexes > 0);
2231  Assert(vacrel->do_index_vacuuming);
2232  Assert(vacrel->do_index_cleanup);
2235 
2236  /* Precheck for XID wraparound emergencies */
2237  if (lazy_check_wraparound_failsafe(vacrel))
2238  {
2239  /* Wraparound emergency -- don't even start an index scan */
2240  return false;
2241  }
2242 
2243  /* Report that we are now vacuuming indexes */
2246 
2247  if (!ParallelVacuumIsActive(vacrel))
2248  {
2249  for (int idx = 0; idx < vacrel->nindexes; idx++)
2250  {
2251  Relation indrel = vacrel->indrels[idx];
2252  IndexBulkDeleteResult *istat = vacrel->indstats[idx];
2253 
2254  vacrel->indstats[idx] =
2255  lazy_vacuum_one_index(indrel, istat, vacrel->old_live_tuples,
2256  vacrel);
2257 
2258  if (lazy_check_wraparound_failsafe(vacrel))
2259  {
2260  /* Wraparound emergency -- end current index scan */
2261  allindexes = false;
2262  break;
2263  }
2264  }
2265  }
2266  else
2267  {
2268  /* Outsource everything to parallel variant */
2270 
2271  /*
2272  * Do a postcheck to consider applying wraparound failsafe now. Note
2273  * that parallel VACUUM only gets the precheck and this postcheck.
2274  */
2275  if (lazy_check_wraparound_failsafe(vacrel))
2276  allindexes = false;
2277  }
2278 
2279  /*
2280  * We delete all LP_DEAD items from the first heap pass in all indexes on
2281  * each call here (except calls where we choose to do the failsafe). This
2282  * makes the next call to lazy_vacuum_heap_rel() safe (except in the event
2283  * of the failsafe triggering, which prevents the next call from taking
2284  * place).
2285  */
2286  Assert(vacrel->num_index_scans > 0 ||
2287  vacrel->dead_tuples->num_tuples == vacrel->lpdead_items);
2288  Assert(allindexes || vacrel->failsafe_active);
2289 
2290  /*
2291  * Increase and report the number of index scans.
2292  *
2293  * We deliberately include the case where we started a round of bulk
2294  * deletes that we weren't able to finish due to the failsafe triggering.
2295  */
2296  vacrel->num_index_scans++;
2298  vacrel->num_index_scans);
2299 
2300  return allindexes;
2301 }
static bool lazy_check_wraparound_failsafe(LVRelState *vacrel)
Definition: vacuumlazy.c:2606
Relation * indrels
Definition: vacuumlazy.c:309
bool do_index_vacuuming
Definition: vacuumlazy.c:318
int num_index_scans
Definition: vacuumlazy.c:367
#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:369
IndexBulkDeleteResult ** indstats
Definition: vacuumlazy.c:364
bool failsafe_active
Definition: vacuumlazy.c:313
#define ParallelVacuumIsActive(vacrel)
Definition: vacuumlazy.c:161
LVDeadTuples * dead_tuples
Definition: vacuumlazy.c:350
#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:3030
static void do_parallel_lazy_vacuum_all_indexes(LVRelState *vacrel)
Definition: vacuumlazy.c:2646
MultiXactId relminmxid
Definition: vacuumlazy.c:331
TransactionId relfrozenxid
Definition: vacuumlazy.c:330
#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:328

◆ lazy_vacuum_heap_page()

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

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

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