diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c index 51acc09aa7..f97094201b 100644 --- a/contrib/pg_prewarm/autoprewarm.c +++ b/contrib/pg_prewarm/autoprewarm.c @@ -1,26 +1,27 @@ /*------------------------------------------------------------------------- * * autoprewarm.c - * Automatically prewarm the shared buffer pool when server restarts. + * Automatically prewarms the shared buffer pool when server restarts. * * DESCRIPTION * - * It is a bgworker which automatically records information about blocks - * which were present in buffer pool before server shutdown and then - * prewarm the buffer pool upon server restart with those blocks. + * Autoprewarm is a bgworker process that automatically records the + * information about blocks which were present in buffer pool before + * server shutdown. Then prewarms the buffer pool on server restart + * with those blocks. * * How does it work? When the shared library "pg_prewarm" is preloaded, a * bgworker "autoprewarm" is launched immediately after the server has - * reached consistent state. The bgworker will start loading blocks - * recorded in the format BlockInfoRecord - * database,tablespace,filenode,forknum,blocknum in + * reached a consistent state. The bgworker will start loading blocks + * recorded in the format BlockInfoRecord consisting of + * database, tablespace, filenode, forknum, blocknum in * $PGDATA/AUTOPREWARM_FILE, until there is no free buffer left in the * buffer pool. This way we do not replace any new blocks which were * loaded either by the recovery process or the querying clients. * * Once the "autoprewarm" bgworker has completed its prewarm task, it will * start a new task to periodically dump the BlockInfoRecords related to - * blocks which are currently in shared buffer pool. Upon next server + * the blocks which are currently in shared buffer pool. On next server * restart, the bgworker will prewarm the buffer pool by loading those * blocks. The GUC pg_prewarm.dump_interval will control the dumping * activity of the bgworker. @@ -89,14 +90,13 @@ static void apw_sigterm_handler(SIGNAL_ARGS); static void apw_sighup_handler(SIGNAL_ARGS); static void apw_sigusr1_handler(SIGNAL_ARGS); -/* flags set by signal handlers */ +/* Flags set by signal handlers */ static volatile sig_atomic_t got_sigterm = false; static volatile sig_atomic_t got_sighup = false; /* - * Signal handler for SIGTERM - * Set a flag to let the main loop to terminate, and set our latch to wake it - * up. + * Signal handler for SIGTERM + * Set a flag to handle. */ static void apw_sigterm_handler(SIGNAL_ARGS) @@ -113,8 +113,7 @@ apw_sigterm_handler(SIGNAL_ARGS) /* * Signal handler for SIGHUP - * Set a flag to tell the process to reread the config file, and set our - * latch to wake it up. + * Set a flag to reread the config file. */ static void apw_sighup_handler(SIGNAL_ARGS) @@ -131,8 +130,7 @@ apw_sighup_handler(SIGNAL_ARGS) /* * Signal handler for SIGUSR1. - * The prewarm per-database workers will notify with SIGUSR1 on their - * startup/shutdown. + * The prewarm workers notify with SIGUSR1 on their startup/shutdown. */ static void apw_sigusr1_handler(SIGNAL_ARGS) @@ -146,13 +144,11 @@ apw_sigusr1_handler(SIGNAL_ARGS) } /* ============================================================================ - * ============== types and variables used by autoprewarm ============= + * ============== Types and variables used by autoprewarm ============= * ============================================================================ */ -/* - * Metadata of each persistent block which is dumped and used to load. - */ +/* Metadata of each persistent block which is dumped and used for loading. */ typedef struct BlockInfoRecord { Oid database; @@ -162,18 +158,14 @@ typedef struct BlockInfoRecord BlockNumber blocknum; } BlockInfoRecord; -/* - * Tasks performed by autoprewarm workers. - */ +/* Tasks performed by autoprewarm workers.*/ typedef enum { TASK_PREWARM_BUFFERPOOL, /* prewarm the buffer pool. */ TASK_DUMP_BUFFERPOOL_INFO /* dump the buffer pool block info. */ } AutoPrewarmTask; -/* - * Shared state information about the running autoprewarm bgworker. - */ +/* Shared state information for autoprewarm bgworker. */ typedef struct AutoPrewarmSharedState { LWLock lock; /* mutual exclusion */ @@ -182,7 +174,7 @@ typedef struct AutoPrewarmSharedState bool skip_prewarm_on_restart; /* if set true, prewarm task * will not be done */ - /* following items are for communication with per-database worker */ + /* Following items are for communication with per-database worker */ dsm_handle block_info_handle; Oid database; int prewarm_start_idx; @@ -191,16 +183,16 @@ typedef struct AutoPrewarmSharedState static AutoPrewarmSharedState *state = NULL; -/* GUC variable which control the dump activity of autoprewarm. */ +/* GUC variable that controls the dump activity of autoprewarm. */ static int dump_interval = 0; /* - * GUC variable which say whether autoprewarm worker has to be started when + * GUC variable to decide if autoprewarm worker has to be started when * preloaded. */ static bool autoprewarm = true; -/* compare member elements to check if they are not equal. */ +/* Compare member elements to check if they are not equal. */ #define cmp_member_elem(fld) \ do { \ if (a->fld < b->fld) \ @@ -228,7 +220,7 @@ blockinfo_cmp(const void *p, const void *q) } /* ============================================================================ - * ===================== prewarm part of autoprewarm ======================= + * ===================== Prewarm part of autoprewarm ======================= * ============================================================================ */ @@ -248,7 +240,7 @@ reset_shm_state(int code, Datum arg) /* * init_autoprewarm_state - * Allocate and initialize autoprewarm related shared memory + * Allocate and initialize autoprewarm related shared memory. */ static void init_autoprewarm_state(void) @@ -273,11 +265,10 @@ init_autoprewarm_state(void) /* * load_one_database - * Load block infos of one database by connecting to them. + * This sub-routine loads the BlockInfoRecords of the database set in AutoPrewarmSharedState. * - * Start of prewarm per-database worker. This will try to load blocks of one - * database starting from block info position state->prewarm_start_idx to - * state->prewarm_stop_idx. + * Connect to the database and load the blocks of that database starting from + * the position state->prewarm_start_idx to state->prewarm_stop_idx. */ void load_one_database(Datum main_arg) @@ -293,9 +284,7 @@ load_one_database(Datum main_arg) pqsignal(SIGTERM, apw_sigterm_handler); pqsignal(SIGHUP, apw_sighup_handler); - /* - * We're now ready to receive signals - */ + /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); init_autoprewarm_state(); @@ -317,18 +306,17 @@ load_one_database(Datum main_arg) Buffer buf; /* - * Quit if we've reached records for another database. Unless the - * previous blocks were of global objects which were combined with - * next database's block infos. + * Quit if we've reached records for another database. If previous + * blocks are of some global objects, then continue pre-warming. */ if (old_blk != NULL && old_blk->database != blk->database && old_blk->database != 0) break; /* - * When we reach a new relation, close the old one. Note, however, - * that the previous try_relation_open may have failed, in which case - * rel will be NULL. + * As soon as we encounter a block of a new relation, close the old + * relation. Note, that rel will be NULL if try_relation_open failed + * previously, in that case there is nothing to close. */ if (old_blk != NULL && old_blk->filenode != blk->filenode && rel != NULL) @@ -340,7 +328,7 @@ load_one_database(Datum main_arg) /* * Try to open each new relation, but only once, when we first - * encounter it. If it's been dropped, skip the associated blocks. + * encounter it. If it's been dropped, skip the associated blocks. */ if (old_blk == NULL || old_blk->filenode != blk->filenode) { @@ -370,8 +358,8 @@ load_one_database(Datum main_arg) RelationOpenSmgr(rel); /* - * smgrexists is not safe for illegal forknum, so test before - * calling same. + * smgrexists is not safe for illegal forknum, hence check if the + * passed forknum is valid before using it in smgrexists. */ if (blk->forknum > InvalidForkNumber && blk->forknum <= MAX_FORKNUM && @@ -381,10 +369,10 @@ load_one_database(Datum main_arg) nblocks = 0; } - /* check if blocknum is valid and with in fork file size. */ + /* Check if blocknum is valid and within fork file size. */ if (blk->blocknum >= nblocks) { - /* move to next forknum. */ + /* Move to next forknum. */ ++pos; old_blk = blk; continue; @@ -402,7 +390,7 @@ load_one_database(Datum main_arg) dsm_detach(seg); - /* release lock on previous relation. */ + /* Release lock on previous relation. */ if (rel) { relation_close(rel, AccessShareLock); @@ -427,7 +415,7 @@ launch_and_wait_for_per_database_worker(void) (Datum) NULL, BGW_NEVER_RESTART, BGWORKER_BACKEND_DATABASE_CONNECTION); - /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerShutdown */ + /* Set bgw_notify_pid so that we can use WaitForBackgroundWorkerShutdown */ worker.bgw_notify_pid = MyProcPid; if (!RegisterDynamicBackgroundWorker(&worker, &handle)) @@ -444,14 +432,14 @@ launch_and_wait_for_per_database_worker(void) /* * prewarm_buffer_pool - * The main routine which prewarm the buffer pool + * The main routine that prewarms the buffer pool. * - * The prewarm bgworker will first load all of the BlockInfoRecord's in - * $PGDATA/AUTOPREWARM_FILE to a dsm. And those BlockInfoRecords are further - * separated based on their database. And for each group of BlockInfoRecords a - * per-database worker will be launched to load corresponding blocks. Each of - * those workers will be launched in sequential order only after the previous - * one has finished its job. + * The prewarm bgworker will first load all the BlockInfoRecords in + * $PGDATA/AUTOPREWARM_FILE to a dsm. Further, these BlockInfoRecords are + * separated based on their databases. Finally, for each group of + * BlockInfoRecords a per-database worker will be launched to load the + * corresponding blocks. Launch the next worker only after the previous one has + * finished its job. */ static void prewarm_buffer_pool(void) @@ -463,8 +451,8 @@ prewarm_buffer_pool(void) dsm_segment *seg; /* - * since there could be at max one worker who could do a prewarm no need - * to take lock before setting skip_prewarm_on_restart. + * Since there could be at most one worker for prewarm, locking is not + * required for setting skip_prewarm_on_restart. */ state->skip_prewarm_on_restart = true; @@ -509,7 +497,7 @@ prewarm_buffer_pool(void) for (i = 0; i < num_elements; i++) { - /* get next block. */ + /* Get next block. */ if (5 != fscanf(file, "%u,%u,%u,%u,%u\n", &blkinfo[i].database, &blkinfo[i].tablespace, &blkinfo[i].filenode, (uint32 *) &blkinfo[i].forknum, &blkinfo[i].blocknum)) @@ -523,7 +511,7 @@ prewarm_buffer_pool(void) i, num_elements); /* - * sort the block number to increase the chance of sequential reads during + * Sort the block number to increase the chance of sequential reads during * load. */ pg_qsort(blkinfo, num_elements, sizeof(BlockInfoRecord), blockinfo_cmp); @@ -531,15 +519,15 @@ prewarm_buffer_pool(void) state->block_info_handle = dsm_segment_handle(seg); state->prewarm_start_idx = state->prewarm_stop_idx = 0; - /* get next database's first block info's position. */ + /* Get the info position of the first block of the next database. */ while (state->prewarm_start_idx < num_elements) { uint32 i = state->prewarm_start_idx; Oid current_db = blkinfo[i].database; /* - * advance the prewarm_stop_idx to end of block infos of current - * database. + * Advance the prewarm_stop_idx to the first BlockRecordInfo that does + * not belong to this database. */ do { @@ -547,9 +535,8 @@ prewarm_buffer_pool(void) if (current_db != blkinfo[i].database) { /* - * For block info of a global object whose database will be 0 - * try to combine them with next non-zero database's block - * infos to load. + * For the BlockRecordInfos of a global object, combine them + * with BlockRecordInfos of the next non-global object. */ if (current_db != InvalidOid) break; @@ -558,9 +545,9 @@ prewarm_buffer_pool(void) } while (i < num_elements); /* - * If we are here with database as InvalidOid it means we only have - * block_infos belonging to global objects. As we do not have a valid - * database to connect we shall simply ignore them. + * If we are here with database having InvalidOid, then only + * BlockRecordInfos belonging to global objects exist. Since, we can + * not connect with InvalidOid skip prewarming for these objects. */ if (current_db == 0) break; @@ -571,8 +558,8 @@ prewarm_buffer_pool(void) Assert(state->prewarm_start_idx < state->prewarm_stop_idx); /* - * Register a per-database worker to load new database's block. And - * wait until they finish their job to launch next one. + * Register a per-database worker to load blocks of the database. Wait + * until it has finished before starting the next worker. */ launch_and_wait_for_per_database_worker(); state->prewarm_start_idx = state->prewarm_stop_idx; @@ -587,22 +574,23 @@ prewarm_buffer_pool(void) return; } -/* ============================================================================ - * ============= buffer pool info dump part of autoprewarm =============== +/* + * ============================================================================ + * ===================== Dump part of Autoprewarm ============================= * ============================================================================ */ -/* This sub-module is for periodically dumping buffer pool's block info into - * a dump file AUTOPREWARM_FILE. - * Each entry of block info looks like this: - * database,tablespace,filenode,forknum,blocknum and we shall call it as - * BlockInfoRecord. Note we write in the text form so that the dump information - * is readable and if necessary can be carefully edited. +/* + * This sub-module is for periodically dumping BlockRecordInfos in buffer pool + * into a dump file AUTOPREWARM_FILE. + * Each entry of BlockRecordInfo consists of database, tablespace, filenode, + * forknum, blocknum. Note that this is in the text form so that the dump + * information is readable and can be edited, if required. */ /* * dump_now - * Dumps block infos in buffer pool + * Dumps BlockRecordInfos in buffer pool. */ static uint32 dump_now(bool is_bgworker) @@ -643,9 +631,7 @@ dump_now(bool is_bgworker) { uint32 buf_state; - /* - * In case of a SIGHUP, just reload the configuration. - */ + /* In case of a SIGHUP, just reload the configuration. */ if (got_sighup) { got_sighup = false; @@ -661,7 +647,7 @@ dump_now(bool is_bgworker) bufHdr = GetBufferDescriptor(i); - /* lock each buffer header before inspecting. */ + /* Lock each buffer header before inspecting. */ buf_state = LockBufHdr(bufHdr); if (buf_state & BM_TAG_VALID) @@ -696,9 +682,7 @@ dump_now(bool is_bgworker) for (i = 0; i < num_blocks; i++) { - /* - * In case of a SIGHUP, just reload the configuration. - */ + /* In case of a SIGHUP, just reload the configuration. */ if (got_sighup) { got_sighup = false; @@ -738,7 +722,7 @@ dump_now(bool is_bgworker) pfree(block_info_array); /* - * rename transient_dump_file_path to AUTOPREWARM_FILE to make things + * Rename transient_dump_file_path to AUTOPREWARM_FILE to make things * permanent. */ ret = CloseTransientFile(fd); @@ -758,10 +742,9 @@ dump_now(bool is_bgworker) /* * dump_block_info_periodically - * Loop which periodically calls dump_now() + * Sub-routine to periodically call dump_now(). * - * At regular intervals, which is defined by GUC dump_interval, dump_now() will - * be called. + * Call dum_now() at regular intervals defined by GUC variable dump_interval. */ void dump_block_info_periodically(void) @@ -776,7 +759,7 @@ dump_block_info_periodically(void) nap.tv_sec = AT_PWARM_DEFAULT_DUMP_INTERVAL; nap.tv_usec = 0; - /* Has been set not to dump. Nothing more to do. */ + /* Have we been asked to stop dumping? */ if (dump_interval == AT_PWARM_OFF) return; @@ -789,9 +772,13 @@ dump_block_info_periodically(void) (dump_interval * 1000))) { dump_now(true); + + /* + * It is better to stop when shutdown signal is received + * during or right after a dump. + */ if (got_sigterm) - return; /* got shutdown signal during or right after a - * dump. And, I think better to return now. */ + return; last_dump_time = GetCurrentTimestamp(); nap.tv_sec = dump_interval; nap.tv_usec = 0; @@ -817,9 +804,7 @@ dump_block_info_periodically(void) if (rc & WL_POSTMASTER_DEATH) proc_exit(1); - /* - * In case of a SIGHUP, just reload the configuration. - */ + /* In case of a SIGHUP, just reload the configuration. */ if (got_sighup) { got_sighup = false; @@ -827,14 +812,14 @@ dump_block_info_periodically(void) } } - /* One last block info dump while postmaster shutdown. */ + /* It's time for postmaster shutdown, let's dump for one last time. */ if (dump_interval != AT_PWARM_OFF) dump_now(true); } /* * autoprewarm_main - * The main entry point of autoprewarm bgworker process + * The main entry point of autoprewarm bgworker process. */ void autoprewarm_main(Datum main_arg) @@ -846,7 +831,7 @@ autoprewarm_main(Datum main_arg) pqsignal(SIGHUP, apw_sighup_handler); pqsignal(SIGUSR1, apw_sigusr1_handler); - /* We're now ready to receive signals */ + /* We're now ready to receive signals. */ BackgroundWorkerUnblockSignals(); todo_task = DatumGetInt32(main_arg); @@ -859,7 +844,7 @@ autoprewarm_main(Datum main_arg) { LWLockRelease(&state->lock); ereport(LOG, - (errmsg("could not continue autoprewarm worker is already running under PID %d", + (errmsg("autoprewarm worker is already running under PID %d", state->bgworker_pid))); return; } @@ -872,9 +857,7 @@ autoprewarm_main(Datum main_arg) ereport(LOG, (errmsg("autoprewarm has started"))); - /* - * **** perform autoprewarm's task **** - */ + /* Perform autoprewarm's task. */ if (todo_task == TASK_PREWARM_BUFFERPOOL && !state->skip_prewarm_on_restart) prewarm_buffer_pool(); @@ -886,13 +869,13 @@ autoprewarm_main(Datum main_arg) } /* ============================================================================ - * ============= extension's entry functions/utilities =================== + * ============= Extension's entry functions/utilities =================== * ============================================================================ */ /* * setup_autoprewarm - * A Common function to initialize BackgroundWorker structure + * A Common function to initialize BackgroundWorker structure. */ static void setup_autoprewarm(BackgroundWorker *autoprewarm, const char *worker_name, @@ -913,7 +896,7 @@ setup_autoprewarm(BackgroundWorker *autoprewarm, const char *worker_name, /* * _PG_init - * Extension's entry point + * Extension's entry point. */ void _PG_init(void) @@ -935,8 +918,8 @@ _PG_init(void) DefineCustomIntVariable("pg_prewarm.dump_interval", "Sets the maximum time between two buffer pool dumps", - "If set to Zero, timer based dumping is disabled." - " If set to -1, stops the running autoprewarm.", + "If set to zero, timer based dumping is disabled." + " If set to -1, stops autoprewarm.", &dump_interval, AT_PWARM_DEFAULT_DUMP_INTERVAL, AT_PWARM_OFF, INT_MAX / 1000, @@ -948,14 +931,14 @@ _PG_init(void) EmitWarningsOnPlaceholders("pg_prewarm"); - /* if not run as a preloaded library, nothing more to do here! */ + /* If not run as a preloaded library, nothing more to do. */ if (!process_shared_preload_libraries_in_progress) return; - /* Request additional shared resources */ + /* Request additional shared resources. */ RequestAddinShmemSpace(MAXALIGN(sizeof(AutoPrewarmSharedState))); - /* Has been set not to start autoprewarm bgworker. Nothing more to do. */ + /* If autoprewarm bgworker is disabled then nothing more to do. */ if (!autoprewarm) return; @@ -967,7 +950,7 @@ _PG_init(void) /* * autoprewarm_dump_launcher - * Dynamically launch an autoprewarm dump worker + * Dynamically launch an autoprewarm dump worker. */ static pid_t autoprewarm_dump_launcher(void) @@ -980,7 +963,7 @@ autoprewarm_dump_launcher(void) setup_autoprewarm(&worker, "autoprewarm", "autoprewarm_main", Int32GetDatum(TASK_DUMP_BUFFERPOOL_INFO), 0, 0); - /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */ + /* Set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */ worker.bgw_notify_pid = MyProcPid; if (!RegisterDynamicBackgroundWorker(&worker, &handle)) @@ -1014,14 +997,14 @@ autoprewarm_dump_launcher(void) /* * launch_autoprewarm_dump - * The C-Language entry function to launch autoprewarm dump bgworker + * The C-Language entry function to launch autoprewarm dump bgworker. */ Datum launch_autoprewarm_dump(PG_FUNCTION_ARGS) { pid_t pid; - /* Has been set not to dump. Nothing more to do. */ + /* If dump_interval is disabled then nothing more to do. */ if (dump_interval == AT_PWARM_OFF) PG_RETURN_NULL(); @@ -1031,7 +1014,7 @@ launch_autoprewarm_dump(PG_FUNCTION_ARGS) /* * autoprewarm_dump_now - * The C-Language entry function to dump immediately + * The C-Language entry function to dump immediately. */ Datum autoprewarm_dump_now(PG_FUNCTION_ARGS)