LCOV - code coverage report
Current view: top level - src/backend/postmaster - datachecksum_state.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 75.5 % 493 372
Test Date: 2026-06-30 20:16:43 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              : /* Possible operations the DataChecksumsWorker can perform */
     280              : typedef enum DataChecksumsWorkerOperation
     281              : {
     282              :     ENABLE_DATACHECKSUMS,
     283              :     DISABLE_DATACHECKSUMS,
     284              : } DataChecksumsWorkerOperation;
     285              : 
     286              : /* Possible states for a database entry which has been processed */
     287              : typedef enum
     288              : {
     289              :     DATACHECKSUMSWORKER_SUCCESSFUL = 0,
     290              :     DATACHECKSUMSWORKER_ABORTED,
     291              :     DATACHECKSUMSWORKER_FAILED,
     292              :     DATACHECKSUMSWORKER_DROPDB,
     293              : } DataChecksumsWorkerResult;
     294              : 
     295              : /*
     296              :  * Signaling between backends calling pg_enable/disable_data_checksums, the
     297              :  * checksums launcher process, and the checksums worker process.
     298              :  *
     299              :  * This struct is protected by DataChecksumsWorkerLock
     300              :  */
     301              : typedef struct DataChecksumsStateStruct
     302              : {
     303              :     /*
     304              :      * These are set by pg_{enable|disable}_data_checksums, to tell the
     305              :      * launcher what the target state is.
     306              :      */
     307              :     DataChecksumsWorkerOperation launch_operation;
     308              :     int         launch_cost_delay;
     309              :     int         launch_cost_limit;
     310              : 
     311              :     /*
     312              :      * Is a launcher process currently running?  This is set by the main
     313              :      * launcher process, after it has read the above launch_* parameters.
     314              :      */
     315              :     bool        launcher_running;
     316              : 
     317              :     /*
     318              :      * Every time a new worker is launched, it's assigned a unique invocation
     319              :      * number by incrementing this counter.
     320              :      */
     321              :     uint64      worker_invocation_counter;
     322              : 
     323              :     /*
     324              :      * Information about the current worker, if it's currently running.  These
     325              :      * are set by the worker launcher.
     326              :      */
     327              :     uint64      worker_invocation;  /* unique invocation number */
     328              :     Oid         database_oid;   /* database it's processing */
     329              :     pid_t       worker_pid;     /* worker process's PID */
     330              : 
     331              :     /*
     332              :      * These fields indicate the target state that the worker is currently
     333              :      * running with.  They can be different from the corresponding launch_*
     334              :      * fields, if a new pg_enable/disable_data_checksums() call was made while
     335              :      * the launcher/worker was already running.  The worker will periodically
     336              :      * check if new cost settings have been requested, and if so will copy
     337              :      * them from the launch_* fields and reset cost throttling to match the
     338              :      * new values.
     339              :      */
     340              :     DataChecksumsWorkerOperation operation;
     341              :     int         cost_delay;
     342              :     int         cost_limit;
     343              : 
     344              :     /*
     345              :      * Signaling between the launcher and the worker process. Protected by
     346              :      * DataChecksumsWorkerLock.
     347              :      */
     348              : 
     349              :     /* result, set by worker before exiting */
     350              :     DataChecksumsWorkerResult worker_result;
     351              : 
     352              :     /*
     353              :      * Tells the worker process whether it should also process the shared
     354              :      * catalogs
     355              :      */
     356              :     bool        process_shared_catalogs;
     357              : } DataChecksumsStateStruct;
     358              : 
     359              : /* Shared memory segment for datachecksumsworker */
     360              : static DataChecksumsStateStruct *DataChecksumState;
     361              : 
     362              : typedef struct DataChecksumsWorkerDatabase
     363              : {
     364              :     Oid         dboid;
     365              :     char       *dbname;
     366              : } DataChecksumsWorkerDatabase;
     367              : 
     368              : /* Flag set by the interrupt handler */
     369              : static volatile sig_atomic_t abort_requested = false;
     370              : 
     371              : static uint64 worker_invocation;
     372              : 
     373              : /*
     374              :  * Have we set the DataChecksumsStateStruct->launcher_running flag?
     375              :  * If we have, we need to clear it before exiting!
     376              :  */
     377              : static volatile sig_atomic_t launcher_running = false;
     378              : 
     379              : /* Are we enabling data checksums, or disabling them? */
     380              : static DataChecksumsWorkerOperation operation;
     381              : 
     382              : /* Prototypes */
     383              : static void StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op,
     384              :                                              int cost_delay,
     385              :                                              int cost_limit);
     386              : static void DataChecksumsShmemRequest(void *arg);
     387              : static bool DatabaseExists(Oid dboid);
     388              : static List *BuildDatabaseList(void);
     389              : static List *BuildRelationList(bool temp_relations, bool include_shared);
     390              : static void FreeDatabaseList(List *dblist);
     391              : static DataChecksumsWorkerResult ProcessDatabase(DataChecksumsWorkerDatabase *db);
     392              : static bool ProcessAllDatabases(void);
     393              : static bool ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy);
     394              : static void launcher_cancel_handler(SIGNAL_ARGS);
     395              : static void WaitForAllTransactionsToFinish(void);
     396              : 
     397              : const ShmemCallbacks DataChecksumsShmemCallbacks = {
     398              :     .request_fn = DataChecksumsShmemRequest,
     399              : };
     400              : 
     401              : #define CHECK_FOR_LAUNCHER_ABORT_REQUEST() \
     402              :     do {                                                            \
     403              :         Assert(MyBackendType == B_DATACHECKSUMSWORKER_LAUNCHER);    \
     404              :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);          \
     405              :         if (DataChecksumState->launch_operation != operation)        \
     406              :             abort_requested = true;                                 \
     407              :         LWLockRelease(DataChecksumsWorkerLock);                     \
     408              :     } while (0)
     409              : 
     410              : #define CHECK_FOR_WORKER_ABORT_REQUEST() \
     411              :     do {                                                            \
     412              :         Assert(MyBackendType == B_DATACHECKSUMSWORKER_WORKER);      \
     413              :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);          \
     414              :         if (DataChecksumState->worker_invocation != worker_invocation || \
     415              :             DataChecksumState->launch_operation != operation)        \
     416              :             abort_requested = true;                                 \
     417              :         LWLockRelease(DataChecksumsWorkerLock);                     \
     418              :     } while (0)
     419              : 
     420              : 
     421              : /*****************************************************************************
     422              :  * Functionality for manipulating the data checksum state in the cluster
     423              :  */
     424              : 
     425              : void
     426            8 : EmitAndWaitDataChecksumsBarrier(uint32 state)
     427              : {
     428              :     uint64      barrier;
     429              : 
     430            8 :     switch (state)
     431              :     {
     432            3 :         case PG_DATA_CHECKSUM_INPROGRESS_ON:
     433            3 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_ON);
     434            3 :             WaitForProcSignalBarrier(barrier);
     435            3 :             break;
     436              : 
     437            1 :         case PG_DATA_CHECKSUM_INPROGRESS_OFF:
     438            1 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_OFF);
     439            1 :             WaitForProcSignalBarrier(barrier);
     440            1 :             break;
     441              : 
     442            2 :         case PG_DATA_CHECKSUM_VERSION:
     443            2 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_ON);
     444            2 :             WaitForProcSignalBarrier(barrier);
     445            2 :             break;
     446              : 
     447            2 :         case PG_DATA_CHECKSUM_OFF:
     448            2 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_OFF);
     449            2 :             WaitForProcSignalBarrier(barrier);
     450            2 :             break;
     451              : 
     452            8 :         default:
     453              :             Assert(false);
     454              :     }
     455            8 : }
     456              : 
     457              : /*
     458              :  * AbsorbDataChecksumsBarrier
     459              :  *      Generic function for absorbing data checksum state changes
     460              :  *
     461              :  * All procsignalbarriers regarding data checksum state changes are absorbed
     462              :  * with this function.  The set of conditions required for the state change to
     463              :  * be accepted are listed in the checksum_barriers struct, target_state is
     464              :  * used to look up the relevant entry.
     465              :  */
     466              : bool
     467          277 : AbsorbDataChecksumsBarrier(ProcSignalBarrierType barrier)
     468              : {
     469              :     uint32      target_state;
     470          277 :     int         current = data_checksums;
     471          277 :     bool        found = false;
     472              : 
     473              :     /*
     474              :      * Translate the barrier condition to the target state, doing it here
     475              :      * instead of in the procsignal code saves the latter from knowing about
     476              :      * checksum states.
     477              :      */
     478          277 :     switch (barrier)
     479              :     {
     480           95 :         case PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_ON:
     481           95 :             target_state = PG_DATA_CHECKSUM_INPROGRESS_ON;
     482           95 :             break;
     483           72 :         case PROCSIGNAL_BARRIER_CHECKSUM_ON:
     484           72 :             target_state = PG_DATA_CHECKSUM_VERSION;
     485           72 :             break;
     486           52 :         case PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_OFF:
     487           52 :             target_state = PG_DATA_CHECKSUM_INPROGRESS_OFF;
     488           52 :             break;
     489           58 :         case PROCSIGNAL_BARRIER_CHECKSUM_OFF:
     490           58 :             target_state = PG_DATA_CHECKSUM_OFF;
     491           58 :             break;
     492            0 :         default:
     493            0 :             elog(ERROR, "incorrect barrier \"%i\" received", barrier);
     494              :     }
     495              : 
     496              :     /*
     497              :      * If the target state matches the current state then the barrier has been
     498              :      * repeated.
     499              :      */
     500          277 :     if (current == target_state)
     501            1 :         return true;
     502              : 
     503              :     /*
     504              :      * If the cluster is in recovery we skip the validation of current state
     505              :      * since the replay is trusted.
     506              :      */
     507          276 :     if (RecoveryInProgress())
     508              :     {
     509           48 :         SetLocalDataChecksumState(target_state);
     510           48 :         return true;
     511              :     }
     512              : 
     513              :     /*
     514              :      * Find the barrier condition definition for the target state. Not finding
     515              :      * a condition would be a grave programmer error as the states are a
     516              :      * discrete set.
     517              :      */
     518         1042 :     for (int i = 0; i < lengthof(checksum_barriers) && !found; i++)
     519              :     {
     520          814 :         if (checksum_barriers[i].from == current && checksum_barriers[i].to == target_state)
     521          228 :             found = true;
     522              :     }
     523              : 
     524              :     /*
     525              :      * If the relevant state criteria aren't satisfied, throw an error which
     526              :      * will be caught by the procsignal machinery for a later retry.
     527              :      */
     528          228 :     if (!found)
     529            0 :         ereport(ERROR,
     530              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     531              :                 errmsg("incorrect data checksum state %i for target state %i",
     532              :                        current, target_state));
     533              : 
     534          228 :     SetLocalDataChecksumState(target_state);
     535          228 :     return true;
     536              : }
     537              : 
     538              : 
     539              : /*
     540              :  * Disables data checksums for the cluster, if applicable. Starts a background
     541              :  * worker which turns off the data checksums.
     542              :  */
     543              : Datum
     544            7 : disable_data_checksums(PG_FUNCTION_ARGS)
     545              : {
     546            7 :     PreventCommandDuringRecovery("pg_disable_data_checksums()");
     547              : 
     548            7 :     if (!superuser())
     549            0 :         ereport(ERROR,
     550              :                 errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     551              :                 errmsg("must be superuser to change data checksum state"));
     552              : 
     553            7 :     StartDataChecksumsWorkerLauncher(DISABLE_DATACHECKSUMS, 0, 0);
     554            7 :     PG_RETURN_VOID();
     555              : }
     556              : 
     557              : /*
     558              :  * Enables data checksums for the cluster, if applicable.  Supports vacuum-
     559              :  * like cost based throttling to limit system load. Starts a background worker
     560              :  * which updates data checksums on existing data.
     561              :  */
     562              : Datum
     563           11 : enable_data_checksums(PG_FUNCTION_ARGS)
     564              : {
     565           11 :     int         cost_delay = PG_GETARG_INT32(0);
     566           11 :     int         cost_limit = PG_GETARG_INT32(1);
     567              : 
     568           11 :     PreventCommandDuringRecovery("pg_enable_data_checksums()");
     569              : 
     570           11 :     if (!superuser())
     571            0 :         ereport(ERROR,
     572              :                 errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     573              :                 errmsg("must be superuser to change data checksum state"));
     574              : 
     575           11 :     if (cost_delay < 0)
     576            0 :         ereport(ERROR,
     577              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     578              :                 errmsg("cost delay cannot be a negative value"));
     579              : 
     580           11 :     if (cost_limit <= 0)
     581            0 :         ereport(ERROR,
     582              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     583              :                 errmsg("cost limit must be greater than zero"));
     584              : 
     585           11 :     StartDataChecksumsWorkerLauncher(ENABLE_DATACHECKSUMS, cost_delay, cost_limit);
     586              : 
     587           11 :     PG_RETURN_VOID();
     588              : }
     589              : 
     590              : 
     591              : /*****************************************************************************
     592              :  * Functionality for running the datachecksumsworker and associated launcher
     593              :  */
     594              : 
     595              : /*
     596              :  * StartDataChecksumsWorkerLauncher
     597              :  *      Start the datachecksumsworker launcher process, if not running yet
     598              :  *
     599              :  * This is called to start data checksums processing for enabling as well as
     600              :  * disabling.
     601              :  */
     602              : static void
     603           18 : StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op,
     604              :                                  int cost_delay,
     605              :                                  int cost_limit)
     606              : {
     607              :     BackgroundWorker bgw;
     608              :     BackgroundWorkerHandle *bgw_handle;
     609              :     bool        running;
     610              : 
     611              : #ifdef USE_ASSERT_CHECKING
     612              :     /* The cost delay settings have no effect when disabling */
     613              :     if (op == DISABLE_DATACHECKSUMS)
     614              :         Assert(cost_delay == 0 && cost_limit == 0);
     615              : #endif
     616              : 
     617           18 :     INJECTION_POINT("datachecksumsworker-startup-delay", NULL);
     618              : 
     619              :     /* Store the desired state in shared memory */
     620           18 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     621              : 
     622           18 :     DataChecksumState->launch_operation = op;
     623           18 :     DataChecksumState->launch_cost_delay = cost_delay;
     624           18 :     DataChecksumState->launch_cost_limit = cost_limit;
     625              : 
     626              :     /* Is the launcher already running? If so, what is it doing? */
     627           18 :     running = DataChecksumState->launcher_running;
     628              : 
     629           18 :     LWLockRelease(DataChecksumsWorkerLock);
     630              : 
     631              :     /*
     632              :      * Launch a new launcher process, if it's not running already.
     633              :      *
     634              :      * If the launcher is currently busy enabling the checksums, and we want
     635              :      * them disabled (or vice versa), the launcher will notice that at latest
     636              :      * when it's about to exit, and will loop back to process the new request.
     637              :      * So if the launcher is already running, we don't need to do anything
     638              :      * more here to abort it.
     639              :      *
     640              :      * If you call pg_enable/disable_data_checksums() twice in a row, before
     641              :      * the launcher has had a chance to start up, we still end up launching it
     642              :      * twice.  That's OK, the second invocation will see that a launcher is
     643              :      * already running and exit quickly.
     644              :      */
     645           18 :     if (!running)
     646              :     {
     647           18 :         if ((op == ENABLE_DATACHECKSUMS && DataChecksumsOn()) ||
     648            7 :             (op == DISABLE_DATACHECKSUMS && DataChecksumsOff()))
     649              :         {
     650            3 :             ereport(LOG,
     651              :                     errmsg("data checksums already in desired state, exiting"));
     652            3 :             return;
     653              :         }
     654              : 
     655              :         /*
     656              :          * Prepare the BackgroundWorker and launch it.
     657              :          */
     658           15 :         memset(&bgw, 0, sizeof(bgw));
     659           15 :         bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
     660           15 :         bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
     661           15 :         snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
     662           15 :         snprintf(bgw.bgw_function_name, BGW_MAXLEN, "DataChecksumsWorkerLauncherMain");
     663           15 :         snprintf(bgw.bgw_name, BGW_MAXLEN, "datachecksums launcher");
     664           15 :         snprintf(bgw.bgw_type, BGW_MAXLEN, "datachecksums launcher");
     665           15 :         bgw.bgw_restart_time = BGW_NEVER_RESTART;
     666           15 :         bgw.bgw_notify_pid = MyProcPid;
     667           15 :         bgw.bgw_main_arg = (Datum) 0;
     668              : 
     669           15 :         if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
     670            0 :             ereport(ERROR,
     671              :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
     672              :                     errmsg("failed to start background worker to process data checksums"));
     673              :     }
     674              :     else
     675              :     {
     676            0 :         ereport(LOG,
     677              :                 errmsg("data checksum processing already running"));
     678              :     }
     679              : }
     680              : 
     681              : /*
     682              :  * ProcessSingleRelationFork
     683              :  *      Enable data checksums in a single relation/fork.
     684              :  *
     685              :  * Returns true if successful, and false if *aborted*. On error, an actual
     686              :  * error is raised in the lower levels.
     687              :  */
     688              : static bool
     689         7704 : ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy)
     690              : {
     691         7704 :     BlockNumber numblocks = RelationGetNumberOfBlocksInFork(reln, forkNum);
     692              :     char        activity[NAMEDATALEN * 2 + 128];
     693              :     char       *relns;
     694              : 
     695         7704 :     relns = get_namespace_name(RelationGetNamespace(reln));
     696              : 
     697              :     /* Report the current relation to pg_stat_activity */
     698         7704 :     snprintf(activity, sizeof(activity) - 1, "processing: %s.%s (%s, %u blocks)",
     699         7704 :              (relns ? relns : ""), RelationGetRelationName(reln), forkNames[forkNum], numblocks);
     700         7704 :     pgstat_report_activity(STATE_RUNNING, activity);
     701         7704 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_BLOCKS_TOTAL, numblocks);
     702         7704 :     if (relns)
     703         7704 :         pfree(relns);
     704              : 
     705              :     /*
     706              :      * We are looping over the blocks which existed at the time of process
     707              :      * start, which is safe since new blocks are created with checksums set
     708              :      * already due to the state being "inprogress-on".
     709              :      */
     710        48348 :     for (BlockNumber blknum = 0; blknum < numblocks; blknum++)
     711              :     {
     712        40645 :         Buffer      buf = ReadBufferExtended(reln, forkNum, blknum, RBM_NORMAL, strategy);
     713              : 
     714              :         /* Need to get an exclusive lock to mark the buffer as dirty */
     715        40645 :         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     716              : 
     717              :         /*
     718              :          * Mark the buffer as dirty and force a full page write.  We have to
     719              :          * re-write the page to WAL even if the checksum hasn't changed,
     720              :          * because if there is a replica it might have a slightly different
     721              :          * version of the page with an invalid checksum, caused by unlogged
     722              :          * changes (e.g. hint bits) on the primary happening while checksums
     723              :          * were off. This can happen if there was a valid checksum on the page
     724              :          * at one point in the past, so only when checksums are first on, then
     725              :          * off, and then turned on again.  TODO: investigate if this could be
     726              :          * avoided if the checksum is calculated to be correct and wal_level
     727              :          * is set to "minimal".
     728              :          *
     729              :          * Unlogged relations don't need WAL since they are reset to their
     730              :          * init fork on recovery.  We still dirty the buffer so that the
     731              :          * checksum is written to disk at the next checkpoint.
     732              :          *
     733              :          * The init fork is an exception: it is WAL-logged so the standby can
     734              :          * materialize the relation after promotion (see
     735              :          * ResetUnloggedRelations()).  Skipping it here would leave the
     736              :          * standby with a stale init fork that, once copied to the main fork
     737              :          * on promotion, would fail checksum verification on every read.
     738              :          */
     739        40645 :         START_CRIT_SECTION();
     740        40645 :         MarkBufferDirty(buf);
     741        40645 :         if (RelationNeedsWAL(reln) || forkNum == INIT_FORKNUM)
     742        40611 :             log_newpage_buffer(buf, false);
     743        40645 :         END_CRIT_SECTION();
     744              : 
     745        40645 :         UnlockReleaseBuffer(buf);
     746              : 
     747              :         /* Check if we are asked to abort, the abortion will bubble up. */
     748              :         Assert(operation == ENABLE_DATACHECKSUMS);
     749        40645 :         CHECK_FOR_WORKER_ABORT_REQUEST();
     750        40645 :         if (abort_requested)
     751            0 :             return false;
     752              : 
     753              :         /* update the block counter */
     754        40645 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_BLOCKS_DONE,
     755        40645 :                                      (blknum + 1));
     756              : 
     757              :         /*
     758              :          * Processing is re-using the vacuum cost delay for process
     759              :          * throttling, hence why we call vacuum APIs here.
     760              :          */
     761        40645 :         vacuum_delay_point(false);
     762              :     }
     763              : 
     764         7703 :     return true;
     765              : }
     766              : 
     767              : /*
     768              :  * ProcessSingleRelationByOid
     769              :  *      Process a single relation based on oid.
     770              :  *
     771              :  * Returns true if successful, and false if *aborted*. On error, an actual
     772              :  * error is raised in the lower levels.
     773              :  */
     774              : static bool
     775         5968 : ProcessSingleRelationByOid(Oid relationId, BufferAccessStrategy strategy)
     776              : {
     777              :     Relation    rel;
     778         5968 :     bool        aborted = false;
     779              : 
     780         5968 :     StartTransactionCommand();
     781              : 
     782         5968 :     rel = try_relation_open(relationId, AccessShareLock);
     783         5968 :     if (rel == NULL)
     784              :     {
     785              :         /*
     786              :          * Relation no longer exists. We don't consider this an error since
     787              :          * there are no pages in it that need data checksums, and thus return
     788              :          * true. The worker operates off a list of relations generated at the
     789              :          * start of processing, so relations being dropped in the meantime is
     790              :          * to be expected.
     791              :          */
     792            0 :         CommitTransactionCommand();
     793            0 :         pgstat_report_activity(STATE_IDLE, NULL);
     794            0 :         return true;
     795              :     }
     796         5968 :     RelationGetSmgr(rel);
     797              : 
     798        29836 :     for (ForkNumber fnum = 0; fnum <= MAX_FORKNUM; fnum++)
     799              :     {
     800        23869 :         if (smgrexists(rel->rd_smgr, fnum))
     801              :         {
     802         7704 :             if (!ProcessSingleRelationFork(rel, fnum, strategy))
     803              :             {
     804            0 :                 aborted = true;
     805            0 :                 break;
     806              :             }
     807              :         }
     808              :     }
     809         5967 :     relation_close(rel, AccessShareLock);
     810              : 
     811         5967 :     CommitTransactionCommand();
     812         5967 :     pgstat_report_activity(STATE_IDLE, NULL);
     813              : 
     814         5967 :     return !aborted;
     815              : }
     816              : 
     817              : /*
     818              :  * ProcessDatabase
     819              :  *      Enable data checksums in a single database.
     820              :  *
     821              :  * We do this by launching a dynamic background worker into this database, and
     822              :  * waiting for it to finish.  We have to do this in a separate worker, since
     823              :  * each process can only be connected to one database during its lifetime.
     824              :  */
     825              : static DataChecksumsWorkerResult
     826           23 : ProcessDatabase(DataChecksumsWorkerDatabase *db)
     827              : {
     828              :     BackgroundWorker bgw;
     829              :     BackgroundWorkerHandle *bgw_handle;
     830              :     BgwHandleStatus status;
     831              :     pid_t       pid;
     832              :     uint64      invocation;
     833              :     char        activity[NAMEDATALEN + 64];
     834              :     DataChecksumsWorkerResult result;
     835              : 
     836           23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     837              : 
     838              :     /*
     839              :      * Initialize result to FAILED.  The worker will change it to SUCCESSFUL
     840              :      * if it completes successfully.
     841              :      */
     842           23 :     DataChecksumState->worker_result = DATACHECKSUMSWORKER_FAILED;
     843           23 :     DataChecksumState->worker_pid = InvalidPid;
     844              : 
     845           23 :     invocation = ++DataChecksumState->worker_invocation_counter;
     846           23 :     DataChecksumState->worker_invocation = invocation;
     847           23 :     DataChecksumState->database_oid = db->dboid;
     848              : 
     849           23 :     LWLockRelease(DataChecksumsWorkerLock);
     850              : 
     851           23 :     memset(&bgw, 0, sizeof(bgw));
     852           23 :     bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
     853           23 :     bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
     854           23 :     snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
     855           23 :     snprintf(bgw.bgw_function_name, BGW_MAXLEN, "%s", "DataChecksumsWorkerMain");
     856           23 :     snprintf(bgw.bgw_name, BGW_MAXLEN, "datachecksums worker");
     857           23 :     snprintf(bgw.bgw_type, BGW_MAXLEN, "datachecksums worker");
     858           23 :     bgw.bgw_restart_time = BGW_NEVER_RESTART;
     859           23 :     bgw.bgw_notify_pid = MyProcPid;
     860              :     /* pass the invocation number to the worker process */
     861           23 :     bgw.bgw_main_arg = UInt64GetDatum(invocation);
     862              : 
     863              :     /*
     864              :      * If there are no worker slots available, there is little we can do.  If
     865              :      * we retry in a bit it's still unlikely that the user has managed to
     866              :      * reconfigure in the meantime and we'd be run through retries fast.
     867              :      */
     868           23 :     if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
     869              :     {
     870            0 :         ereport(WARNING,
     871              :                 errmsg("could not start background worker for enabling data checksums in database \"%s\"",
     872              :                        db->dbname),
     873              :                 errhint("The \"%s\" setting might be too low.", "max_worker_processes"));
     874            0 :         return DATACHECKSUMSWORKER_FAILED;
     875              :     }
     876              : 
     877           23 :     status = WaitForBackgroundWorkerStartup(bgw_handle, &pid);
     878           23 :     if (status == BGWH_STOPPED)
     879              :     {
     880              :         /*
     881              :          * If the worker managed to start, and stop, before we got to waiting
     882              :          * for it we can see a STOPPED status here without it being a failure.
     883              :          */
     884            0 :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
     885              :         Assert(DataChecksumState->worker_invocation == invocation);
     886            0 :         if (DataChecksumState->worker_result == DATACHECKSUMSWORKER_SUCCESSFUL)
     887              :         {
     888            0 :             LWLockRelease(DataChecksumsWorkerLock);
     889            0 :             pgstat_report_activity(STATE_IDLE, NULL);
     890            0 :             return DATACHECKSUMSWORKER_SUCCESSFUL;
     891              :         }
     892            0 :         LWLockRelease(DataChecksumsWorkerLock);
     893              : 
     894            0 :         ereport(WARNING,
     895              :                 errmsg("could not start background worker for enabling data checksums in database \"%s\"",
     896              :                        db->dbname),
     897              :                 errhint("More details on the error might be found in the server log."));
     898              : 
     899              :         /*
     900              :          * Heuristic to see if the database was dropped, and if it was we can
     901              :          * treat it as not an error, else treat as fatal and error out.
     902              :          */
     903            0 :         if (DatabaseExists(db->dboid))
     904            0 :             return DATACHECKSUMSWORKER_FAILED;
     905              :         else
     906            0 :             return DATACHECKSUMSWORKER_DROPDB;
     907              :     }
     908              : 
     909              :     /*
     910              :      * If the postmaster crashed we cannot end up with a processed database so
     911              :      * we have no alternative other than exiting. When enabling checksums we
     912              :      * won't at this time have changed the data checksums state in pg_control
     913              :      * to enabled so when the cluster comes back up processing will have to be
     914              :      * restarted.
     915              :      */
     916           23 :     if (status == BGWH_POSTMASTER_DIED)
     917            0 :         ereport(FATAL,
     918              :                 errcode(ERRCODE_ADMIN_SHUTDOWN),
     919              :                 errmsg("cannot enable data checksums without the postmaster process"),
     920              :                 errhint("Restart the database and restart data checksum processing by calling pg_enable_data_checksums()."));
     921              : 
     922              :     Assert(status == BGWH_STARTED);
     923           23 :     ereport(LOG,
     924              :             errmsg("initiating data checksum processing in database \"%s\"",
     925              :                    db->dbname));
     926              : 
     927              :     /* Save the pid of the worker so we can signal it later */
     928           23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     929              :     Assert(DataChecksumState->worker_invocation == invocation);
     930           23 :     DataChecksumState->worker_pid = pid;
     931           23 :     LWLockRelease(DataChecksumsWorkerLock);
     932              : 
     933           23 :     snprintf(activity, sizeof(activity) - 1,
     934              :              "Waiting for worker in database %s (pid %ld)", db->dbname, (long) pid);
     935           23 :     pgstat_report_activity(STATE_RUNNING, activity);
     936              : 
     937           23 :     status = WaitForBackgroundWorkerShutdown(bgw_handle);
     938           22 :     if (status == BGWH_POSTMASTER_DIED)
     939            0 :         ereport(FATAL,
     940              :                 errcode(ERRCODE_ADMIN_SHUTDOWN),
     941              :                 errmsg("postmaster exited during data checksum processing in \"%s\"",
     942              :                        db->dbname),
     943              :                 errhint("Restart the database and restart data checksum processing by calling pg_enable_data_checksums()."));
     944              : 
     945           22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     946              :     Assert(DataChecksumState->worker_invocation == invocation);
     947           22 :     result = DataChecksumState->worker_result;
     948           22 :     DataChecksumState->worker_pid = InvalidPid;
     949           22 :     LWLockRelease(DataChecksumsWorkerLock);
     950              : 
     951           22 :     if (result == DATACHECKSUMSWORKER_ABORTED)
     952            0 :         ereport(LOG,
     953              :                 errmsg("data checksums processing was aborted in database \"%s\"",
     954              :                        db->dbname));
     955           22 :     pgstat_report_activity(STATE_IDLE, NULL);
     956           22 :     return result;
     957              : }
     958              : 
     959              : /*
     960              :  * launcher_exit
     961              :  *
     962              :  * Internal routine for cleaning up state when a launcher process which has
     963              :  * performed checksum operations exits. A launcher process which is exiting due
     964              :  * to a duplicate started launcher does not need to perform any cleanup and
     965              :  * this function should not be called. Otherwise, we need to clean up the abort
     966              :  * flag to ensure that processing can be started again if it was previously
     967              :  * aborted (note: started again, *not* restarted from where it left off).
     968              :  */
     969              : static void
     970           14 : launcher_exit(int code, Datum arg)
     971              : {
     972           14 :     abort_requested = false;
     973              : 
     974           14 :     if (launcher_running)
     975              :     {
     976            2 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     977            2 :         if (DataChecksumState->worker_pid != InvalidPid)
     978              :         {
     979            1 :             ereport(LOG,
     980              :                     errmsg("data checksums launcher exiting while worker is still running, signalling worker"));
     981            1 :             kill(DataChecksumState->worker_pid, SIGTERM);
     982            1 :             DataChecksumState->worker_pid = InvalidPid;
     983              :         }
     984            2 :         LWLockRelease(DataChecksumsWorkerLock);
     985              :     }
     986              : 
     987              :     /*
     988              :      * If the launcher is exiting before data checksums are enabled then set
     989              :      * the state to off since processing cannot be resumed.
     990              :      */
     991           14 :     if (DataChecksumsInProgressOn())
     992            1 :         SetDataChecksumsOff();
     993              : 
     994           14 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     995           14 :     launcher_running = false;
     996           14 :     DataChecksumState->launcher_running = false;
     997           14 :     LWLockRelease(DataChecksumsWorkerLock);
     998           14 : }
     999              : 
    1000              : /*
    1001              :  * launcher_cancel_handler
    1002              :  *
    1003              :  * Internal routine for reacting to SIGINT and flagging the worker to abort.
    1004              :  * The worker won't be interrupted immediately but will check for abort flag
    1005              :  * between each block in a relation.
    1006              :  */
    1007              : static void
    1008            0 : launcher_cancel_handler(SIGNAL_ARGS)
    1009              : {
    1010            0 :     int         save_errno = errno;
    1011              : 
    1012            0 :     abort_requested = true;
    1013              : 
    1014              :     /*
    1015              :      * There is no sleeping in the main loop, the flag will be checked
    1016              :      * periodically in ProcessSingleRelationFork. The worker does however
    1017              :      * sleep when waiting for concurrent transactions to end so we still need
    1018              :      * to set the latch.
    1019              :      */
    1020            0 :     SetLatch(MyLatch);
    1021              : 
    1022            0 :     errno = save_errno;
    1023            0 : }
    1024              : 
    1025              : /*
    1026              :  * WaitForAllTransactionsToFinish
    1027              :  *      Blocks awaiting all current transactions to finish
    1028              :  *
    1029              :  * Returns when all transactions which are active at the call of the function
    1030              :  * have ended.
    1031              :  *
    1032              :  * NB: this will return early, if aborted by SIGINT or if the target state
    1033              :  * is changed while we're running.
    1034              :  */
    1035              : static void
    1036            9 : WaitForAllTransactionsToFinish(void)
    1037              : {
    1038              :     TransactionId waitforxid;
    1039              : 
    1040            9 :     LWLockAcquire(XidGenLock, LW_SHARED);
    1041            9 :     waitforxid = XidFromFullTransactionId(TransamVariables->nextXid);
    1042            9 :     LWLockRelease(XidGenLock);
    1043              : 
    1044            9 :     while (TransactionIdPrecedes(GetOldestActiveTransactionId(false, true), waitforxid))
    1045              :     {
    1046              :         char        activity[64];
    1047              :         int         rc;
    1048              : 
    1049              :         /* Oldest running xid is older than us, so wait */
    1050            0 :         snprintf(activity,
    1051              :                  sizeof(activity),
    1052              :                  "Waiting for transactions older than %u to end",
    1053              :                  waitforxid);
    1054            0 :         pgstat_report_activity(STATE_RUNNING, activity);
    1055              : 
    1056              :         /* Retry every 3 seconds */
    1057            0 :         ResetLatch(MyLatch);
    1058            0 :         rc = WaitLatch(MyLatch,
    1059              :                        WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
    1060              :                        3000,
    1061              :                        WAIT_EVENT_CHECKSUM_ENABLE_STARTCONDITION);
    1062              : 
    1063              :         /*
    1064              :          * If the postmaster died, bail out.  But first print a log message to
    1065              :          * note that the checksumming didn't complete.
    1066              :          */
    1067            0 :         if (rc & WL_POSTMASTER_DEATH)
    1068            0 :             ereport(FATAL,
    1069              :                     errcode(ERRCODE_ADMIN_SHUTDOWN),
    1070              :                     errmsg("postmaster exited during data checksums processing"),
    1071              :                     errhint("Data checksums processing must be restarted manually after cluster restart."));
    1072              : 
    1073            0 :         CHECK_FOR_INTERRUPTS();
    1074            0 :         CHECK_FOR_LAUNCHER_ABORT_REQUEST();
    1075              : 
    1076            0 :         if (abort_requested)
    1077            0 :             break;
    1078              :     }
    1079              : 
    1080            9 :     pgstat_report_activity(STATE_IDLE, NULL);
    1081            9 :     return;
    1082              : }
    1083              : 
    1084              : /*
    1085              :  * DataChecksumsWorkerLauncherMain
    1086              :  *
    1087              :  * Main function for launching dynamic background workers for processing data
    1088              :  * checksums in databases. This function has the bgworker management, with
    1089              :  * ProcessAllDatabases being responsible for looping over the databases and
    1090              :  * initiating processing.
    1091              :  */
    1092              : void
    1093           14 : DataChecksumsWorkerLauncherMain(Datum arg)
    1094              : {
    1095              : 
    1096           14 :     ereport(DEBUG1,
    1097              :             errmsg("background worker \"datachecksums launcher\" started"));
    1098              : 
    1099           14 :     pqsignal(SIGTERM, die);
    1100           14 :     pqsignal(SIGINT, launcher_cancel_handler);
    1101           14 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    1102           14 :     pqsignal(SIGUSR2, PG_SIG_IGN);
    1103              : 
    1104           14 :     BackgroundWorkerUnblockSignals();
    1105              : 
    1106           14 :     MyBackendType = B_DATACHECKSUMSWORKER_LAUNCHER;
    1107           14 :     init_ps_display(NULL);
    1108              : 
    1109           14 :     INJECTION_POINT("datachecksumsworker-launcher-delay", NULL);
    1110              : 
    1111           14 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1112              : 
    1113           14 :     if (DataChecksumState->launcher_running)
    1114              :     {
    1115            0 :         ereport(LOG,
    1116              :                 errmsg("background worker \"datachecksums launcher\" already running, exiting"));
    1117              :         /* Launcher was already running, let it finish */
    1118            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1119            0 :         return;
    1120              :     }
    1121              : 
    1122           14 :     on_shmem_exit(launcher_exit, 0);
    1123           14 :     launcher_running = true;
    1124              : 
    1125              :     /* Initialize a connection to shared catalogs only */
    1126           14 :     BackgroundWorkerInitializeConnectionByOid(InvalidOid, InvalidOid, 0);
    1127              : 
    1128           14 :     operation = DataChecksumState->launch_operation;
    1129           14 :     DataChecksumState->launcher_running = true;
    1130           14 :     DataChecksumState->operation = operation;
    1131           14 :     DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1132           14 :     DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1133           14 :     LWLockRelease(DataChecksumsWorkerLock);
    1134              : 
    1135              :     /*
    1136              :      * The target state can change while we are busy enabling/disabling
    1137              :      * checksums, if the user calls pg_disable/enable_data_checksums() before
    1138              :      * we are finished with the previous request. In that case, we will loop
    1139              :      * back here, to process the new request.
    1140              :      */
    1141           14 : again:
    1142              : 
    1143           14 :     pgstat_progress_start_command(PROGRESS_COMMAND_DATACHECKSUMS,
    1144              :                                   InvalidOid);
    1145              : 
    1146           14 :     if (operation == ENABLE_DATACHECKSUMS)
    1147              :     {
    1148              :         /*
    1149              :          * If we are asked to enable checksums in a cluster which already has
    1150              :          * checksums enabled, exit immediately as there is nothing more to do.
    1151              :          */
    1152            9 :         if (DataChecksumsNeedVerify())
    1153            0 :             goto done;
    1154              : 
    1155            9 :         ereport(LOG,
    1156              :                 errmsg("enabling data checksums requested, starting data checksum calculation"));
    1157              : 
    1158              :         /*
    1159              :          * Set the state to inprogress-on and wait on the procsignal barrier.
    1160              :          */
    1161            9 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1162              :                                      PROGRESS_DATACHECKSUMS_PHASE_ENABLING);
    1163            9 :         SetDataChecksumsOnInProgress();
    1164              : 
    1165              :         /*
    1166              :          * All backends are now in inprogress-on state and are writing data
    1167              :          * checksums.  Start processing all data at rest.
    1168              :          */
    1169            9 :         if (!ProcessAllDatabases())
    1170              :         {
    1171              :             /*
    1172              :              * If the target state changed during processing then it's not a
    1173              :              * failure, so restart processing instead.
    1174              :              */
    1175            0 :             CHECK_FOR_LAUNCHER_ABORT_REQUEST();
    1176            0 :             if (abort_requested)
    1177            0 :                 goto done;
    1178            0 :             ereport(ERROR,
    1179              :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
    1180              :                     errmsg("unable to enable data checksums in cluster"));
    1181              :         }
    1182              : 
    1183              :         /*
    1184              :          * Data checksums have been set on all pages, set the state to on in
    1185              :          * order to instruct backends to validate checksums on reading.
    1186              :          */
    1187            7 :         SetDataChecksumsOn();
    1188              : 
    1189            7 :         ereport(LOG,
    1190              :                 errmsg("data checksums are now enabled"));
    1191              :     }
    1192            5 :     else if (operation == DISABLE_DATACHECKSUMS)
    1193              :     {
    1194            5 :         ereport(LOG,
    1195              :                 errmsg("disabling data checksums requested"));
    1196              : 
    1197            5 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1198              :                                      PROGRESS_DATACHECKSUMS_PHASE_DISABLING);
    1199            5 :         SetDataChecksumsOff();
    1200            5 :         ereport(LOG,
    1201              :                 errmsg("data checksums are now disabled"));
    1202              :     }
    1203              :     else
    1204              :         Assert(false);
    1205              : 
    1206            0 : done:
    1207              : 
    1208              :     /*
    1209              :      * This state will only be displayed for a fleeting moment, but for the
    1210              :      * sake of correctness it is still added before ending the command.
    1211              :      */
    1212           12 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1213              :                                  PROGRESS_DATACHECKSUMS_PHASE_DONE);
    1214              : 
    1215              :     /*
    1216              :      * All done. But before we exit, check if the target state was changed
    1217              :      * while we were running. In that case we will have to start all over
    1218              :      * again.
    1219              :      */
    1220           12 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1221           12 :     if (DataChecksumState->launch_operation != operation)
    1222              :     {
    1223            0 :         DataChecksumState->operation = DataChecksumState->launch_operation;
    1224            0 :         operation = DataChecksumState->launch_operation;
    1225            0 :         DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1226            0 :         DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1227            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1228            0 :         goto again;
    1229              :     }
    1230              : 
    1231              :     /* Shut down progress reporting as we are done */
    1232           12 :     pgstat_progress_end_command();
    1233              : 
    1234           12 :     launcher_running = false;
    1235           12 :     DataChecksumState->launcher_running = false;
    1236           12 :     LWLockRelease(DataChecksumsWorkerLock);
    1237              : }
    1238              : 
    1239              : /*
    1240              :  * ProcessAllDatabases
    1241              :  *      Compute the list of all databases and process checksums in each
    1242              :  *
    1243              :  * This will generate a list of databases to process for enabling checksums.
    1244              :  * If a database encounters a failure then processing will end immediately and
    1245              :  * return an error.
    1246              :  */
    1247              : static bool
    1248            9 : ProcessAllDatabases(void)
    1249              : {
    1250              :     List       *DatabaseList;
    1251            9 :     int         cumulative_total = 0;
    1252              : 
    1253              :     /* Set up so first run processes shared catalogs, not once in every db */
    1254            9 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1255            9 :     DataChecksumState->process_shared_catalogs = true;
    1256            9 :     LWLockRelease(DataChecksumsWorkerLock);
    1257              : 
    1258              :     /* Get a list of all databases to process */
    1259            9 :     WaitForAllTransactionsToFinish();
    1260            9 :     DatabaseList = BuildDatabaseList();
    1261              : 
    1262              :     /*
    1263              :      * Update progress reporting with the total number of databases we need to
    1264              :      * process.  This number should not be changed during processing, the
    1265              :      * columns for processed databases is instead increased such that it can
    1266              :      * be compared against the total.
    1267              :      */
    1268              :     {
    1269            9 :         const int   index[] = {
    1270              :             PROGRESS_DATACHECKSUMS_DBS_TOTAL,
    1271              :             PROGRESS_DATACHECKSUMS_DBS_DONE,
    1272              :             PROGRESS_DATACHECKSUMS_RELS_TOTAL,
    1273              :             PROGRESS_DATACHECKSUMS_RELS_DONE,
    1274              :             PROGRESS_DATACHECKSUMS_BLOCKS_TOTAL,
    1275              :             PROGRESS_DATACHECKSUMS_BLOCKS_DONE,
    1276              :         };
    1277              : 
    1278              :         int64       vals[6];
    1279              : 
    1280            9 :         vals[0] = list_length(DatabaseList);
    1281            9 :         vals[1] = 0;
    1282              :         /* translated to NULL */
    1283            9 :         vals[2] = -1;
    1284            9 :         vals[3] = -1;
    1285            9 :         vals[4] = -1;
    1286            9 :         vals[5] = -1;
    1287              : 
    1288            9 :         pgstat_progress_update_multi_param(6, index, vals);
    1289              :     }
    1290              : 
    1291           37 :     foreach_ptr(DataChecksumsWorkerDatabase, db, DatabaseList)
    1292              :     {
    1293              :         DataChecksumsWorkerResult result;
    1294              : 
    1295           23 :         result = ProcessDatabase(db);
    1296              : 
    1297              : #ifdef USE_INJECTION_POINTS
    1298              :         /* Allow a test process to alter the result of the operation */
    1299           22 :         if (IS_INJECTION_POINT_ATTACHED("datachecksumsworker-fail-db-result"))
    1300              :         {
    1301            1 :             result = DATACHECKSUMSWORKER_FAILED;
    1302            1 :             INJECTION_POINT_CACHED("datachecksumsworker-fail-db-result",
    1303              :                                    db->dbname);
    1304              :         }
    1305              : #endif
    1306              : 
    1307           22 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_DBS_DONE,
    1308              :                                      ++cumulative_total);
    1309              : 
    1310           22 :         if (result == DATACHECKSUMSWORKER_FAILED)
    1311              :         {
    1312              :             /*
    1313              :              * Disable checksums on cluster, because we failed one of the
    1314              :              * databases and this is an all or nothing process.
    1315              :              */
    1316            1 :             SetDataChecksumsOff();
    1317            1 :             ereport(ERROR,
    1318              :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
    1319              :                     errmsg("data checksums failed to get enabled in all databases, aborting"),
    1320              :                     errhint("The server log might have more information on the cause of the error."));
    1321              :         }
    1322           21 :         else if (result == DATACHECKSUMSWORKER_ABORTED || abort_requested)
    1323              :         {
    1324              :             /* Abort flag set, so exit the whole process */
    1325            0 :             return false;
    1326              :         }
    1327              : 
    1328              :         /*
    1329              :          * When one database has completed, it will have done shared catalogs
    1330              :          * so we don't have to process them again.
    1331              :          */
    1332           21 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1333           21 :         DataChecksumState->process_shared_catalogs = false;
    1334           21 :         LWLockRelease(DataChecksumsWorkerLock);
    1335              :     }
    1336              : 
    1337            7 :     FreeDatabaseList(DatabaseList);
    1338              : 
    1339            7 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1340              :                                  PROGRESS_DATACHECKSUMS_PHASE_WAITING_BARRIER);
    1341            7 :     return true;
    1342              : }
    1343              : 
    1344              : /*
    1345              :  * DataChecksumsShmemRequest
    1346              :  *      Request datachecksumsworker-related shared memory
    1347              :  */
    1348              : static void
    1349         1247 : DataChecksumsShmemRequest(void *arg)
    1350              : {
    1351         1247 :     ShmemRequestStruct(.name = "DataChecksumsWorker Data",
    1352              :                        .size = sizeof(DataChecksumsStateStruct),
    1353              :                        .ptr = (void **) &DataChecksumState,
    1354              :         );
    1355         1247 : }
    1356              : 
    1357              : /*
    1358              :  * DatabaseExists
    1359              :  *
    1360              :  * Scans the system catalog to check if a database with the given Oid exists
    1361              :  * and returns true if it is found and valid, else false. Note, we cannot use
    1362              :  * database_is_invalid_oid here as it will ERROR out, and we want to gracefully
    1363              :  * handle errors.
    1364              :  */
    1365              : static bool
    1366            0 : DatabaseExists(Oid dboid)
    1367              : {
    1368              :     Relation    rel;
    1369              :     ScanKeyData skey;
    1370              :     SysScanDesc scan;
    1371              :     bool        found;
    1372              :     HeapTuple   tuple;
    1373              :     Form_pg_database pg_database_tuple;
    1374              : 
    1375            0 :     StartTransactionCommand();
    1376              : 
    1377            0 :     rel = table_open(DatabaseRelationId, AccessShareLock);
    1378            0 :     ScanKeyInit(&skey,
    1379              :                 Anum_pg_database_oid,
    1380              :                 BTEqualStrategyNumber, F_OIDEQ,
    1381              :                 ObjectIdGetDatum(dboid));
    1382            0 :     scan = systable_beginscan(rel, DatabaseOidIndexId, true, SnapshotSelf,
    1383              :                               1, &skey);
    1384            0 :     tuple = systable_getnext(scan);
    1385            0 :     found = HeapTupleIsValid(tuple);
    1386              : 
    1387              :     /* If the Oid exists, ensure that it's not partially dropped */
    1388            0 :     if (found)
    1389              :     {
    1390            0 :         pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
    1391            0 :         if (database_is_invalid_form(pg_database_tuple))
    1392            0 :             found = false;
    1393              :     }
    1394              : 
    1395            0 :     systable_endscan(scan);
    1396            0 :     table_close(rel, AccessShareLock);
    1397              : 
    1398            0 :     CommitTransactionCommand();
    1399              : 
    1400            0 :     return found;
    1401              : }
    1402              : 
    1403              : /*
    1404              :  * BuildDatabaseList
    1405              :  *      Compile a list of all currently available databases in the cluster
    1406              :  *
    1407              :  * This creates the list of databases for the datachecksumsworker workers to
    1408              :  * add checksums to. If the caller wants to ensure that no concurrently
    1409              :  * running CREATE DATABASE calls exist, this needs to be preceded by a call
    1410              :  * to WaitForAllTransactionsToFinish().
    1411              :  */
    1412              : static List *
    1413            9 : BuildDatabaseList(void)
    1414              : {
    1415            9 :     List       *DatabaseList = NIL;
    1416              :     Relation    rel;
    1417              :     TableScanDesc scan;
    1418              :     HeapTuple   tup;
    1419            9 :     MemoryContext ctx = CurrentMemoryContext;
    1420              :     MemoryContext oldctx;
    1421              : 
    1422            9 :     StartTransactionCommand();
    1423              : 
    1424            9 :     rel = table_open(DatabaseRelationId, AccessShareLock);
    1425            9 :     scan = table_beginscan_catalog(rel, 0, NULL);
    1426              : 
    1427           36 :     while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    1428              :     {
    1429           27 :         Form_pg_database pgdb = (Form_pg_database) GETSTRUCT(tup);
    1430              :         DataChecksumsWorkerDatabase *db;
    1431              : 
    1432           27 :         oldctx = MemoryContextSwitchTo(ctx);
    1433              : 
    1434           27 :         db = (DataChecksumsWorkerDatabase *) palloc0(sizeof(DataChecksumsWorkerDatabase));
    1435              : 
    1436           27 :         db->dboid = pgdb->oid;
    1437           27 :         db->dbname = pstrdup(NameStr(pgdb->datname));
    1438              : 
    1439           27 :         DatabaseList = lappend(DatabaseList, db);
    1440              : 
    1441           27 :         MemoryContextSwitchTo(oldctx);
    1442              :     }
    1443              : 
    1444            9 :     table_endscan(scan);
    1445            9 :     table_close(rel, AccessShareLock);
    1446              : 
    1447            9 :     CommitTransactionCommand();
    1448              : 
    1449            9 :     return DatabaseList;
    1450              : }
    1451              : 
    1452              : static void
    1453            7 : FreeDatabaseList(List *dblist)
    1454              : {
    1455            7 :     if (!dblist)
    1456            0 :         return;
    1457              : 
    1458           35 :     foreach_ptr(DataChecksumsWorkerDatabase, db, dblist)
    1459              :     {
    1460           21 :         if (db->dbname != NULL)
    1461           21 :             pfree(db->dbname);
    1462              :     }
    1463              : 
    1464            7 :     list_free_deep(dblist);
    1465              : }
    1466              : 
    1467              : /*
    1468              :  * BuildRelationList
    1469              :  *      Compile a list of relations in the database
    1470              :  *
    1471              :  * Returns a list of OIDs for the requested relation types. If temp_relations
    1472              :  * is True then only temporary relations are returned. If temp_relations is
    1473              :  * False then non-temporary relations which have data checksums are returned.
    1474              :  * If include_shared is True then shared relations are included as well in a
    1475              :  * non-temporary list. include_shared has no relevance when building a list of
    1476              :  * temporary relations.
    1477              :  */
    1478              : static List *
    1479           68 : BuildRelationList(bool temp_relations, bool include_shared)
    1480              : {
    1481           68 :     List       *RelationList = NIL;
    1482              :     Relation    rel;
    1483              :     TableScanDesc scan;
    1484              :     HeapTuple   tup;
    1485           68 :     MemoryContext ctx = CurrentMemoryContext;
    1486              :     MemoryContext oldctx;
    1487              : 
    1488           68 :     StartTransactionCommand();
    1489              : 
    1490           68 :     rel = table_open(RelationRelationId, AccessShareLock);
    1491           68 :     scan = table_beginscan_catalog(rel, 0, NULL);
    1492              : 
    1493        30847 :     while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    1494              :     {
    1495        30779 :         Form_pg_class pgc = (Form_pg_class) GETSTRUCT(tup);
    1496              : 
    1497              :         /* Only include temporary relations when explicitly asked to */
    1498        30779 :         if (pgc->relpersistence == RELPERSISTENCE_TEMP)
    1499              :         {
    1500            2 :             if (!temp_relations)
    1501            1 :                 continue;
    1502              :         }
    1503              :         else
    1504              :         {
    1505              :             /*
    1506              :              * If we are only interested in temp relations then continue
    1507              :              * immediately as the current relation isn't a temp relation.
    1508              :              */
    1509        30777 :             if (temp_relations)
    1510        20367 :                 continue;
    1511              : 
    1512        10410 :             if (!RELKIND_HAS_STORAGE(pgc->relkind))
    1513         3726 :                 continue;
    1514              : 
    1515         6684 :             if (pgc->relisshared && !include_shared)
    1516          644 :                 continue;
    1517              :         }
    1518              : 
    1519         6041 :         oldctx = MemoryContextSwitchTo(ctx);
    1520         6041 :         RelationList = lappend_oid(RelationList, pgc->oid);
    1521         6041 :         MemoryContextSwitchTo(oldctx);
    1522              :     }
    1523              : 
    1524           68 :     table_endscan(scan);
    1525           68 :     table_close(rel, AccessShareLock);
    1526              : 
    1527           68 :     CommitTransactionCommand();
    1528              : 
    1529           68 :     return RelationList;
    1530              : }
    1531              : 
    1532              : /*
    1533              :  * DataChecksumsWorkerMain
    1534              :  *
    1535              :  * Main function for enabling checksums in a single database. This is the
    1536              :  * function set as the bgw_function_name in the dynamic background worker
    1537              :  * process initiated for each database by the worker launcher. After enabling
    1538              :  * data checksums in each applicable relation in the database, it will wait for
    1539              :  * all temporary relations that were present when the function started to
    1540              :  * disappear before returning. This is required since we cannot rewrite
    1541              :  * existing temporary relations with data checksums.
    1542              :  */
    1543              : void
    1544           23 : DataChecksumsWorkerMain(Datum arg)
    1545              : {
    1546              :     Oid         dboid;
    1547           23 :     List       *RelationList = NIL;
    1548           23 :     List       *InitialTempTableList = NIL;
    1549              :     BufferAccessStrategy strategy;
    1550           23 :     bool        aborted = false;
    1551              :     int64       rels_done;
    1552              :     bool        process_shared;
    1553              : #ifdef USE_INJECTION_POINTS
    1554           23 :     bool        retried = false;
    1555              : #endif
    1556              : 
    1557           23 :     worker_invocation = DatumGetUInt64(arg);
    1558              : 
    1559           23 :     operation = ENABLE_DATACHECKSUMS;
    1560              : 
    1561           23 :     pqsignal(SIGTERM, die);
    1562           23 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    1563              : 
    1564           23 :     BackgroundWorkerUnblockSignals();
    1565              : 
    1566           23 :     MyBackendType = B_DATACHECKSUMSWORKER_WORKER;
    1567           23 :     init_ps_display(NULL);
    1568              : 
    1569           23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
    1570           23 :     if (DataChecksumState->worker_invocation != worker_invocation)
    1571              :     {
    1572            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1573            0 :         return;
    1574              :     }
    1575           23 :     dboid = DataChecksumState->database_oid;
    1576           23 :     LWLockRelease(DataChecksumsWorkerLock);
    1577              : 
    1578           23 :     BackgroundWorkerInitializeConnectionByOid(dboid, InvalidOid,
    1579              :                                               BGWORKER_BYPASS_ALLOWCONN);
    1580              : 
    1581              :     /* worker will have a separate entry in pg_stat_progress_data_checksums */
    1582           23 :     pgstat_progress_start_command(PROGRESS_COMMAND_DATACHECKSUMS,
    1583              :                                   InvalidOid);
    1584              : 
    1585              :     /*
    1586              :      * Get a list of all temp tables present as we start in this database. We
    1587              :      * need to wait until they are all gone before we exit.  For the list of
    1588              :      * relations to enable checksums in, check if shared catalogs have been
    1589              :      * processed already.
    1590              :      */
    1591           23 :     InitialTempTableList = BuildRelationList(true, false);
    1592           23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1593           23 :     if (DataChecksumState->worker_invocation != worker_invocation)
    1594              :     {
    1595            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1596            0 :         return;
    1597              :     }
    1598           23 :     process_shared = DataChecksumState->process_shared_catalogs;
    1599              : 
    1600              :     /*
    1601              :      * Enable vacuum cost delay, if any.  While this process isn't doing any
    1602              :      * vacuuming, we are re-using the infrastructure that vacuum cost delay
    1603              :      * provides rather than inventing something bespoke. This is an internal
    1604              :      * implementation detail and care should be taken to avoid it bleeding
    1605              :      * through to the user to avoid confusion.
    1606              :      *
    1607              :      * VacuumUpdateCosts() propagates the values to the variables actually
    1608              :      * read by vacuum_delay_point().
    1609              :      */
    1610           23 :     VacuumCostDelay = DataChecksumState->cost_delay;
    1611           23 :     VacuumCostLimit = DataChecksumState->cost_limit;
    1612           23 :     LWLockRelease(DataChecksumsWorkerLock);
    1613           23 :     VacuumUpdateCosts();
    1614           23 :     VacuumCostBalance = 0;
    1615              : 
    1616              :     /*
    1617              :      * Create and set the vacuum strategy as our buffer strategy.
    1618              :      */
    1619           23 :     strategy = GetAccessStrategy(BAS_VACUUM);
    1620              : 
    1621           23 :     RelationList = BuildRelationList(false, process_shared);
    1622              : 
    1623              :     /* Update the total number of relations to be processed in this DB. */
    1624              :     {
    1625           23 :         const int   index[] = {
    1626              :             PROGRESS_DATACHECKSUMS_RELS_TOTAL,
    1627              :             PROGRESS_DATACHECKSUMS_RELS_DONE
    1628              :         };
    1629              : 
    1630              :         int64       vals[2];
    1631              : 
    1632           23 :         vals[0] = list_length(RelationList);
    1633           23 :         vals[1] = 0;
    1634              : 
    1635           23 :         pgstat_progress_update_multi_param(2, index, vals);
    1636              :     }
    1637              : 
    1638              :     /* Process the relations */
    1639           23 :     rels_done = 0;
    1640         6012 :     foreach_oid(reloid, RelationList)
    1641              :     {
    1642         5968 :         bool        costs_updated = false;
    1643              : 
    1644         5968 :         if (!ProcessSingleRelationByOid(reloid, strategy))
    1645              :         {
    1646            0 :             aborted = true;
    1647            0 :             break;
    1648              :         }
    1649              : 
    1650         5967 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_RELS_DONE,
    1651              :                                      ++rels_done);
    1652         5967 :         CHECK_FOR_INTERRUPTS();
    1653         5967 :         CHECK_FOR_WORKER_ABORT_REQUEST();
    1654              : 
    1655         5967 :         if (abort_requested)
    1656            0 :             break;
    1657              : 
    1658              :         /*
    1659              :          * Check if the cost settings changed during runtime and if so, update
    1660              :          * to reflect the new values and signal that the access strategy needs
    1661              :          * to be refreshed.
    1662              :          */
    1663         5967 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1664         5967 :         if (DataChecksumState->worker_invocation != worker_invocation)
    1665              :         {
    1666            0 :             LWLockRelease(DataChecksumsWorkerLock);
    1667            0 :             break;
    1668              :         }
    1669         5967 :         if ((DataChecksumState->launch_cost_delay != DataChecksumState->cost_delay)
    1670         5967 :             || (DataChecksumState->launch_cost_limit != DataChecksumState->cost_limit))
    1671              :         {
    1672            0 :             costs_updated = true;
    1673            0 :             VacuumCostDelay = DataChecksumState->launch_cost_delay;
    1674            0 :             VacuumCostLimit = DataChecksumState->launch_cost_limit;
    1675            0 :             VacuumUpdateCosts();
    1676              : 
    1677            0 :             DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1678            0 :             DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1679              :         }
    1680              :         else
    1681         5967 :             costs_updated = false;
    1682         5967 :         LWLockRelease(DataChecksumsWorkerLock);
    1683              : 
    1684         5967 :         if (costs_updated)
    1685              :         {
    1686            0 :             FreeAccessStrategy(strategy);
    1687            0 :             strategy = GetAccessStrategy(BAS_VACUUM);
    1688              :         }
    1689              :     }
    1690              : 
    1691           22 :     list_free(RelationList);
    1692           22 :     FreeAccessStrategy(strategy);
    1693              : 
    1694           22 :     if (aborted || abort_requested)
    1695              :     {
    1696            0 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1697            0 :         if (DataChecksumState->worker_invocation == worker_invocation)
    1698            0 :             DataChecksumState->worker_result = DATACHECKSUMSWORKER_ABORTED;
    1699            0 :         LWLockRelease(DataChecksumsWorkerLock);
    1700            0 :         ereport(DEBUG1,
    1701              :                 errmsg("data checksum processing aborted in database OID %u",
    1702              :                        dboid));
    1703            0 :         return;
    1704              :     }
    1705              : 
    1706              :     /* The worker is about to wait for temporary tables to go away. */
    1707           22 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1708              :                                  PROGRESS_DATACHECKSUMS_PHASE_WAITING_TEMPREL);
    1709              : 
    1710              :     /*
    1711              :      * Wait for all temp tables that existed when we started to go away. This
    1712              :      * is necessary since we cannot "reach" them to enable checksums. Any temp
    1713              :      * tables created after we started will already have checksums in them
    1714              :      * (due to the "inprogress-on" state), so no need to wait for those.
    1715              :      */
    1716              :     for (;;)
    1717            0 :     {
    1718              :         List       *CurrentTempTables;
    1719              :         int         numleft;
    1720              :         char        activity[64];
    1721              : 
    1722           22 :         CurrentTempTables = BuildRelationList(true, false);
    1723           22 :         numleft = 0;
    1724           44 :         foreach_oid(tmptbloid, InitialTempTableList)
    1725              :         {
    1726            0 :             if (list_member_oid(CurrentTempTables, tmptbloid))
    1727            0 :                 numleft++;
    1728              :         }
    1729           22 :         list_free(CurrentTempTables);
    1730              : 
    1731              : #ifdef USE_INJECTION_POINTS
    1732           22 :         if (IS_INJECTION_POINT_ATTACHED("datachecksumsworker-fake-temptable-wait"))
    1733              :         {
    1734              :             /* Make sure to just cause one retry */
    1735            0 :             if (!retried && numleft == 0)
    1736              :             {
    1737            0 :                 numleft = 1;
    1738            0 :                 retried = true;
    1739              : 
    1740            0 :                 INJECTION_POINT_CACHED("datachecksumsworker-fake-temptable-wait", NULL);
    1741              :             }
    1742              :         }
    1743              : #endif
    1744              : 
    1745           22 :         if (numleft == 0)
    1746           22 :             break;
    1747              : 
    1748              :         /*
    1749              :          * At least one temp table is left to wait for, indicate in pgstat
    1750              :          * activity and progress reporting.
    1751              :          */
    1752            0 :         snprintf(activity,
    1753              :                  sizeof(activity),
    1754              :                  "Waiting for %d temp tables to be removed", numleft);
    1755            0 :         pgstat_report_activity(STATE_RUNNING, activity);
    1756              : 
    1757              :         /* Retry every 3 seconds */
    1758            0 :         ResetLatch(MyLatch);
    1759            0 :         (void) WaitLatch(MyLatch,
    1760              :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
    1761              :                          3000,
    1762              :                          WAIT_EVENT_CHECKSUM_ENABLE_TEMPTABLE_WAIT);
    1763              : 
    1764            0 :         CHECK_FOR_INTERRUPTS();
    1765            0 :         CHECK_FOR_WORKER_ABORT_REQUEST();
    1766              : 
    1767            0 :         if (aborted || abort_requested)
    1768              :         {
    1769            0 :             LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1770            0 :             if (DataChecksumState->worker_invocation == worker_invocation)
    1771            0 :                 DataChecksumState->worker_result = DATACHECKSUMSWORKER_ABORTED;
    1772            0 :             LWLockRelease(DataChecksumsWorkerLock);
    1773            0 :             ereport(LOG,
    1774              :                     errmsg("data checksum processing aborted in database OID %u",
    1775              :                            dboid));
    1776            0 :             return;
    1777              :         }
    1778              :     }
    1779              : 
    1780           22 :     list_free(InitialTempTableList);
    1781              : 
    1782              :     /* worker done */
    1783           22 :     pgstat_progress_end_command();
    1784              : 
    1785           22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1786           22 :     if (DataChecksumState->worker_invocation == worker_invocation)
    1787           22 :         DataChecksumState->worker_result = DATACHECKSUMSWORKER_SUCCESSFUL;
    1788           22 :     LWLockRelease(DataChecksumsWorkerLock);
    1789              : }
        

Generated by: LCOV version 2.0-1