LCOV - code coverage report
Current view: top level - src/backend/postmaster - datachecksum_state.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19beta1 Lines: 76.7 % 480 368
Test Date: 2026-06-10 16:16:35 Functions: 89.5 % 19 17
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * datachecksum_state.c
       4              :  *    Background worker for enabling or disabling data checksums online as
       5              :  *    well as functionality for manipulating data checksum state
       6              :  *
       7              :  * When enabling data checksums on a cluster at initdb time or when shut down
       8              :  * with pg_checksums, no extra process is required as each page is checksummed,
       9              :  * and verified, when accessed.  When enabling checksums on an already running
      10              :  * cluster, this worker will ensure that all pages are checksummed before
      11              :  * verification of the checksums is turned on. In the case of disabling
      12              :  * checksums, the state transition is performed only in the control file, no
      13              :  * changes are performed on the data pages.
      14              :  *
      15              :  * Checksums can be either enabled or disabled cluster-wide, with on/off being
      16              :  * the end state for data_checksums.
      17              :  *
      18              :  * 1. Enabling checksums
      19              :  * ---------------------
      20              :  * When enabling checksums in an online cluster, data_checksums will be set to
      21              :  * "inprogress-on" which signals that write operations MUST compute and write
      22              :  * the checksum on the data page, but during reading the checksum SHALL NOT be
      23              :  * verified. This ensures that all objects created while checksums are being
      24              :  * enabled will have checksums set, but reads won't fail due to missing or
      25              :  * invalid checksums. Invalid checksums can be present in case the cluster had
      26              :  * checksums enabled, then disabled them and updated the page while they were
      27              :  * disabled.
      28              :  *
      29              :  * The DataChecksumsWorker will compile a list of all databases at the start,
      30              :  * any databases created concurrently will see the in-progress state and will
      31              :  * be checksummed automatically.  All databases from the original list MUST BE
      32              :  * successfully processed in order for data checksums to be enabled, the only
      33              :  * exception are databases which are dropped before having been processed.
      34              :  *
      35              :  * For each database, all relations which have storage are read and every data
      36              :  * page is marked dirty to force a write with the checksum. This will generate
      37              :  * a lot of WAL as the entire database is read and written.
      38              :  *
      39              :  * If the processing is interrupted by a cluster crash or restart, it needs to
      40              :  * be restarted from the beginning again as state isn't persisted.
      41              :  *
      42              :  * 2. Disabling checksums
      43              :  * ----------------------
      44              :  * When disabling checksums, data_checksums will be set to "inprogress-off"
      45              :  * which signals that checksums are written but no longer need to be verified.
      46              :  * This ensures that backends which have not yet transitioned to the
      47              :  * "inprogress-off" state will still see valid checksums on pages.
      48              :  *
      49              :  * 3. Synchronization and Correctness
      50              :  * ----------------------------------
      51              :  * The processes involved in enabling or disabling data checksums in an
      52              :  * online cluster must be properly synchronized with the normal backends
      53              :  * serving concurrent queries to ensure correctness. Correctness is defined
      54              :  * as the following:
      55              :  *
      56              :  *    - Backends SHALL NOT violate the data_checksums state they have agreed to
      57              :  *      by acknowledging the procsignalbarrier:  This means that all backends
      58              :  *      MUST calculate and write data checksums during all states except off;
      59              :  *      MUST validate checksums only in the 'on' state.
      60              :  *    - Data checksums SHALL NOT be considered enabled cluster-wide until all
      61              :  *      currently connected backends have state "on": This means that all
      62              :  *      backends must wait on the procsignalbarrier to be acknowledged by all
      63              :  *      before proceeding to validate data checksums.
      64              :  *
      65              :  * There are two steps of synchronization required for changing data_checksums
      66              :  * in an online cluster: (i) changing state in the active backends ("on",
      67              :  * "off", "inprogress-on" and "inprogress-off"), and (ii) ensuring no
      68              :  * incompatible objects and processes are left in a database when workers end.
      69              :  * The former deals with cluster-wide agreement on data checksum state and the
      70              :  * latter with ensuring that any concurrent activity cannot break the data
      71              :  * checksum contract during processing.
      72              :  *
      73              :  * Synchronizing the state change is done with procsignal barriers. Before
      74              :  * updating the data_checksums state in the control file, all other backends must absorb the
      75              :  * barrier.  Barrier absorption will happen during interrupt processing, which
      76              :  * means that connected backends will change state at different times.  If
      77              :  * waiting for a barrier is done during startup, for example during replay, it
      78              :  * is important to realize that any locks held by the startup process might
      79              :  * cause deadlocks if backends end up waiting for those locks while startup
      80              :  * is waiting for a procsignalbarrier.
      81              :  *
      82              :  * 3.1 When Enabling Data Checksums
      83              :  * --------------------------------
      84              :  * A process which fails to observe data checksums being enabled can induce two
      85              :  * types of errors: failing to write the checksum when modifying the page and
      86              :  * failing to validate the data checksum on the page when reading it.
      87              :  *
      88              :  * When processing starts all backends belong to one of the below sets, with
      89              :  * one of Bd and Bi being empty:
      90              :  *
      91              :  * Bg: Backend updating the global state and emitting the procsignalbarrier
      92              :  * Bd: Backends in "off" state
      93              :  * Bi: Backends in "inprogress-on" state
      94              :  *
      95              :  * If processing is started in an online cluster then all backends are in Bd.
      96              :  * If processing was halted by the cluster shutting down (due to a crash or
      97              :  * intentional restart), the controlfile state "inprogress-on" will be observed
      98              :  * on system startup and all backends will be placed in Bd. The controlfile
      99              :  * state will also be set to "off".
     100              :  *
     101              :  * Backends transition Bd -> Bi via a procsignalbarrier which is emitted by the
     102              :  * DataChecksumsWorkerLauncherMain.  When all backends have acknowledged the
     103              :  * barrier then Bd will be empty and the next phase can begin: calculating and
     104              :  * writing data checksums with DataChecksumsWorkers.  When the
     105              :  * DataChecksumsWorker processes have finished writing checksums on all pages,
     106              :  * data checksums are enabled cluster-wide via another procsignalbarrier.
     107              :  * There are four sets of backends where Bd shall be an empty set:
     108              :  *
     109              :  * Bg: Backend updating the global state and emitting the procsignalbarrier
     110              :  * Bd: Backends in "off" state
     111              :  * Be: Backends in "on" state
     112              :  * Bi: Backends in "inprogress-on" state
     113              :  *
     114              :  * Backends in Bi and Be will write checksums when modifying a page, but only
     115              :  * backends in Be will verify the checksum during reading. The Bg backend is
     116              :  * blocked waiting for all backends in Bi to process interrupts and move to
     117              :  * Be. Any backend starting while Bg is waiting on the procsignalbarrier will
     118              :  * observe the global state being "on" and will thus automatically belong to
     119              :  * Be.  Checksums are enabled cluster-wide when Bi is an empty set. Bi and Be
     120              :  * are compatible sets while still operating based on their local state as
     121              :  * both write data checksums.
     122              :  *
     123              :  * 3.2 When Disabling Data Checksums
     124              :  * ---------------------------------
     125              :  * A process which fails to observe that data checksums have been disabled
     126              :  * can induce two types of errors: writing the checksum when modifying the
     127              :  * page and validating a data checksum which is no longer correct due to
     128              :  * modifications to the page. The former is not an error per se as data
     129              :  * integrity is maintained, but it is wasteful.  The latter will cause errors
     130              :  * in user operations.  Assuming the following sets of backends:
     131              :  *
     132              :  * Bg: Backend updating the global state and emitting the procsignalbarrier
     133              :  * Bd: Backends in "off" state
     134              :  * Be: Backends in "on" state
     135              :  * Bo: Backends in "inprogress-off" state
     136              :  * Bi: Backends in "inprogress-on" state
     137              :  *
     138              :  * Backends transition from the Be state to Bd like so: Be -> Bo -> Bd.  From
     139              :  * all other states, the transition can be straight to Bd.
     140              :  *
     141              :  * The goal is to transition all backends to Bd making the others empty sets.
     142              :  * Backends in Bo write data checksums, but don't validate them, such that
     143              :  * backends still in Be can continue to validate pages until the barrier has
     144              :  * been absorbed such that they are in Bo. Once all backends are in Bo, the
     145              :  * barrier to transition to "off" can be raised and all backends can safely
     146              :  * stop writing data checksums as no backend is enforcing data checksum
     147              :  * validation any longer.
     148              :  *
     149              :  * 4. Future opportunities for optimizations
     150              :  * -----------------------------------------
     151              :  * Below are some potential optimizations and improvements which were brought
     152              :  * up during reviews of this feature, but which weren't implemented in the
     153              :  * initial version. These are ideas listed without any validation on their
     154              :  * feasibility or potential payoff. More discussion on (most of) these can be
     155              :  * found on the -hackers threads linked to in the commit message of this
     156              :  * feature.
     157              :  *
     158              :  *   * Launching datachecksumsworker for resuming operation from the startup
     159              :  *     process: Currently users have to restart processing manually after a
     160              :  *     restart since dynamic background worker cannot be started from the
     161              :  *     postmaster. Changing the startup process could make restarting the
     162              :  *     processing automatic on cluster restart.
     163              :  *   * Avoid dirtying the page when checksums already match: Iff the checksum
     164              :  *     on the page happens to already match we still dirty the page. It should
     165              :  *     be enough to only do the log_newpage_buffer() call in that case.
     166              :  *   * Teach pg_checksums to avoid checksummed pages when pg_checksums is used
     167              :  *     to enable checksums on a cluster which is in inprogress-on state and
     168              :  *     may have checksummed pages (make pg_checksums be able to resume an
     169              :  *     online operation). This should only be attempted for wal_level minimal.
     170              :  *   * Restartability (not necessarily with page granularity).
     171              :  *   * Avoid processing databases which were created during inprogress-on.
     172              :  *     Right now all databases are processed regardless to be safe.
     173              :  *   * Teach CREATE DATABASE to calculate checksums for databases created
     174              :  *     during inprogress-on with a template database which has yet to be
     175              :  *     processed.
     176              :  *
     177              :  *
     178              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
     179              :  * Portions Copyright (c) 1994, Regents of the University of California
     180              :  *
     181              :  *
     182              :  * IDENTIFICATION
     183              :  *    src/backend/postmaster/datachecksum_state.c
     184              :  *
     185              :  *-------------------------------------------------------------------------
     186              :  */
     187              : #include "postgres.h"
     188              : 
     189              : #include "access/genam.h"
     190              : #include "access/heapam.h"
     191              : #include "access/htup_details.h"
     192              : #include "access/xact.h"
     193              : #include "access/xlog.h"
     194              : #include "access/xloginsert.h"
     195              : #include "catalog/indexing.h"
     196              : #include "catalog/pg_class.h"
     197              : #include "catalog/pg_database.h"
     198              : #include "commands/progress.h"
     199              : #include "commands/vacuum.h"
     200              : #include "common/relpath.h"
     201              : #include "miscadmin.h"
     202              : #include "pgstat.h"
     203              : #include "postmaster/bgworker.h"
     204              : #include "postmaster/bgwriter.h"
     205              : #include "postmaster/datachecksum_state.h"
     206              : #include "storage/bufmgr.h"
     207              : #include "storage/checksum.h"
     208              : #include "storage/ipc.h"
     209              : #include "storage/latch.h"
     210              : #include "storage/lmgr.h"
     211              : #include "storage/lwlock.h"
     212              : #include "storage/procarray.h"
     213              : #include "storage/smgr.h"
     214              : #include "storage/subsystems.h"
     215              : #include "tcop/tcopprot.h"
     216              : #include "utils/builtins.h"
     217              : #include "utils/fmgroids.h"
     218              : #include "utils/injection_point.h"
     219              : #include "utils/lsyscache.h"
     220              : #include "utils/ps_status.h"
     221              : #include "utils/syscache.h"
     222              : #include "utils/wait_event.h"
     223              : 
     224              : /*
     225              :  * Configuration of conditions which must match when absorbing a procsignal
     226              :  * barrier during data checksum enable/disable operations.  A single function
     227              :  * is used for absorbing all barriers, and the current and target states must
     228              :  * be defined as a from/to tuple in the checksum_barriers struct.
     229              :  */
     230              : typedef struct ChecksumBarrierCondition
     231              : {
     232              :     /* Current state of data checksums */
     233              :     int         from;
     234              :     /* Target state for data checksums */
     235              :     int         to;
     236              : } ChecksumBarrierCondition;
     237              : 
     238              : static const ChecksumBarrierCondition checksum_barriers[9] =
     239              : {
     240              :     /*
     241              :      * Disabling checksums: If checksums are currently enabled, disabling must
     242              :      * go through the 'inprogress-off' state.
     243              :      */
     244              :     {PG_DATA_CHECKSUM_VERSION, PG_DATA_CHECKSUM_INPROGRESS_OFF},
     245              :     {PG_DATA_CHECKSUM_INPROGRESS_OFF, PG_DATA_CHECKSUM_OFF},
     246              : 
     247              :     /*
     248              :      * If checksums are in the process of being enabled, but are not yet being
     249              :      * verified, we can abort by going back to 'off' state.
     250              :      */
     251              :     {PG_DATA_CHECKSUM_INPROGRESS_ON, PG_DATA_CHECKSUM_OFF},
     252              : 
     253              :     /*
     254              :      * Enabling checksums must normally go through the 'inprogress-on' state.
     255              :      */
     256              :     {PG_DATA_CHECKSUM_OFF, PG_DATA_CHECKSUM_INPROGRESS_ON},
     257              :     {PG_DATA_CHECKSUM_INPROGRESS_ON, PG_DATA_CHECKSUM_VERSION},
     258              : 
     259              :     /*
     260              :      * If checksums are being disabled but all backends are still computing
     261              :      * checksums, we can go straight back to 'on'
     262              :      */
     263              :     {PG_DATA_CHECKSUM_INPROGRESS_OFF, PG_DATA_CHECKSUM_VERSION},
     264              : 
     265              :     /*
     266              :      * If checksums are being enabled when launcher_exit is executed, state is
     267              :      * set to off since we cannot reach on at that point.
     268              :      */
     269              :     {PG_DATA_CHECKSUM_INPROGRESS_ON, PG_DATA_CHECKSUM_INPROGRESS_OFF},
     270              : 
     271              :     /*
     272              :      * Transitions that can happen when a new request is made while another is
     273              :      * currently being processed.
     274              :      */
     275              :     {PG_DATA_CHECKSUM_INPROGRESS_OFF, PG_DATA_CHECKSUM_INPROGRESS_ON},
     276              :     {PG_DATA_CHECKSUM_OFF, PG_DATA_CHECKSUM_INPROGRESS_OFF},
     277              : };
     278              : 
     279              : /*
     280              :  * Signaling between backends calling pg_enable/disable_data_checksums, the
     281              :  * checksums launcher process, and the checksums worker process.
     282              :  *
     283              :  * This struct is protected by DataChecksumsWorkerLock
     284              :  */
     285              : typedef struct DataChecksumsStateStruct
     286              : {
     287              :     /*
     288              :      * These are set by pg_{enable|disable}_data_checksums, to tell the
     289              :      * launcher what the target state is.
     290              :      */
     291              :     DataChecksumsWorkerOperation launch_operation;
     292              :     int         launch_cost_delay;
     293              :     int         launch_cost_limit;
     294              : 
     295              :     /*
     296              :      * Is a launcher process currently running?  This is set by the main
     297              :      * launcher process, after it has read the above launch_* parameters.
     298              :      */
     299              :     bool        launcher_running;
     300              : 
     301              :     /*
     302              :      * PID of the worker process, if it's currently running, of InvalidPid if
     303              :      * none. This is set by the worker launcher when it starts waiting for a
     304              :      * worker process to finish.
     305              :      */
     306              :     pid_t       worker_pid;
     307              : 
     308              :     /*
     309              :      * These fields indicate the target state that the launcher is currently
     310              :      * working towards. They can be different from the corresponding launch_*
     311              :      * fields, if a new pg_enable/disable_data_checksums() call was made while
     312              :      * the launcher/worker was already running.
     313              :      *
     314              :      * The below members are set when the launcher starts, and are only
     315              :      * accessed read-only by the single worker. Thus, we can access these
     316              :      * without a lock. If multiple workers, or dynamic cost parameters, are
     317              :      * supported at some point then this would need to be revisited.
     318              :      */
     319              :     DataChecksumsWorkerOperation operation;
     320              :     int         cost_delay;
     321              :     int         cost_limit;
     322              : 
     323              :     /*
     324              :      * Signaling between the launcher and the worker process. Protected by
     325              :      * DataChecksumsWorkerLock.
     326              :      */
     327              : 
     328              :     /* result, set by worker before exiting */
     329              :     DataChecksumsWorkerResult success;
     330              : 
     331              :     /*
     332              :      * Tells the worker process whether it should also process the shared
     333              :      * catalogs
     334              :      */
     335              :     bool        process_shared_catalogs;
     336              : } DataChecksumsStateStruct;
     337              : 
     338              : /* Shared memory segment for datachecksumsworker */
     339              : static DataChecksumsStateStruct *DataChecksumState;
     340              : 
     341              : typedef struct DataChecksumsWorkerDatabase
     342              : {
     343              :     Oid         dboid;
     344              :     char       *dbname;
     345              : } DataChecksumsWorkerDatabase;
     346              : 
     347              : /* Flag set by the interrupt handler */
     348              : static volatile sig_atomic_t abort_requested = false;
     349              : 
     350              : /*
     351              :  * Have we set the DataChecksumsStateStruct->launcher_running flag?
     352              :  * If we have, we need to clear it before exiting!
     353              :  */
     354              : static volatile sig_atomic_t launcher_running = false;
     355              : 
     356              : /* Are we enabling data checksums, or disabling them? */
     357              : static DataChecksumsWorkerOperation operation;
     358              : 
     359              : /* Prototypes */
     360              : static void DataChecksumsShmemRequest(void *arg);
     361              : static bool DatabaseExists(Oid dboid);
     362              : static List *BuildDatabaseList(void);
     363              : static List *BuildRelationList(bool temp_relations, bool include_shared);
     364              : static void FreeDatabaseList(List *dblist);
     365              : static DataChecksumsWorkerResult ProcessDatabase(DataChecksumsWorkerDatabase *db);
     366              : static bool ProcessAllDatabases(void);
     367              : static bool ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy);
     368              : static void launcher_cancel_handler(SIGNAL_ARGS);
     369              : static void WaitForAllTransactionsToFinish(void);
     370              : 
     371              : const ShmemCallbacks DataChecksumsShmemCallbacks = {
     372              :     .request_fn = DataChecksumsShmemRequest,
     373              : };
     374              : 
     375              : #define CHECK_FOR_ABORT_REQUEST() \
     376              :     do {                                                            \
     377              :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);          \
     378              :         if (DataChecksumState->launch_operation != operation)        \
     379              :             abort_requested = true;                                 \
     380              :         LWLockRelease(DataChecksumsWorkerLock);                     \
     381              :     } while (0)
     382              : 
     383              : 
     384              : /*****************************************************************************
     385              :  * Functionality for manipulating the data checksum state in the cluster
     386              :  */
     387              : 
     388              : void
     389            8 : EmitAndWaitDataChecksumsBarrier(uint32 state)
     390              : {
     391              :     uint64      barrier;
     392              : 
     393            8 :     switch (state)
     394              :     {
     395            3 :         case PG_DATA_CHECKSUM_INPROGRESS_ON:
     396            3 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_ON);
     397            3 :             WaitForProcSignalBarrier(barrier);
     398            3 :             break;
     399              : 
     400            1 :         case PG_DATA_CHECKSUM_INPROGRESS_OFF:
     401            1 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_OFF);
     402            1 :             WaitForProcSignalBarrier(barrier);
     403            1 :             break;
     404              : 
     405            2 :         case PG_DATA_CHECKSUM_VERSION:
     406            2 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_ON);
     407            2 :             WaitForProcSignalBarrier(barrier);
     408            2 :             break;
     409              : 
     410            2 :         case PG_DATA_CHECKSUM_OFF:
     411            2 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_OFF);
     412            2 :             WaitForProcSignalBarrier(barrier);
     413            2 :             break;
     414              : 
     415            8 :         default:
     416              :             Assert(false);
     417              :     }
     418            8 : }
     419              : 
     420              : /*
     421              :  * AbsorbDataChecksumsBarrier
     422              :  *      Generic function for absorbing data checksum state changes
     423              :  *
     424              :  * All procsignalbarriers regarding data checksum state changes are absorbed
     425              :  * with this function.  The set of conditions required for the state change to
     426              :  * be accepted are listed in the checksum_barriers struct, target_state is
     427              :  * used to look up the relevant entry.
     428              :  */
     429              : bool
     430          278 : AbsorbDataChecksumsBarrier(ProcSignalBarrierType barrier)
     431              : {
     432              :     uint32      target_state;
     433          278 :     int         current = data_checksums;
     434          278 :     bool        found = false;
     435              : 
     436              :     /*
     437              :      * Translate the barrier condition to the target state, doing it here
     438              :      * instead of in the procsignal code saves the latter from knowing about
     439              :      * checksum states.
     440              :      */
     441          278 :     switch (barrier)
     442              :     {
     443           94 :         case PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_ON:
     444           94 :             target_state = PG_DATA_CHECKSUM_INPROGRESS_ON;
     445           94 :             break;
     446           71 :         case PROCSIGNAL_BARRIER_CHECKSUM_ON:
     447           71 :             target_state = PG_DATA_CHECKSUM_VERSION;
     448           71 :             break;
     449           54 :         case PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_OFF:
     450           54 :             target_state = PG_DATA_CHECKSUM_INPROGRESS_OFF;
     451           54 :             break;
     452           59 :         case PROCSIGNAL_BARRIER_CHECKSUM_OFF:
     453           59 :             target_state = PG_DATA_CHECKSUM_OFF;
     454           59 :             break;
     455            0 :         default:
     456            0 :             elog(ERROR, "incorrect barrier \"%i\" received", barrier);
     457              :     }
     458              : 
     459              :     /*
     460              :      * If the target state matches the current state then the barrier has been
     461              :      * repeated.
     462              :      */
     463          278 :     if (current == target_state)
     464            1 :         return true;
     465              : 
     466              :     /*
     467              :      * If the cluster is in recovery we skip the validation of current state
     468              :      * since the replay is trusted.
     469              :      */
     470          277 :     if (RecoveryInProgress())
     471              :     {
     472           48 :         SetLocalDataChecksumState(target_state);
     473           48 :         return true;
     474              :     }
     475              : 
     476              :     /*
     477              :      * Find the barrier condition definition for the target state. Not finding
     478              :      * a condition would be a grave programmer error as the states are a
     479              :      * discrete set.
     480              :      */
     481         1044 :     for (int i = 0; i < lengthof(checksum_barriers) && !found; i++)
     482              :     {
     483          815 :         if (checksum_barriers[i].from == current && checksum_barriers[i].to == target_state)
     484          229 :             found = true;
     485              :     }
     486              : 
     487              :     /*
     488              :      * If the relevant state criteria aren't satisfied, throw an error which
     489              :      * will be caught by the procsignal machinery for a later retry.
     490              :      */
     491          229 :     if (!found)
     492            0 :         ereport(ERROR,
     493              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     494              :                 errmsg("incorrect data checksum state %i for target state %i",
     495              :                        current, target_state));
     496              : 
     497          229 :     SetLocalDataChecksumState(target_state);
     498          229 :     return true;
     499              : }
     500              : 
     501              : 
     502              : /*
     503              :  * Disables data checksums for the cluster, if applicable. Starts a background
     504              :  * worker which turns off the data checksums.
     505              :  */
     506              : Datum
     507            7 : disable_data_checksums(PG_FUNCTION_ARGS)
     508              : {
     509            7 :     PreventCommandDuringRecovery("pg_disable_data_checksums()");
     510              : 
     511            7 :     if (!superuser())
     512            0 :         ereport(ERROR,
     513              :                 errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     514              :                 errmsg("must be superuser to change data checksum state"));
     515              : 
     516            7 :     StartDataChecksumsWorkerLauncher(DISABLE_DATACHECKSUMS, 0, 0);
     517            7 :     PG_RETURN_VOID();
     518              : }
     519              : 
     520              : /*
     521              :  * Enables data checksums for the cluster, if applicable.  Supports vacuum-
     522              :  * like cost based throttling to limit system load. Starts a background worker
     523              :  * which updates data checksums on existing data.
     524              :  */
     525              : Datum
     526           11 : enable_data_checksums(PG_FUNCTION_ARGS)
     527              : {
     528           11 :     int         cost_delay = PG_GETARG_INT32(0);
     529           11 :     int         cost_limit = PG_GETARG_INT32(1);
     530              : 
     531           11 :     PreventCommandDuringRecovery("pg_enable_data_checksums()");
     532              : 
     533           11 :     if (!superuser())
     534            0 :         ereport(ERROR,
     535              :                 errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     536              :                 errmsg("must be superuser to change data checksum state"));
     537              : 
     538           11 :     if (cost_delay < 0)
     539            0 :         ereport(ERROR,
     540              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     541              :                 errmsg("cost delay cannot be a negative value"));
     542              : 
     543           11 :     if (cost_limit <= 0)
     544            0 :         ereport(ERROR,
     545              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     546              :                 errmsg("cost limit must be greater than zero"));
     547              : 
     548           11 :     StartDataChecksumsWorkerLauncher(ENABLE_DATACHECKSUMS, cost_delay, cost_limit);
     549              : 
     550           11 :     PG_RETURN_VOID();
     551              : }
     552              : 
     553              : 
     554              : /*****************************************************************************
     555              :  * Functionality for running the datachecksumsworker and associated launcher
     556              :  */
     557              : 
     558              : /*
     559              :  * StartDataChecksumsWorkerLauncher
     560              :  *      Main entry point for datachecksumsworker launcher process
     561              :  *
     562              :  * The main entrypoint for starting data checksums processing for enabling as
     563              :  * well as disabling.
     564              :  */
     565              : void
     566           18 : StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op,
     567              :                                  int cost_delay,
     568              :                                  int cost_limit)
     569              : {
     570              :     BackgroundWorker bgw;
     571              :     BackgroundWorkerHandle *bgw_handle;
     572              :     bool        running;
     573              : 
     574              : #ifdef USE_ASSERT_CHECKING
     575              :     /* The cost delay settings have no effect when disabling */
     576              :     if (op == DISABLE_DATACHECKSUMS)
     577              :         Assert(cost_delay == 0 && cost_limit == 0);
     578              : #endif
     579              : 
     580           18 :     INJECTION_POINT("datachecksumsworker-startup-delay", NULL);
     581              : 
     582              :     /* Store the desired state in shared memory */
     583           18 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     584              : 
     585           18 :     DataChecksumState->launch_operation = op;
     586           18 :     DataChecksumState->launch_cost_delay = cost_delay;
     587           18 :     DataChecksumState->launch_cost_limit = cost_limit;
     588              : 
     589              :     /* Is the launcher already running? If so, what is it doing? */
     590           18 :     running = DataChecksumState->launcher_running;
     591              : 
     592           18 :     LWLockRelease(DataChecksumsWorkerLock);
     593              : 
     594              :     /*
     595              :      * Launch a new launcher process, if it's not running already.
     596              :      *
     597              :      * If the launcher is currently busy enabling the checksums, and we want
     598              :      * them disabled (or vice versa), the launcher will notice that at latest
     599              :      * when it's about to exit, and will loop back to process the new request.
     600              :      * So if the launcher is already running, we don't need to do anything
     601              :      * more here to abort it.
     602              :      *
     603              :      * If you call pg_enable/disable_data_checksums() twice in a row, before
     604              :      * the launcher has had a chance to start up, we still end up launching it
     605              :      * twice.  That's OK, the second invocation will see that a launcher is
     606              :      * already running and exit quickly.
     607              :      */
     608           18 :     if (!running)
     609              :     {
     610           18 :         if ((op == ENABLE_DATACHECKSUMS && DataChecksumsOn()) ||
     611            7 :             (op == DISABLE_DATACHECKSUMS && DataChecksumsOff()))
     612              :         {
     613            3 :             ereport(LOG,
     614              :                     errmsg("data checksums already in desired state, exiting"));
     615            3 :             return;
     616              :         }
     617              : 
     618              :         /*
     619              :          * Prepare the BackgroundWorker and launch it.
     620              :          */
     621           15 :         memset(&bgw, 0, sizeof(bgw));
     622           15 :         bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
     623           15 :         bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
     624           15 :         snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
     625           15 :         snprintf(bgw.bgw_function_name, BGW_MAXLEN, "DataChecksumsWorkerLauncherMain");
     626           15 :         snprintf(bgw.bgw_name, BGW_MAXLEN, "datachecksums launcher");
     627           15 :         snprintf(bgw.bgw_type, BGW_MAXLEN, "datachecksums launcher");
     628           15 :         bgw.bgw_restart_time = BGW_NEVER_RESTART;
     629           15 :         bgw.bgw_notify_pid = MyProcPid;
     630           15 :         bgw.bgw_main_arg = (Datum) 0;
     631              : 
     632           15 :         if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
     633            0 :             ereport(ERROR,
     634              :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
     635              :                     errmsg("failed to start background worker to process data checksums"));
     636              :     }
     637              :     else
     638              :     {
     639            0 :         ereport(LOG,
     640              :                 errmsg("data checksum processing already running"));
     641              :     }
     642              : }
     643              : 
     644              : /*
     645              :  * ProcessSingleRelationFork
     646              :  *      Enable data checksums in a single relation/fork.
     647              :  *
     648              :  * Returns true if successful, and false if *aborted*. On error, an actual
     649              :  * error is raised in the lower levels.
     650              :  */
     651              : static bool
     652         7846 : ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy)
     653              : {
     654         7846 :     BlockNumber numblocks = RelationGetNumberOfBlocksInFork(reln, forkNum);
     655              :     char        activity[NAMEDATALEN * 2 + 128];
     656              :     char       *relns;
     657              : 
     658         7846 :     relns = get_namespace_name(RelationGetNamespace(reln));
     659              : 
     660              :     /* Report the current relation to pg_stat_activity */
     661         7846 :     snprintf(activity, sizeof(activity) - 1, "processing: %s.%s (%s, %u blocks)",
     662         7846 :              (relns ? relns : ""), RelationGetRelationName(reln), forkNames[forkNum], numblocks);
     663         7846 :     pgstat_report_activity(STATE_RUNNING, activity);
     664         7846 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_BLOCKS_TOTAL, numblocks);
     665         7846 :     if (relns)
     666         7846 :         pfree(relns);
     667              : 
     668              :     /*
     669              :      * We are looping over the blocks which existed at the time of process
     670              :      * start, which is safe since new blocks are created with checksums set
     671              :      * already due to the state being "inprogress-on".
     672              :      */
     673        48849 :     for (BlockNumber blknum = 0; blknum < numblocks; blknum++)
     674              :     {
     675        41003 :         Buffer      buf = ReadBufferExtended(reln, forkNum, blknum, RBM_NORMAL, strategy);
     676              : 
     677              :         /* Need to get an exclusive lock to mark the buffer as dirty */
     678        41003 :         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     679              : 
     680              :         /*
     681              :          * Mark the buffer as dirty and force a full page write.  We have to
     682              :          * re-write the page to WAL even if the checksum hasn't changed,
     683              :          * because if there is a replica it might have a slightly different
     684              :          * version of the page with an invalid checksum, caused by unlogged
     685              :          * changes (e.g. hint bits) on the primary happening while checksums
     686              :          * were off. This can happen if there was a valid checksum on the page
     687              :          * at one point in the past, so only when checksums are first on, then
     688              :          * off, and then turned on again.  TODO: investigate if this could be
     689              :          * avoided if the checksum is calculated to be correct and wal_level
     690              :          * is set to "minimal".
     691              :          *
     692              :          * Unlogged relations don't need WAL since they are reset to their
     693              :          * init fork on recovery.  We still dirty the buffer so that the
     694              :          * checksum is written to disk at the next checkpoint.
     695              :          *
     696              :          * The init fork is an exception: it is WAL-logged so the standby can
     697              :          * materialize the relation after promotion (see
     698              :          * ResetUnloggedRelations()).  Skipping it here would leave the
     699              :          * standby with a stale init fork that, once copied to the main fork
     700              :          * on promotion, would fail checksum verification on every read.
     701              :          */
     702        41003 :         START_CRIT_SECTION();
     703        41003 :         MarkBufferDirty(buf);
     704        41003 :         if (RelationNeedsWAL(reln) || forkNum == INIT_FORKNUM)
     705        40969 :             log_newpage_buffer(buf, false);
     706        41003 :         END_CRIT_SECTION();
     707              : 
     708        41003 :         UnlockReleaseBuffer(buf);
     709              : 
     710              :         /* Check if we are asked to abort, the abortion will bubble up. */
     711              :         Assert(operation == ENABLE_DATACHECKSUMS);
     712        41003 :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
     713        41003 :         if (DataChecksumState->launch_operation == DISABLE_DATACHECKSUMS)
     714            0 :             abort_requested = true;
     715        41003 :         LWLockRelease(DataChecksumsWorkerLock);
     716              : 
     717        41003 :         if (abort_requested)
     718            0 :             return false;
     719              : 
     720              :         /* update the block counter */
     721        41003 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_BLOCKS_DONE,
     722        41003 :                                      (blknum + 1));
     723              : 
     724              :         /*
     725              :          * Processing is re-using the vacuum cost delay for process
     726              :          * throttling, hence why we call vacuum APIs here.
     727              :          */
     728        41003 :         vacuum_delay_point(false);
     729              :     }
     730              : 
     731         7846 :     return true;
     732              : }
     733              : 
     734              : /*
     735              :  * ProcessSingleRelationByOid
     736              :  *      Process a single relation based on oid.
     737              :  *
     738              :  * Returns true if successful, and false if *aborted*. On error, an actual
     739              :  * error is raised in the lower levels.
     740              :  */
     741              : static bool
     742         6040 : ProcessSingleRelationByOid(Oid relationId, BufferAccessStrategy strategy)
     743              : {
     744              :     Relation    rel;
     745         6040 :     bool        aborted = false;
     746              : 
     747         6040 :     StartTransactionCommand();
     748              : 
     749         6040 :     rel = try_relation_open(relationId, AccessShareLock);
     750         6040 :     if (rel == NULL)
     751              :     {
     752              :         /*
     753              :          * Relation no longer exists. We don't consider this an error since
     754              :          * there are no pages in it that need data checksums, and thus return
     755              :          * true. The worker operates off a list of relations generated at the
     756              :          * start of processing, so relations being dropped in the meantime is
     757              :          * to be expected.
     758              :          */
     759            0 :         CommitTransactionCommand();
     760            0 :         pgstat_report_activity(STATE_IDLE, NULL);
     761            0 :         return true;
     762              :     }
     763         6040 :     RelationGetSmgr(rel);
     764              : 
     765        30200 :     for (ForkNumber fnum = 0; fnum <= MAX_FORKNUM; fnum++)
     766              :     {
     767        24160 :         if (smgrexists(rel->rd_smgr, fnum))
     768              :         {
     769         7846 :             if (!ProcessSingleRelationFork(rel, fnum, strategy))
     770              :             {
     771            0 :                 aborted = true;
     772            0 :                 break;
     773              :             }
     774              :         }
     775              :     }
     776         6040 :     relation_close(rel, AccessShareLock);
     777              : 
     778         6040 :     CommitTransactionCommand();
     779         6040 :     pgstat_report_activity(STATE_IDLE, NULL);
     780              : 
     781         6040 :     return !aborted;
     782              : }
     783              : 
     784              : /*
     785              :  * ProcessDatabase
     786              :  *      Enable data checksums in a single database.
     787              :  *
     788              :  * We do this by launching a dynamic background worker into this database, and
     789              :  * waiting for it to finish.  We have to do this in a separate worker, since
     790              :  * each process can only be connected to one database during its lifetime.
     791              :  */
     792              : static DataChecksumsWorkerResult
     793           23 : ProcessDatabase(DataChecksumsWorkerDatabase *db)
     794              : {
     795              :     BackgroundWorker bgw;
     796              :     BackgroundWorkerHandle *bgw_handle;
     797              :     BgwHandleStatus status;
     798              :     pid_t       pid;
     799              :     char        activity[NAMEDATALEN + 64];
     800              : 
     801           23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     802           23 :     DataChecksumState->success = DATACHECKSUMSWORKER_FAILED;
     803           23 :     LWLockRelease(DataChecksumsWorkerLock);
     804              : 
     805           23 :     memset(&bgw, 0, sizeof(bgw));
     806           23 :     bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
     807           23 :     bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
     808           23 :     snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
     809           23 :     snprintf(bgw.bgw_function_name, BGW_MAXLEN, "%s", "DataChecksumsWorkerMain");
     810           23 :     snprintf(bgw.bgw_name, BGW_MAXLEN, "datachecksums worker");
     811           23 :     snprintf(bgw.bgw_type, BGW_MAXLEN, "datachecksums worker");
     812           23 :     bgw.bgw_restart_time = BGW_NEVER_RESTART;
     813           23 :     bgw.bgw_notify_pid = MyProcPid;
     814           23 :     bgw.bgw_main_arg = ObjectIdGetDatum(db->dboid);
     815              : 
     816              :     /*
     817              :      * If there are no worker slots available, there is little we can do.  If
     818              :      * we retry in a bit it's still unlikely that the user has managed to
     819              :      * reconfigure in the meantime and we'd be run through retries fast.
     820              :      */
     821           23 :     if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
     822              :     {
     823            0 :         ereport(WARNING,
     824              :                 errmsg("could not start background worker for enabling data checksums in database \"%s\"",
     825              :                        db->dbname),
     826              :                 errhint("The \"%s\" setting might be too low.", "max_worker_processes"));
     827            0 :         return DATACHECKSUMSWORKER_FAILED;
     828              :     }
     829              : 
     830           23 :     status = WaitForBackgroundWorkerStartup(bgw_handle, &pid);
     831           23 :     if (status == BGWH_STOPPED)
     832              :     {
     833              :         /*
     834              :          * If the worker managed to start, and stop, before we got to waiting
     835              :          * for it we can see a STOPPED status here without it being a failure.
     836              :          */
     837            0 :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
     838            0 :         if (DataChecksumState->success == DATACHECKSUMSWORKER_SUCCESSFUL)
     839              :         {
     840            0 :             LWLockRelease(DataChecksumsWorkerLock);
     841            0 :             pgstat_report_activity(STATE_IDLE, NULL);
     842            0 :             LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     843            0 :             DataChecksumState->worker_pid = InvalidPid;
     844            0 :             LWLockRelease(DataChecksumsWorkerLock);
     845            0 :             return DataChecksumState->success;
     846              :         }
     847            0 :         LWLockRelease(DataChecksumsWorkerLock);
     848              : 
     849            0 :         ereport(WARNING,
     850              :                 errmsg("could not start background worker for enabling data checksums in database \"%s\"",
     851              :                        db->dbname),
     852              :                 errhint("More details on the error might be found in the server log."));
     853              : 
     854              :         /*
     855              :          * Heuristic to see if the database was dropped, and if it was we can
     856              :          * treat it as not an error, else treat as fatal and error out.
     857              :          */
     858            0 :         if (DatabaseExists(db->dboid))
     859            0 :             return DATACHECKSUMSWORKER_FAILED;
     860              :         else
     861            0 :             return DATACHECKSUMSWORKER_DROPDB;
     862              :     }
     863              : 
     864              :     /*
     865              :      * If the postmaster crashed we cannot end up with a processed database so
     866              :      * we have no alternative other than exiting. When enabling checksums we
     867              :      * won't at this time have changed the data checksums state in pg_control
     868              :      * to enabled so when the cluster comes back up processing will have to be
     869              :      * restarted.
     870              :      */
     871           23 :     if (status == BGWH_POSTMASTER_DIED)
     872            0 :         ereport(FATAL,
     873              :                 errcode(ERRCODE_ADMIN_SHUTDOWN),
     874              :                 errmsg("cannot enable data checksums without the postmaster process"),
     875              :                 errhint("Restart the database and restart data checksum processing by calling pg_enable_data_checksums()."));
     876              : 
     877              :     Assert(status == BGWH_STARTED);
     878           23 :     ereport(LOG,
     879              :             errmsg("initiating data checksum processing in database \"%s\"",
     880              :                    db->dbname));
     881              : 
     882              :     /* Save the pid of the worker so we can signal it later */
     883           23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     884           23 :     DataChecksumState->worker_pid = pid;
     885           23 :     LWLockRelease(DataChecksumsWorkerLock);
     886              : 
     887           23 :     snprintf(activity, sizeof(activity) - 1,
     888              :              "Waiting for worker in database %s (pid %ld)", db->dbname, (long) pid);
     889           23 :     pgstat_report_activity(STATE_RUNNING, activity);
     890              : 
     891           23 :     status = WaitForBackgroundWorkerShutdown(bgw_handle);
     892           22 :     if (status == BGWH_POSTMASTER_DIED)
     893            0 :         ereport(FATAL,
     894              :                 errcode(ERRCODE_ADMIN_SHUTDOWN),
     895              :                 errmsg("postmaster exited during data checksum processing in \"%s\"",
     896              :                        db->dbname),
     897              :                 errhint("Restart the database and restart data checksum processing by calling pg_enable_data_checksums()."));
     898              : 
     899           22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
     900           22 :     if (DataChecksumState->success == DATACHECKSUMSWORKER_ABORTED)
     901            0 :         ereport(LOG,
     902              :                 errmsg("data checksums processing was aborted in database \"%s\"",
     903              :                        db->dbname));
     904           22 :     LWLockRelease(DataChecksumsWorkerLock);
     905              : 
     906           22 :     pgstat_report_activity(STATE_IDLE, NULL);
     907           22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     908           22 :     DataChecksumState->worker_pid = InvalidPid;
     909           22 :     LWLockRelease(DataChecksumsWorkerLock);
     910              : 
     911           22 :     return DataChecksumState->success;
     912              : }
     913              : 
     914              : /*
     915              :  * launcher_exit
     916              :  *
     917              :  * Internal routine for cleaning up state when a launcher process which has
     918              :  * performed checksum operations exits. A launcher process which is exiting due
     919              :  * to a duplicate started launcher does not need to perform any cleanup and
     920              :  * this function should not be called. Otherwise, we need to clean up the abort
     921              :  * flag to ensure that processing can be started again if it was previously
     922              :  * aborted (note: started again, *not* restarted from where it left off).
     923              :  */
     924              : static void
     925           14 : launcher_exit(int code, Datum arg)
     926              : {
     927           14 :     abort_requested = false;
     928              : 
     929           14 :     if (launcher_running)
     930              :     {
     931            2 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     932            2 :         if (DataChecksumState->worker_pid != InvalidPid)
     933              :         {
     934            1 :             ereport(LOG,
     935              :                     errmsg("data checksums launcher exiting while worker is still running, signalling worker"));
     936            1 :             kill(DataChecksumState->worker_pid, SIGTERM);
     937              :         }
     938            2 :         LWLockRelease(DataChecksumsWorkerLock);
     939              :     }
     940              : 
     941              :     /*
     942              :      * If the launcher is exiting before data checksums are enabled then set
     943              :      * the state to off since processing cannot be resumed.
     944              :      */
     945           14 :     if (DataChecksumsInProgressOn())
     946            1 :         SetDataChecksumsOff();
     947              : 
     948           14 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     949           14 :     launcher_running = false;
     950           14 :     DataChecksumState->launcher_running = false;
     951           14 :     LWLockRelease(DataChecksumsWorkerLock);
     952           14 : }
     953              : 
     954              : /*
     955              :  * launcher_cancel_handler
     956              :  *
     957              :  * Internal routine for reacting to SIGINT and flagging the worker to abort.
     958              :  * The worker won't be interrupted immediately but will check for abort flag
     959              :  * between each block in a relation.
     960              :  */
     961              : static void
     962            0 : launcher_cancel_handler(SIGNAL_ARGS)
     963              : {
     964            0 :     int         save_errno = errno;
     965              : 
     966            0 :     abort_requested = true;
     967              : 
     968              :     /*
     969              :      * There is no sleeping in the main loop, the flag will be checked
     970              :      * periodically in ProcessSingleRelationFork. The worker does however
     971              :      * sleep when waiting for concurrent transactions to end so we still need
     972              :      * to set the latch.
     973              :      */
     974            0 :     SetLatch(MyLatch);
     975              : 
     976            0 :     errno = save_errno;
     977            0 : }
     978              : 
     979              : /*
     980              :  * WaitForAllTransactionsToFinish
     981              :  *      Blocks awaiting all current transactions to finish
     982              :  *
     983              :  * Returns when all transactions which are active at the call of the function
     984              :  * have ended, or if the postmaster dies while waiting. If the postmaster dies
     985              :  * the abort flag will be set to indicate that the caller of this shouldn't
     986              :  * proceed.
     987              :  *
     988              :  * NB: this will return early, if aborted by SIGINT or if the target state
     989              :  * is changed while we're running.
     990              :  */
     991              : static void
     992            9 : WaitForAllTransactionsToFinish(void)
     993              : {
     994              :     TransactionId waitforxid;
     995              : 
     996            9 :     LWLockAcquire(XidGenLock, LW_SHARED);
     997            9 :     waitforxid = XidFromFullTransactionId(TransamVariables->nextXid);
     998            9 :     LWLockRelease(XidGenLock);
     999              : 
    1000            9 :     while (TransactionIdPrecedes(GetOldestActiveTransactionId(false, true), waitforxid))
    1001              :     {
    1002              :         char        activity[64];
    1003              :         int         rc;
    1004              : 
    1005              :         /* Oldest running xid is older than us, so wait */
    1006            0 :         snprintf(activity,
    1007              :                  sizeof(activity),
    1008              :                  "Waiting for transactions older than %u to end",
    1009              :                  waitforxid);
    1010            0 :         pgstat_report_activity(STATE_RUNNING, activity);
    1011              : 
    1012              :         /* Retry every 3 seconds */
    1013            0 :         ResetLatch(MyLatch);
    1014            0 :         rc = WaitLatch(MyLatch,
    1015              :                        WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
    1016              :                        3000,
    1017              :                        WAIT_EVENT_CHECKSUM_ENABLE_STARTCONDITION);
    1018              : 
    1019              :         /*
    1020              :          * If the postmaster died we won't be able to enable checksums
    1021              :          * cluster-wide so abort and hope to continue when restarted.
    1022              :          */
    1023            0 :         if (rc & WL_POSTMASTER_DEATH)
    1024            0 :             ereport(FATAL,
    1025              :                     errcode(ERRCODE_ADMIN_SHUTDOWN),
    1026              :                     errmsg("postmaster exited during data checksums processing"),
    1027              :                     errhint("Data checksums processing must be restarted manually after cluster restart."));
    1028              : 
    1029            0 :         CHECK_FOR_INTERRUPTS();
    1030            0 :         CHECK_FOR_ABORT_REQUEST();
    1031              : 
    1032            0 :         if (abort_requested)
    1033            0 :             break;
    1034              :     }
    1035              : 
    1036            9 :     pgstat_report_activity(STATE_IDLE, NULL);
    1037            9 :     return;
    1038              : }
    1039              : 
    1040              : /*
    1041              :  * DataChecksumsWorkerLauncherMain
    1042              :  *
    1043              :  * Main function for launching dynamic background workers for processing data
    1044              :  * checksums in databases. This function has the bgworker management, with
    1045              :  * ProcessAllDatabases being responsible for looping over the databases and
    1046              :  * initiating processing.
    1047              :  */
    1048              : void
    1049           14 : DataChecksumsWorkerLauncherMain(Datum arg)
    1050              : {
    1051              : 
    1052           14 :     ereport(DEBUG1,
    1053              :             errmsg("background worker \"datachecksums launcher\" started"));
    1054              : 
    1055           14 :     pqsignal(SIGTERM, die);
    1056           14 :     pqsignal(SIGINT, launcher_cancel_handler);
    1057           14 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    1058           14 :     pqsignal(SIGUSR2, PG_SIG_IGN);
    1059              : 
    1060           14 :     BackgroundWorkerUnblockSignals();
    1061              : 
    1062           14 :     MyBackendType = B_DATACHECKSUMSWORKER_LAUNCHER;
    1063           14 :     init_ps_display(NULL);
    1064              : 
    1065           14 :     INJECTION_POINT("datachecksumsworker-launcher-delay", NULL);
    1066              : 
    1067           14 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1068              : 
    1069           14 :     if (DataChecksumState->launcher_running)
    1070              :     {
    1071            0 :         ereport(LOG,
    1072              :                 errmsg("background worker \"datachecksums launcher\" already running, exiting"));
    1073              :         /* Launcher was already running, let it finish */
    1074            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1075            0 :         return;
    1076              :     }
    1077              : 
    1078           14 :     on_shmem_exit(launcher_exit, 0);
    1079           14 :     launcher_running = true;
    1080              : 
    1081              :     /* Initialize a connection to shared catalogs only */
    1082           14 :     BackgroundWorkerInitializeConnectionByOid(InvalidOid, InvalidOid, 0);
    1083              : 
    1084           14 :     operation = DataChecksumState->launch_operation;
    1085           14 :     DataChecksumState->launcher_running = true;
    1086           14 :     DataChecksumState->operation = operation;
    1087           14 :     DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1088           14 :     DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1089           14 :     LWLockRelease(DataChecksumsWorkerLock);
    1090              : 
    1091              :     /*
    1092              :      * The target state can change while we are busy enabling/disabling
    1093              :      * checksums, if the user calls pg_disable/enable_data_checksums() before
    1094              :      * we are finished with the previous request. In that case, we will loop
    1095              :      * back here, to process the new request.
    1096              :      */
    1097           14 : again:
    1098              : 
    1099           14 :     pgstat_progress_start_command(PROGRESS_COMMAND_DATACHECKSUMS,
    1100              :                                   InvalidOid);
    1101              : 
    1102           14 :     if (operation == ENABLE_DATACHECKSUMS)
    1103              :     {
    1104              :         /*
    1105              :          * If we are asked to enable checksums in a cluster which already has
    1106              :          * checksums enabled, exit immediately as there is nothing more to do.
    1107              :          */
    1108            9 :         if (DataChecksumsNeedVerify())
    1109            0 :             goto done;
    1110              : 
    1111            9 :         ereport(LOG,
    1112              :                 errmsg("enabling data checksums requested, starting data checksum calculation"));
    1113              : 
    1114              :         /*
    1115              :          * Set the state to inprogress-on and wait on the procsignal barrier.
    1116              :          */
    1117            9 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1118              :                                      PROGRESS_DATACHECKSUMS_PHASE_ENABLING);
    1119            9 :         SetDataChecksumsOnInProgress();
    1120              : 
    1121              :         /*
    1122              :          * All backends are now in inprogress-on state and are writing data
    1123              :          * checksums.  Start processing all data at rest.
    1124              :          */
    1125            9 :         if (!ProcessAllDatabases())
    1126              :         {
    1127              :             /*
    1128              :              * If the target state changed during processing then it's not a
    1129              :              * failure, so restart processing instead.
    1130              :              */
    1131            0 :             LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1132            0 :             if (DataChecksumState->launch_operation != operation)
    1133              :             {
    1134            0 :                 LWLockRelease(DataChecksumsWorkerLock);
    1135            0 :                 goto done;
    1136              :             }
    1137            0 :             LWLockRelease(DataChecksumsWorkerLock);
    1138            0 :             ereport(ERROR,
    1139              :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
    1140              :                     errmsg("unable to enable data checksums in cluster"));
    1141              :         }
    1142              : 
    1143              :         /*
    1144              :          * Data checksums have been set on all pages, set the state to on in
    1145              :          * order to instruct backends to validate checksums on reading.
    1146              :          */
    1147            7 :         SetDataChecksumsOn();
    1148              : 
    1149            7 :         ereport(LOG,
    1150              :                 errmsg("data checksums are now enabled"));
    1151              :     }
    1152            5 :     else if (operation == DISABLE_DATACHECKSUMS)
    1153              :     {
    1154            5 :         ereport(LOG,
    1155              :                 errmsg("disabling data checksums requested"));
    1156              : 
    1157            5 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1158              :                                      PROGRESS_DATACHECKSUMS_PHASE_DISABLING);
    1159            5 :         SetDataChecksumsOff();
    1160            5 :         ereport(LOG,
    1161              :                 errmsg("data checksums are now disabled"));
    1162              :     }
    1163              :     else
    1164              :         Assert(false);
    1165              : 
    1166            0 : done:
    1167              : 
    1168              :     /*
    1169              :      * This state will only be displayed for a fleeting moment, but for the
    1170              :      * sake of correctness it is still added before ending the command.
    1171              :      */
    1172           12 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1173              :                                  PROGRESS_DATACHECKSUMS_PHASE_DONE);
    1174              : 
    1175              :     /*
    1176              :      * All done. But before we exit, check if the target state was changed
    1177              :      * while we were running. In that case we will have to start all over
    1178              :      * again.
    1179              :      */
    1180           12 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1181           12 :     if (DataChecksumState->launch_operation != operation)
    1182              :     {
    1183            0 :         DataChecksumState->operation = DataChecksumState->launch_operation;
    1184            0 :         operation = DataChecksumState->launch_operation;
    1185            0 :         DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1186            0 :         DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1187            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1188            0 :         goto again;
    1189              :     }
    1190              : 
    1191              :     /* Shut down progress reporting as we are done */
    1192           12 :     pgstat_progress_end_command();
    1193              : 
    1194           12 :     launcher_running = false;
    1195           12 :     DataChecksumState->launcher_running = false;
    1196           12 :     LWLockRelease(DataChecksumsWorkerLock);
    1197              : }
    1198              : 
    1199              : /*
    1200              :  * ProcessAllDatabases
    1201              :  *      Compute the list of all databases and process checksums in each
    1202              :  *
    1203              :  * This will generate a list of databases to process for enabling checksums.
    1204              :  * If a database encounters a failure then processing will end immediately and
    1205              :  * return an error.
    1206              :  */
    1207              : static bool
    1208            9 : ProcessAllDatabases(void)
    1209              : {
    1210              :     List       *DatabaseList;
    1211            9 :     int         cumulative_total = 0;
    1212              : 
    1213              :     /* Set up so first run processes shared catalogs, not once in every db */
    1214            9 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1215            9 :     DataChecksumState->process_shared_catalogs = true;
    1216            9 :     LWLockRelease(DataChecksumsWorkerLock);
    1217              : 
    1218              :     /* Get a list of all databases to process */
    1219            9 :     WaitForAllTransactionsToFinish();
    1220            9 :     DatabaseList = BuildDatabaseList();
    1221              : 
    1222              :     /*
    1223              :      * Update progress reporting with the total number of databases we need to
    1224              :      * process.  This number should not be changed during processing, the
    1225              :      * columns for processed databases is instead increased such that it can
    1226              :      * be compared against the total.
    1227              :      */
    1228              :     {
    1229            9 :         const int   index[] = {
    1230              :             PROGRESS_DATACHECKSUMS_DBS_TOTAL,
    1231              :             PROGRESS_DATACHECKSUMS_DBS_DONE,
    1232              :             PROGRESS_DATACHECKSUMS_RELS_TOTAL,
    1233              :             PROGRESS_DATACHECKSUMS_RELS_DONE,
    1234              :             PROGRESS_DATACHECKSUMS_BLOCKS_TOTAL,
    1235              :             PROGRESS_DATACHECKSUMS_BLOCKS_DONE,
    1236              :         };
    1237              : 
    1238              :         int64       vals[6];
    1239              : 
    1240            9 :         vals[0] = list_length(DatabaseList);
    1241            9 :         vals[1] = 0;
    1242              :         /* translated to NULL */
    1243            9 :         vals[2] = -1;
    1244            9 :         vals[3] = -1;
    1245            9 :         vals[4] = -1;
    1246            9 :         vals[5] = -1;
    1247              : 
    1248            9 :         pgstat_progress_update_multi_param(6, index, vals);
    1249              :     }
    1250              : 
    1251           37 :     foreach_ptr(DataChecksumsWorkerDatabase, db, DatabaseList)
    1252              :     {
    1253              :         DataChecksumsWorkerResult result;
    1254              : 
    1255           23 :         result = ProcessDatabase(db);
    1256              : 
    1257              : #ifdef USE_INJECTION_POINTS
    1258              :         /* Allow a test process to alter the result of the operation */
    1259           22 :         if (IS_INJECTION_POINT_ATTACHED("datachecksumsworker-fail-db-result"))
    1260              :         {
    1261            1 :             result = DATACHECKSUMSWORKER_FAILED;
    1262            1 :             INJECTION_POINT_CACHED("datachecksumsworker-fail-db-result",
    1263              :                                    db->dbname);
    1264              :         }
    1265              : #endif
    1266              : 
    1267           22 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_DBS_DONE,
    1268              :                                      ++cumulative_total);
    1269              : 
    1270           22 :         if (result == DATACHECKSUMSWORKER_FAILED)
    1271              :         {
    1272              :             /*
    1273              :              * Disable checksums on cluster, because we failed one of the
    1274              :              * databases and this is an all or nothing process.
    1275              :              */
    1276            1 :             SetDataChecksumsOff();
    1277            1 :             ereport(ERROR,
    1278              :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
    1279              :                     errmsg("data checksums failed to get enabled in all databases, aborting"),
    1280              :                     errhint("The server log might have more information on the cause of the error."));
    1281              :         }
    1282           21 :         else if (result == DATACHECKSUMSWORKER_ABORTED || abort_requested)
    1283              :         {
    1284              :             /* Abort flag set, so exit the whole process */
    1285            0 :             return false;
    1286              :         }
    1287              : 
    1288              :         /*
    1289              :          * When one database has completed, it will have done shared catalogs
    1290              :          * so we don't have to process them again.
    1291              :          */
    1292           21 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1293           21 :         DataChecksumState->process_shared_catalogs = false;
    1294           21 :         LWLockRelease(DataChecksumsWorkerLock);
    1295              :     }
    1296              : 
    1297            7 :     FreeDatabaseList(DatabaseList);
    1298              : 
    1299            7 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1300              :                                  PROGRESS_DATACHECKSUMS_PHASE_WAITING_BARRIER);
    1301            7 :     return true;
    1302              : }
    1303              : 
    1304              : /*
    1305              :  * DataChecksumsShmemRequest
    1306              :  *      Request datachecksumsworker-related shared memory
    1307              :  */
    1308              : static void
    1309         1255 : DataChecksumsShmemRequest(void *arg)
    1310              : {
    1311         1255 :     ShmemRequestStruct(.name = "DataChecksumsWorker Data",
    1312              :                        .size = sizeof(DataChecksumsStateStruct),
    1313              :                        .ptr = (void **) &DataChecksumState,
    1314              :         );
    1315         1255 : }
    1316              : 
    1317              : /*
    1318              :  * DatabaseExists
    1319              :  *
    1320              :  * Scans the system catalog to check if a database with the given Oid exists
    1321              :  * and returns true if it is found and valid, else false. Note, we cannot use
    1322              :  * database_is_invalid_oid here as it will ERROR out, and we want to gracefully
    1323              :  * handle errors.
    1324              :  */
    1325              : static bool
    1326            0 : DatabaseExists(Oid dboid)
    1327              : {
    1328              :     Relation    rel;
    1329              :     ScanKeyData skey;
    1330              :     SysScanDesc scan;
    1331              :     bool        found;
    1332              :     HeapTuple   tuple;
    1333              :     Form_pg_database pg_database_tuple;
    1334              : 
    1335            0 :     StartTransactionCommand();
    1336              : 
    1337            0 :     rel = table_open(DatabaseRelationId, AccessShareLock);
    1338            0 :     ScanKeyInit(&skey,
    1339              :                 Anum_pg_database_oid,
    1340              :                 BTEqualStrategyNumber, F_OIDEQ,
    1341              :                 ObjectIdGetDatum(dboid));
    1342            0 :     scan = systable_beginscan(rel, DatabaseOidIndexId, true, SnapshotSelf,
    1343              :                               1, &skey);
    1344            0 :     tuple = systable_getnext(scan);
    1345            0 :     found = HeapTupleIsValid(tuple);
    1346              : 
    1347              :     /* If the Oid exists, ensure that it's not partially dropped */
    1348            0 :     if (found)
    1349              :     {
    1350            0 :         pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
    1351            0 :         if (database_is_invalid_form(pg_database_tuple))
    1352            0 :             found = false;
    1353              :     }
    1354              : 
    1355            0 :     systable_endscan(scan);
    1356            0 :     table_close(rel, AccessShareLock);
    1357              : 
    1358            0 :     CommitTransactionCommand();
    1359              : 
    1360            0 :     return found;
    1361              : }
    1362              : 
    1363              : /*
    1364              :  * BuildDatabaseList
    1365              :  *      Compile a list of all currently available databases in the cluster
    1366              :  *
    1367              :  * This creates the list of databases for the datachecksumsworker workers to
    1368              :  * add checksums to. If the caller wants to ensure that no concurrently
    1369              :  * running CREATE DATABASE calls exist, this needs to be preceded by a call
    1370              :  * to WaitForAllTransactionsToFinish().
    1371              :  */
    1372              : static List *
    1373            9 : BuildDatabaseList(void)
    1374              : {
    1375            9 :     List       *DatabaseList = NIL;
    1376              :     Relation    rel;
    1377              :     TableScanDesc scan;
    1378              :     HeapTuple   tup;
    1379            9 :     MemoryContext ctx = CurrentMemoryContext;
    1380              :     MemoryContext oldctx;
    1381              : 
    1382            9 :     StartTransactionCommand();
    1383              : 
    1384            9 :     rel = table_open(DatabaseRelationId, AccessShareLock);
    1385            9 :     scan = table_beginscan_catalog(rel, 0, NULL);
    1386              : 
    1387           36 :     while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    1388              :     {
    1389           27 :         Form_pg_database pgdb = (Form_pg_database) GETSTRUCT(tup);
    1390              :         DataChecksumsWorkerDatabase *db;
    1391              : 
    1392           27 :         oldctx = MemoryContextSwitchTo(ctx);
    1393              : 
    1394           27 :         db = (DataChecksumsWorkerDatabase *) palloc0(sizeof(DataChecksumsWorkerDatabase));
    1395              : 
    1396           27 :         db->dboid = pgdb->oid;
    1397           27 :         db->dbname = pstrdup(NameStr(pgdb->datname));
    1398              : 
    1399           27 :         DatabaseList = lappend(DatabaseList, db);
    1400              : 
    1401           27 :         MemoryContextSwitchTo(oldctx);
    1402              :     }
    1403              : 
    1404            9 :     table_endscan(scan);
    1405            9 :     table_close(rel, AccessShareLock);
    1406              : 
    1407            9 :     CommitTransactionCommand();
    1408              : 
    1409            9 :     return DatabaseList;
    1410              : }
    1411              : 
    1412              : static void
    1413            7 : FreeDatabaseList(List *dblist)
    1414              : {
    1415            7 :     if (!dblist)
    1416            0 :         return;
    1417              : 
    1418           35 :     foreach_ptr(DataChecksumsWorkerDatabase, db, dblist)
    1419              :     {
    1420           21 :         if (db->dbname != NULL)
    1421           21 :             pfree(db->dbname);
    1422              :     }
    1423              : 
    1424            7 :     list_free_deep(dblist);
    1425              : }
    1426              : 
    1427              : /*
    1428              :  * BuildRelationList
    1429              :  *      Compile a list of relations in the database
    1430              :  *
    1431              :  * Returns a list of OIDs for the requested relation types. If temp_relations
    1432              :  * is True then only temporary relations are returned. If temp_relations is
    1433              :  * False then non-temporary relations which have data checksums are returned.
    1434              :  * If include_shared is True then shared relations are included as well in a
    1435              :  * non-temporary list. include_shared has no relevance when building a list of
    1436              :  * temporary relations.
    1437              :  */
    1438              : static List *
    1439           69 : BuildRelationList(bool temp_relations, bool include_shared)
    1440              : {
    1441           69 :     List       *RelationList = NIL;
    1442              :     Relation    rel;
    1443              :     TableScanDesc scan;
    1444              :     HeapTuple   tup;
    1445           69 :     MemoryContext ctx = CurrentMemoryContext;
    1446              :     MemoryContext oldctx;
    1447              : 
    1448           69 :     StartTransactionCommand();
    1449              : 
    1450           69 :     rel = table_open(RelationRelationId, AccessShareLock);
    1451           69 :     scan = table_beginscan_catalog(rel, 0, NULL);
    1452              : 
    1453        31302 :     while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    1454              :     {
    1455        31233 :         Form_pg_class pgc = (Form_pg_class) GETSTRUCT(tup);
    1456              : 
    1457              :         /* Only include temporary relations when explicitly asked to */
    1458        31233 :         if (pgc->relpersistence == RELPERSISTENCE_TEMP)
    1459              :         {
    1460            3 :             if (!temp_relations)
    1461            1 :                 continue;
    1462              :         }
    1463              :         else
    1464              :         {
    1465              :             /*
    1466              :              * If we are only interested in temp relations then continue
    1467              :              * immediately as the current relation isn't a temp relation.
    1468              :              */
    1469        31230 :             if (temp_relations)
    1470        20820 :                 continue;
    1471              : 
    1472        10410 :             if (!RELKIND_HAS_STORAGE(pgc->relkind))
    1473         3726 :                 continue;
    1474              : 
    1475         6684 :             if (pgc->relisshared && !include_shared)
    1476          644 :                 continue;
    1477              :         }
    1478              : 
    1479         6042 :         oldctx = MemoryContextSwitchTo(ctx);
    1480         6042 :         RelationList = lappend_oid(RelationList, pgc->oid);
    1481         6042 :         MemoryContextSwitchTo(oldctx);
    1482              :     }
    1483              : 
    1484           69 :     table_endscan(scan);
    1485           69 :     table_close(rel, AccessShareLock);
    1486              : 
    1487           69 :     CommitTransactionCommand();
    1488              : 
    1489           69 :     return RelationList;
    1490              : }
    1491              : 
    1492              : /*
    1493              :  * DataChecksumsWorkerMain
    1494              :  *
    1495              :  * Main function for enabling checksums in a single database. This is the
    1496              :  * function set as the bgw_function_name in the dynamic background worker
    1497              :  * process initiated for each database by the worker launcher. After enabling
    1498              :  * data checksums in each applicable relation in the database, it will wait for
    1499              :  * all temporary relations that were present when the function started to
    1500              :  * disappear before returning. This is required since we cannot rewrite
    1501              :  * existing temporary relations with data checksums.
    1502              :  */
    1503              : void
    1504           23 : DataChecksumsWorkerMain(Datum arg)
    1505              : {
    1506           23 :     Oid         dboid = DatumGetObjectId(arg);
    1507           23 :     List       *RelationList = NIL;
    1508           23 :     List       *InitialTempTableList = NIL;
    1509              :     BufferAccessStrategy strategy;
    1510           23 :     bool        aborted = false;
    1511              :     int64       rels_done;
    1512              : #ifdef USE_INJECTION_POINTS
    1513           23 :     bool        retried = false;
    1514              : #endif
    1515              : 
    1516           23 :     operation = ENABLE_DATACHECKSUMS;
    1517              : 
    1518           23 :     pqsignal(SIGTERM, die);
    1519           23 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    1520              : 
    1521           23 :     BackgroundWorkerUnblockSignals();
    1522              : 
    1523           23 :     MyBackendType = B_DATACHECKSUMSWORKER_WORKER;
    1524           23 :     init_ps_display(NULL);
    1525              : 
    1526           23 :     BackgroundWorkerInitializeConnectionByOid(dboid, InvalidOid,
    1527              :                                               BGWORKER_BYPASS_ALLOWCONN);
    1528              : 
    1529              :     /* worker will have a separate entry in pg_stat_progress_data_checksums */
    1530           23 :     pgstat_progress_start_command(PROGRESS_COMMAND_DATACHECKSUMS,
    1531              :                                   InvalidOid);
    1532              : 
    1533              :     /*
    1534              :      * Get a list of all temp tables present as we start in this database. We
    1535              :      * need to wait until they are all gone until we are done, since we cannot
    1536              :      * access these relations and modify them.
    1537              :      */
    1538           23 :     InitialTempTableList = BuildRelationList(true, false);
    1539              : 
    1540              :     /*
    1541              :      * Enable vacuum cost delay, if any.  While this process isn't doing any
    1542              :      * vacuuming, we are re-using the infrastructure that vacuum cost delay
    1543              :      * provides rather than inventing something bespoke. This is an internal
    1544              :      * implementation detail and care should be taken to avoid it bleeding
    1545              :      * through to the user to avoid confusion.
    1546              :      *
    1547              :      * VacuumUpdateCosts() propagates the values to the variables actually
    1548              :      * read by vacuum_delay_point().
    1549              :      */
    1550           23 :     VacuumCostDelay = DataChecksumState->cost_delay;
    1551           23 :     VacuumCostLimit = DataChecksumState->cost_limit;
    1552           23 :     VacuumUpdateCosts();
    1553           23 :     VacuumCostBalance = 0;
    1554              : 
    1555              :     /*
    1556              :      * Create and set the vacuum strategy as our buffer strategy.
    1557              :      */
    1558           23 :     strategy = GetAccessStrategy(BAS_VACUUM);
    1559              : 
    1560           23 :     RelationList = BuildRelationList(false,
    1561           23 :                                      DataChecksumState->process_shared_catalogs);
    1562              : 
    1563              :     /* Update the total number of relations to be processed in this DB. */
    1564              :     {
    1565           23 :         const int   index[] = {
    1566              :             PROGRESS_DATACHECKSUMS_RELS_TOTAL,
    1567              :             PROGRESS_DATACHECKSUMS_RELS_DONE
    1568              :         };
    1569              : 
    1570              :         int64       vals[2];
    1571              : 
    1572           23 :         vals[0] = list_length(RelationList);
    1573           23 :         vals[1] = 0;
    1574              : 
    1575           23 :         pgstat_progress_update_multi_param(2, index, vals);
    1576              :     }
    1577              : 
    1578              :     /* Process the relations */
    1579           23 :     rels_done = 0;
    1580         6086 :     foreach_oid(reloid, RelationList)
    1581              :     {
    1582         6040 :         bool        costs_updated = false;
    1583              : 
    1584         6040 :         if (!ProcessSingleRelationByOid(reloid, strategy))
    1585              :         {
    1586            0 :             aborted = true;
    1587            0 :             break;
    1588              :         }
    1589              : 
    1590         6040 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_RELS_DONE,
    1591              :                                      ++rels_done);
    1592         6040 :         CHECK_FOR_INTERRUPTS();
    1593         6040 :         CHECK_FOR_ABORT_REQUEST();
    1594              : 
    1595         6040 :         if (abort_requested)
    1596            0 :             break;
    1597              : 
    1598              :         /*
    1599              :          * Check if the cost settings changed during runtime and if so, update
    1600              :          * to reflect the new values and signal that the access strategy needs
    1601              :          * to be refreshed.
    1602              :          */
    1603         6040 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1604         6040 :         if ((DataChecksumState->launch_cost_delay != DataChecksumState->cost_delay)
    1605         6040 :             || (DataChecksumState->launch_cost_limit != DataChecksumState->cost_limit))
    1606              :         {
    1607            0 :             costs_updated = true;
    1608            0 :             VacuumCostDelay = DataChecksumState->launch_cost_delay;
    1609            0 :             VacuumCostLimit = DataChecksumState->launch_cost_limit;
    1610            0 :             VacuumUpdateCosts();
    1611              : 
    1612            0 :             DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1613            0 :             DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1614              :         }
    1615              :         else
    1616         6040 :             costs_updated = false;
    1617         6040 :         LWLockRelease(DataChecksumsWorkerLock);
    1618              : 
    1619         6040 :         if (costs_updated)
    1620              :         {
    1621            0 :             FreeAccessStrategy(strategy);
    1622            0 :             strategy = GetAccessStrategy(BAS_VACUUM);
    1623              :         }
    1624              :     }
    1625              : 
    1626           23 :     list_free(RelationList);
    1627           23 :     FreeAccessStrategy(strategy);
    1628              : 
    1629           23 :     if (aborted || abort_requested)
    1630              :     {
    1631            0 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1632            0 :         DataChecksumState->success = DATACHECKSUMSWORKER_ABORTED;
    1633            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1634            0 :         ereport(DEBUG1,
    1635              :                 errmsg("data checksum processing aborted in database OID %u",
    1636              :                        dboid));
    1637            0 :         return;
    1638              :     }
    1639              : 
    1640              :     /* The worker is about to wait for temporary tables to go away. */
    1641           23 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1642              :                                  PROGRESS_DATACHECKSUMS_PHASE_WAITING_TEMPREL);
    1643              : 
    1644              :     /*
    1645              :      * Wait for all temp tables that existed when we started to go away. This
    1646              :      * is necessary since we cannot "reach" them to enable checksums. Any temp
    1647              :      * tables created after we started will already have checksums in them
    1648              :      * (due to the "inprogress-on" state), so no need to wait for those.
    1649              :      */
    1650              :     for (;;)
    1651            0 :     {
    1652              :         List       *CurrentTempTables;
    1653              :         int         numleft;
    1654              :         char        activity[64];
    1655              : 
    1656           23 :         CurrentTempTables = BuildRelationList(true, false);
    1657           23 :         numleft = 0;
    1658           47 :         foreach_oid(tmptbloid, InitialTempTableList)
    1659              :         {
    1660            1 :             if (list_member_oid(CurrentTempTables, tmptbloid))
    1661            1 :                 numleft++;
    1662              :         }
    1663           23 :         list_free(CurrentTempTables);
    1664              : 
    1665              : #ifdef USE_INJECTION_POINTS
    1666           23 :         if (IS_INJECTION_POINT_ATTACHED("datachecksumsworker-fake-temptable-wait"))
    1667              :         {
    1668              :             /* Make sure to just cause one retry */
    1669            0 :             if (!retried && numleft == 0)
    1670              :             {
    1671            0 :                 numleft = 1;
    1672            0 :                 retried = true;
    1673              : 
    1674            0 :                 INJECTION_POINT_CACHED("datachecksumsworker-fake-temptable-wait", NULL);
    1675              :             }
    1676              :         }
    1677              : #endif
    1678              : 
    1679           23 :         if (numleft == 0)
    1680           22 :             break;
    1681              : 
    1682              :         /*
    1683              :          * At least one temp table is left to wait for, indicate in pgstat
    1684              :          * activity and progress reporting.
    1685              :          */
    1686            1 :         snprintf(activity,
    1687              :                  sizeof(activity),
    1688              :                  "Waiting for %d temp tables to be removed", numleft);
    1689            1 :         pgstat_report_activity(STATE_RUNNING, activity);
    1690              : 
    1691              :         /* Retry every 3 seconds */
    1692            1 :         ResetLatch(MyLatch);
    1693            1 :         (void) WaitLatch(MyLatch,
    1694              :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
    1695              :                          3000,
    1696              :                          WAIT_EVENT_CHECKSUM_ENABLE_TEMPTABLE_WAIT);
    1697              : 
    1698            1 :         CHECK_FOR_INTERRUPTS();
    1699            0 :         CHECK_FOR_ABORT_REQUEST();
    1700              : 
    1701            0 :         if (aborted || abort_requested)
    1702              :         {
    1703            0 :             LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1704            0 :             DataChecksumState->success = DATACHECKSUMSWORKER_ABORTED;
    1705            0 :             LWLockRelease(DataChecksumsWorkerLock);
    1706            0 :             ereport(LOG,
    1707              :                     errmsg("data checksum processing aborted in database OID %u",
    1708              :                            dboid));
    1709            0 :             return;
    1710              :         }
    1711              :     }
    1712              : 
    1713           22 :     list_free(InitialTempTableList);
    1714              : 
    1715              :     /* worker done */
    1716           22 :     pgstat_progress_end_command();
    1717              : 
    1718           22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1719           22 :     DataChecksumState->success = DATACHECKSUMSWORKER_SUCCESSFUL;
    1720           22 :     LWLockRelease(DataChecksumsWorkerLock);
    1721              : }
        

Generated by: LCOV version 2.0-1