LCOV - code coverage report
Current view: top level - src/backend/postmaster - pgarch.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 157 226 69.5 %
Date: 2021-12-09 04:09:06 Functions: 14 15 93.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pgarch.c
       4             :  *
       5             :  *  PostgreSQL WAL archiver
       6             :  *
       7             :  *  All functions relating to archiver are included here
       8             :  *
       9             :  *  - All functions executed by archiver process
      10             :  *
      11             :  *  - archiver is forked from postmaster, and the two
      12             :  *  processes then communicate using signals. All functions
      13             :  *  executed by postmaster are included in this file.
      14             :  *
      15             :  *  Initial author: Simon Riggs     simon@2ndquadrant.com
      16             :  *
      17             :  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
      18             :  * Portions Copyright (c) 1994, Regents of the University of California
      19             :  *
      20             :  *
      21             :  * IDENTIFICATION
      22             :  *    src/backend/postmaster/pgarch.c
      23             :  *
      24             :  *-------------------------------------------------------------------------
      25             :  */
      26             : #include "postgres.h"
      27             : 
      28             : #include <fcntl.h>
      29             : #include <signal.h>
      30             : #include <time.h>
      31             : #include <sys/stat.h>
      32             : #include <sys/time.h>
      33             : #include <sys/wait.h>
      34             : #include <unistd.h>
      35             : 
      36             : #include "access/xlog.h"
      37             : #include "access/xlog_internal.h"
      38             : #include "lib/binaryheap.h"
      39             : #include "libpq/pqsignal.h"
      40             : #include "miscadmin.h"
      41             : #include "pgstat.h"
      42             : #include "postmaster/interrupt.h"
      43             : #include "postmaster/pgarch.h"
      44             : #include "storage/fd.h"
      45             : #include "storage/ipc.h"
      46             : #include "storage/latch.h"
      47             : #include "storage/pmsignal.h"
      48             : #include "storage/proc.h"
      49             : #include "storage/procsignal.h"
      50             : #include "storage/shmem.h"
      51             : #include "storage/spin.h"
      52             : #include "utils/guc.h"
      53             : #include "utils/ps_status.h"
      54             : 
      55             : 
      56             : /* ----------
      57             :  * Timer definitions.
      58             :  * ----------
      59             :  */
      60             : #define PGARCH_AUTOWAKE_INTERVAL 60 /* How often to force a poll of the
      61             :                                      * archive status directory; in seconds. */
      62             : #define PGARCH_RESTART_INTERVAL 10  /* How often to attempt to restart a
      63             :                                      * failed archiver; in seconds. */
      64             : 
      65             : /*
      66             :  * Maximum number of retries allowed when attempting to archive a WAL
      67             :  * file.
      68             :  */
      69             : #define NUM_ARCHIVE_RETRIES 3
      70             : 
      71             : /*
      72             :  * Maximum number of retries allowed when attempting to remove an
      73             :  * orphan archive status file.
      74             :  */
      75             : #define NUM_ORPHAN_CLEANUP_RETRIES 3
      76             : 
      77             : /*
      78             :  * Maximum number of .ready files to gather per directory scan.
      79             :  */
      80             : #define NUM_FILES_PER_DIRECTORY_SCAN 64
      81             : 
      82             : /* Shared memory area for archiver process */
      83             : typedef struct PgArchData
      84             : {
      85             :     int         pgprocno;       /* pgprocno of archiver process */
      86             : 
      87             :     /*
      88             :      * Forces a directory scan in pgarch_readyXlog().  Protected by
      89             :      * arch_lck.
      90             :      */
      91             :     bool        force_dir_scan;
      92             : 
      93             :     slock_t     arch_lck;
      94             : } PgArchData;
      95             : 
      96             : 
      97             : /* ----------
      98             :  * Local data
      99             :  * ----------
     100             :  */
     101             : static time_t last_sigterm_time = 0;
     102             : static PgArchData *PgArch = NULL;
     103             : 
     104             : /*
     105             :  * Stuff for tracking multiple files to archive from each scan of
     106             :  * archive_status.  Minimizing the number of directory scans when there are
     107             :  * many files to archive can significantly improve archival rate.
     108             :  *
     109             :  * arch_heap is a max-heap that is used during the directory scan to track
     110             :  * the highest-priority files to archive.  After the directory scan
     111             :  * completes, the file names are stored in ascending order of priority in
     112             :  * arch_files.  pgarch_readyXlog() returns files from arch_files until it
     113             :  * is empty, at which point another directory scan must be performed.
     114             :  */
     115             : static binaryheap *arch_heap = NULL;
     116             : static char arch_filenames[NUM_FILES_PER_DIRECTORY_SCAN][MAX_XFN_CHARS];
     117             : static char *arch_files[NUM_FILES_PER_DIRECTORY_SCAN];
     118             : static int arch_files_size = 0;
     119             : 
     120             : /*
     121             :  * Flags set by interrupt handlers for later service in the main loop.
     122             :  */
     123             : static volatile sig_atomic_t ready_to_stop = false;
     124             : 
     125             : /* ----------
     126             :  * Local function forward declarations
     127             :  * ----------
     128             :  */
     129             : static void pgarch_waken_stop(SIGNAL_ARGS);
     130             : static void pgarch_MainLoop(void);
     131             : static void pgarch_ArchiverCopyLoop(void);
     132             : static bool pgarch_archiveXlog(char *xlog);
     133             : static bool pgarch_readyXlog(char *xlog);
     134             : static void pgarch_archiveDone(char *xlog);
     135             : static void pgarch_die(int code, Datum arg);
     136             : static void HandlePgArchInterrupts(void);
     137             : static int ready_file_comparator(Datum a, Datum b, void *arg);
     138             : 
     139             : /* Report shared memory space needed by PgArchShmemInit */
     140             : Size
     141        9630 : PgArchShmemSize(void)
     142             : {
     143        9630 :     Size        size = 0;
     144             : 
     145        9630 :     size = add_size(size, sizeof(PgArchData));
     146             : 
     147        9630 :     return size;
     148             : }
     149             : 
     150             : /* Allocate and initialize archiver-related shared memory */
     151             : void
     152        2894 : PgArchShmemInit(void)
     153             : {
     154             :     bool        found;
     155             : 
     156        2894 :     PgArch = (PgArchData *)
     157        2894 :         ShmemInitStruct("Archiver Data", PgArchShmemSize(), &found);
     158             : 
     159        2894 :     if (!found)
     160             :     {
     161             :         /* First time through, so initialize */
     162        5788 :         MemSet(PgArch, 0, PgArchShmemSize());
     163        2894 :         PgArch->pgprocno = INVALID_PGPROCNO;
     164        2894 :         SpinLockInit(&PgArch->arch_lck);
     165             :     }
     166        2894 : }
     167             : 
     168             : /*
     169             :  * PgArchCanRestart
     170             :  *
     171             :  * Return true and archiver is allowed to restart if enough time has
     172             :  * passed since it was launched last to reach PGARCH_RESTART_INTERVAL.
     173             :  * Otherwise return false.
     174             :  *
     175             :  * This is a safety valve to protect against continuous respawn attempts if the
     176             :  * archiver is dying immediately at launch. Note that since we will retry to
     177             :  * launch the archiver from the postmaster main loop, we will get another
     178             :  * chance later.
     179             :  */
     180             : bool
     181          36 : PgArchCanRestart(void)
     182             : {
     183             :     static time_t last_pgarch_start_time = 0;
     184          36 :     time_t      curtime = time(NULL);
     185             : 
     186             :     /*
     187             :      * Return false and don't restart archiver if too soon since last archiver
     188             :      * start.
     189             :      */
     190          36 :     if ((unsigned int) (curtime - last_pgarch_start_time) <
     191             :         (unsigned int) PGARCH_RESTART_INTERVAL)
     192           0 :         return false;
     193             : 
     194          36 :     last_pgarch_start_time = curtime;
     195          36 :     return true;
     196             : }
     197             : 
     198             : 
     199             : /* Main entry point for archiver process */
     200             : void
     201           6 : PgArchiverMain(void)
     202             : {
     203             :     /*
     204             :      * Ignore all signals usually bound to some action in the postmaster,
     205             :      * except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.
     206             :      */
     207           6 :     pqsignal(SIGHUP, SignalHandlerForConfigReload);
     208           6 :     pqsignal(SIGINT, SIG_IGN);
     209           6 :     pqsignal(SIGTERM, SignalHandlerForShutdownRequest);
     210             :     /* SIGQUIT handler was already set up by InitPostmasterChild */
     211           6 :     pqsignal(SIGALRM, SIG_IGN);
     212           6 :     pqsignal(SIGPIPE, SIG_IGN);
     213           6 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
     214           6 :     pqsignal(SIGUSR2, pgarch_waken_stop);
     215             : 
     216             :     /* Reset some signals that are accepted by postmaster but not here */
     217           6 :     pqsignal(SIGCHLD, SIG_DFL);
     218             : 
     219             :     /* Unblock signals (they were blocked when the postmaster forked us) */
     220           6 :     PG_SETMASK(&UnBlockSig);
     221             : 
     222             :     /* We shouldn't be launched unnecessarily. */
     223             :     Assert(XLogArchivingActive());
     224             : 
     225             :     /* Arrange to clean up at archiver exit */
     226           6 :     on_shmem_exit(pgarch_die, 0);
     227             : 
     228             :     /*
     229             :      * Advertise our pgprocno so that backends can use our latch to wake us up
     230             :      * while we're sleeping.
     231             :      */
     232           6 :     PgArch->pgprocno = MyProc->pgprocno;
     233             : 
     234             :     /* Initialize our max-heap for prioritizing files to archive. */
     235           6 :     arch_heap = binaryheap_allocate(NUM_FILES_PER_DIRECTORY_SCAN,
     236             :                                     ready_file_comparator, NULL);
     237             : 
     238           6 :     pgarch_MainLoop();
     239             : 
     240           6 :     proc_exit(0);
     241             : }
     242             : 
     243             : /*
     244             :  * Wake up the archiver
     245             :  */
     246             : void
     247          72 : PgArchWakeup(void)
     248             : {
     249          72 :     int         arch_pgprocno = PgArch->pgprocno;
     250             : 
     251             :     /*
     252             :      * We don't acquire ProcArrayLock here.  It's actually fine because
     253             :      * procLatch isn't ever freed, so we just can potentially set the wrong
     254             :      * process' (or no process') latch.  Even in that case the archiver will
     255             :      * be relaunched shortly and will start archiving.
     256             :      */
     257          72 :     if (arch_pgprocno != INVALID_PGPROCNO)
     258          56 :         SetLatch(&ProcGlobal->allProcs[arch_pgprocno].procLatch);
     259          72 : }
     260             : 
     261             : 
     262             : /* SIGUSR2 signal handler for archiver process */
     263             : static void
     264           6 : pgarch_waken_stop(SIGNAL_ARGS)
     265             : {
     266           6 :     int         save_errno = errno;
     267             : 
     268             :     /* set flag to do a final cycle and shut down afterwards */
     269           6 :     ready_to_stop = true;
     270           6 :     SetLatch(MyLatch);
     271             : 
     272           6 :     errno = save_errno;
     273           6 : }
     274             : 
     275             : /*
     276             :  * pgarch_MainLoop
     277             :  *
     278             :  * Main loop for archiver
     279             :  */
     280             : static void
     281           6 : pgarch_MainLoop(void)
     282             : {
     283           6 :     pg_time_t   last_copy_time = 0;
     284             :     bool        time_to_stop;
     285             : 
     286             :     /*
     287             :      * There shouldn't be anything for the archiver to do except to wait for a
     288             :      * signal ... however, the archiver exists to protect our data, so she
     289             :      * wakes up occasionally to allow herself to be proactive.
     290             :      */
     291             :     do
     292             :     {
     293          26 :         ResetLatch(MyLatch);
     294             : 
     295             :         /* When we get SIGUSR2, we do one more archive cycle, then exit */
     296          26 :         time_to_stop = ready_to_stop;
     297             : 
     298             :         /* Check for barrier events and config update */
     299          26 :         HandlePgArchInterrupts();
     300             : 
     301             :         /*
     302             :          * If we've gotten SIGTERM, we normally just sit and do nothing until
     303             :          * SIGUSR2 arrives.  However, that means a random SIGTERM would
     304             :          * disable archiving indefinitely, which doesn't seem like a good
     305             :          * idea.  If more than 60 seconds pass since SIGTERM, exit anyway, so
     306             :          * that the postmaster can start a new archiver if needed.
     307             :          */
     308          26 :         if (ShutdownRequestPending)
     309             :         {
     310           0 :             time_t      curtime = time(NULL);
     311             : 
     312           0 :             if (last_sigterm_time == 0)
     313           0 :                 last_sigterm_time = curtime;
     314           0 :             else if ((unsigned int) (curtime - last_sigterm_time) >=
     315             :                      (unsigned int) 60)
     316           0 :                 break;
     317             :         }
     318             : 
     319             :         /* Do what we're here for */
     320          26 :         pgarch_ArchiverCopyLoop();
     321          26 :         last_copy_time = time(NULL);
     322             : 
     323             :         /*
     324             :          * Sleep until a signal is received, or until a poll is forced by
     325             :          * PGARCH_AUTOWAKE_INTERVAL having passed since last_copy_time, or
     326             :          * until postmaster dies.
     327             :          */
     328          26 :         if (!time_to_stop)      /* Don't wait during last iteration */
     329             :         {
     330          20 :             pg_time_t   curtime = (pg_time_t) time(NULL);
     331             :             int         timeout;
     332             : 
     333          20 :             timeout = PGARCH_AUTOWAKE_INTERVAL - (curtime - last_copy_time);
     334          20 :             if (timeout > 0)
     335             :             {
     336             :                 int         rc;
     337             : 
     338          20 :                 rc = WaitLatch(MyLatch,
     339             :                                WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
     340             :                                timeout * 1000L,
     341             :                                WAIT_EVENT_ARCHIVER_MAIN);
     342          20 :                 if (rc & WL_POSTMASTER_DEATH)
     343           0 :                     time_to_stop = true;
     344             :             }
     345             :         }
     346             : 
     347             :         /*
     348             :          * The archiver quits either when the postmaster dies (not expected)
     349             :          * or after completing one more archiving cycle after receiving
     350             :          * SIGUSR2.
     351             :          */
     352          26 :     } while (!time_to_stop);
     353           6 : }
     354             : 
     355             : /*
     356             :  * pgarch_ArchiverCopyLoop
     357             :  *
     358             :  * Archives all outstanding xlogs then returns
     359             :  */
     360             : static void
     361          26 : pgarch_ArchiverCopyLoop(void)
     362             : {
     363             :     char        xlog[MAX_XFN_CHARS + 1];
     364             : 
     365             :     /* force directory scan in the first call to pgarch_readyXlog() */
     366          26 :     arch_files_size = 0;
     367             : 
     368             :     /*
     369             :      * loop through all xlogs with archive_status of .ready and archive
     370             :      * them...mostly we expect this to be a single file, though it is possible
     371             :      * some backend will add files onto the list of those that need archiving
     372             :      * while we are still copying earlier archives
     373             :      */
     374          40 :     while (pgarch_readyXlog(xlog))
     375             :     {
     376          14 :         int         failures = 0;
     377          14 :         int         failures_orphan = 0;
     378             : 
     379             :         for (;;)
     380           0 :         {
     381             :             struct stat stat_buf;
     382             :             char        pathname[MAXPGPATH];
     383             : 
     384             :             /*
     385             :              * Do not initiate any more archive commands after receiving
     386             :              * SIGTERM, nor after the postmaster has died unexpectedly. The
     387             :              * first condition is to try to keep from having init SIGKILL the
     388             :              * command, and the second is to avoid conflicts with another
     389             :              * archiver spawned by a newer postmaster.
     390             :              */
     391          14 :             if (ShutdownRequestPending || !PostmasterIsAlive())
     392           0 :                 return;
     393             : 
     394             :             /*
     395             :              * Check for barrier events and config update.  This is so that
     396             :              * we'll adopt a new setting for archive_command as soon as
     397             :              * possible, even if there is a backlog of files to be archived.
     398             :              */
     399          14 :             HandlePgArchInterrupts();
     400             : 
     401             :             /* can't do anything if no command ... */
     402          14 :             if (!XLogArchiveCommandSet())
     403             :             {
     404           0 :                 ereport(WARNING,
     405             :                         (errmsg("archive_mode enabled, yet archive_command is not set")));
     406           0 :                 return;
     407             :             }
     408             : 
     409             :             /*
     410             :              * Since archive status files are not removed in a durable manner,
     411             :              * a system crash could leave behind .ready files for WAL segments
     412             :              * that have already been recycled or removed.  In this case,
     413             :              * simply remove the orphan status file and move on.  unlink() is
     414             :              * used here as even on subsequent crashes the same orphan files
     415             :              * would get removed, so there is no need to worry about
     416             :              * durability.
     417             :              */
     418          14 :             snprintf(pathname, MAXPGPATH, XLOGDIR "/%s", xlog);
     419          14 :             if (stat(pathname, &stat_buf) != 0 && errno == ENOENT)
     420             :             {
     421             :                 char        xlogready[MAXPGPATH];
     422             : 
     423           0 :                 StatusFilePath(xlogready, xlog, ".ready");
     424           0 :                 if (unlink(xlogready) == 0)
     425             :                 {
     426           0 :                     ereport(WARNING,
     427             :                             (errmsg("removed orphan archive status file \"%s\"",
     428             :                                     xlogready)));
     429             : 
     430             :                     /* leave loop and move to the next status file */
     431           0 :                     break;
     432             :                 }
     433             : 
     434           0 :                 if (++failures_orphan >= NUM_ORPHAN_CLEANUP_RETRIES)
     435             :                 {
     436           0 :                     ereport(WARNING,
     437             :                             (errmsg("removal of orphan archive status file \"%s\" failed too many times, will try again later",
     438             :                                     xlogready)));
     439             : 
     440             :                     /* give up cleanup of orphan status files */
     441           0 :                     return;
     442             :                 }
     443             : 
     444             :                 /* wait a bit before retrying */
     445           0 :                 pg_usleep(1000000L);
     446           0 :                 continue;
     447             :             }
     448             : 
     449          14 :             if (pgarch_archiveXlog(xlog))
     450             :             {
     451             :                 /* successful */
     452          14 :                 pgarch_archiveDone(xlog);
     453             : 
     454             :                 /*
     455             :                  * Tell the collector about the WAL file that we successfully
     456             :                  * archived
     457             :                  */
     458          14 :                 pgstat_send_archiver(xlog, false);
     459             : 
     460          14 :                 break;          /* out of inner retry loop */
     461             :             }
     462             :             else
     463             :             {
     464             :                 /*
     465             :                  * Tell the collector about the WAL file that we failed to
     466             :                  * archive
     467             :                  */
     468           0 :                 pgstat_send_archiver(xlog, true);
     469             : 
     470           0 :                 if (++failures >= NUM_ARCHIVE_RETRIES)
     471             :                 {
     472           0 :                     ereport(WARNING,
     473             :                             (errmsg("archiving write-ahead log file \"%s\" failed too many times, will try again later",
     474             :                                     xlog)));
     475           0 :                     return;     /* give up archiving for now */
     476             :                 }
     477           0 :                 pg_usleep(1000000L);    /* wait a bit before retrying */
     478             :             }
     479             :         }
     480             :     }
     481             : }
     482             : 
     483             : /*
     484             :  * pgarch_archiveXlog
     485             :  *
     486             :  * Invokes system(3) to copy one archive file to wherever it should go
     487             :  *
     488             :  * Returns true if successful
     489             :  */
     490             : static bool
     491          14 : pgarch_archiveXlog(char *xlog)
     492             : {
     493             :     char        xlogarchcmd[MAXPGPATH];
     494             :     char        pathname[MAXPGPATH];
     495             :     char        activitymsg[MAXFNAMELEN + 16];
     496             :     char       *dp;
     497             :     char       *endp;
     498             :     const char *sp;
     499             :     int         rc;
     500             : 
     501          14 :     snprintf(pathname, MAXPGPATH, XLOGDIR "/%s", xlog);
     502             : 
     503             :     /*
     504             :      * construct the command to be executed
     505             :      */
     506          14 :     dp = xlogarchcmd;
     507          14 :     endp = xlogarchcmd + MAXPGPATH - 1;
     508          14 :     *endp = '\0';
     509             : 
     510        1536 :     for (sp = XLogArchiveCommand; *sp; sp++)
     511             :     {
     512        1522 :         if (*sp == '%')
     513             :         {
     514          28 :             switch (sp[1])
     515             :             {
     516          14 :                 case 'p':
     517             :                     /* %p: relative path of source file */
     518          14 :                     sp++;
     519          14 :                     strlcpy(dp, pathname, endp - dp);
     520          14 :                     make_native_path(dp);
     521          14 :                     dp += strlen(dp);
     522          14 :                     break;
     523          14 :                 case 'f':
     524             :                     /* %f: filename of source file */
     525          14 :                     sp++;
     526          14 :                     strlcpy(dp, xlog, endp - dp);
     527          14 :                     dp += strlen(dp);
     528          14 :                     break;
     529           0 :                 case '%':
     530             :                     /* convert %% to a single % */
     531           0 :                     sp++;
     532           0 :                     if (dp < endp)
     533           0 :                         *dp++ = *sp;
     534           0 :                     break;
     535           0 :                 default:
     536             :                     /* otherwise treat the % as not special */
     537           0 :                     if (dp < endp)
     538           0 :                         *dp++ = *sp;
     539           0 :                     break;
     540             :             }
     541             :         }
     542             :         else
     543             :         {
     544        1494 :             if (dp < endp)
     545        1494 :                 *dp++ = *sp;
     546             :         }
     547             :     }
     548          14 :     *dp = '\0';
     549             : 
     550          14 :     ereport(DEBUG3,
     551             :             (errmsg_internal("executing archive command \"%s\"",
     552             :                              xlogarchcmd)));
     553             : 
     554             :     /* Report archive activity in PS display */
     555          14 :     snprintf(activitymsg, sizeof(activitymsg), "archiving %s", xlog);
     556          14 :     set_ps_display(activitymsg);
     557             : 
     558          14 :     pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
     559          14 :     rc = system(xlogarchcmd);
     560          14 :     pgstat_report_wait_end();
     561             : 
     562          14 :     if (rc != 0)
     563             :     {
     564             :         /*
     565             :          * If either the shell itself, or a called command, died on a signal,
     566             :          * abort the archiver.  We do this because system() ignores SIGINT and
     567             :          * SIGQUIT while waiting; so a signal is very likely something that
     568             :          * should have interrupted us too.  Also die if the shell got a hard
     569             :          * "command not found" type of error.  If we overreact it's no big
     570             :          * deal, the postmaster will just start the archiver again.
     571             :          */
     572           0 :         int         lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
     573             : 
     574           0 :         if (WIFEXITED(rc))
     575             :         {
     576           0 :             ereport(lev,
     577             :                     (errmsg("archive command failed with exit code %d",
     578             :                             WEXITSTATUS(rc)),
     579             :                      errdetail("The failed archive command was: %s",
     580             :                                xlogarchcmd)));
     581             :         }
     582           0 :         else if (WIFSIGNALED(rc))
     583             :         {
     584             : #if defined(WIN32)
     585             :             ereport(lev,
     586             :                     (errmsg("archive command was terminated by exception 0x%X",
     587             :                             WTERMSIG(rc)),
     588             :                      errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
     589             :                      errdetail("The failed archive command was: %s",
     590             :                                xlogarchcmd)));
     591             : #else
     592           0 :             ereport(lev,
     593             :                     (errmsg("archive command was terminated by signal %d: %s",
     594             :                             WTERMSIG(rc), pg_strsignal(WTERMSIG(rc))),
     595             :                      errdetail("The failed archive command was: %s",
     596             :                                xlogarchcmd)));
     597             : #endif
     598             :         }
     599             :         else
     600             :         {
     601           0 :             ereport(lev,
     602             :                     (errmsg("archive command exited with unrecognized status %d",
     603             :                             rc),
     604             :                      errdetail("The failed archive command was: %s",
     605             :                                xlogarchcmd)));
     606             :         }
     607             : 
     608           0 :         snprintf(activitymsg, sizeof(activitymsg), "failed on %s", xlog);
     609           0 :         set_ps_display(activitymsg);
     610             : 
     611           0 :         return false;
     612             :     }
     613          14 :     elog(DEBUG1, "archived write-ahead log file \"%s\"", xlog);
     614             : 
     615          14 :     snprintf(activitymsg, sizeof(activitymsg), "last was %s", xlog);
     616          14 :     set_ps_display(activitymsg);
     617             : 
     618          14 :     return true;
     619             : }
     620             : 
     621             : /*
     622             :  * pgarch_readyXlog
     623             :  *
     624             :  * Return name of the oldest xlog file that has not yet been archived.
     625             :  * No notification is set that file archiving is now in progress, so
     626             :  * this would need to be extended if multiple concurrent archival
     627             :  * tasks were created. If a failure occurs, we will completely
     628             :  * re-copy the file at the next available opportunity.
     629             :  *
     630             :  * It is important that we return the oldest, so that we archive xlogs
     631             :  * in order that they were written, for two reasons:
     632             :  * 1) to maintain the sequential chain of xlogs required for recovery
     633             :  * 2) because the oldest ones will sooner become candidates for
     634             :  * recycling at time of checkpoint
     635             :  *
     636             :  * NOTE: the "oldest" comparison will consider any .history file to be older
     637             :  * than any other file except another .history file.  Segments on a timeline
     638             :  * with a smaller ID will be older than all segments on a timeline with a
     639             :  * larger ID; the net result being that past timelines are given higher
     640             :  * priority for archiving.  This seems okay, or at least not obviously worth
     641             :  * changing.
     642             :  */
     643             : static bool
     644          40 : pgarch_readyXlog(char *xlog)
     645             : {
     646             :     char        XLogArchiveStatusDir[MAXPGPATH];
     647             :     DIR        *rldir;
     648             :     struct dirent *rlde;
     649             :     bool        force_dir_scan;
     650             : 
     651             :     /*
     652             :      * If a directory scan was requested, clear the stored file names and
     653             :      * proceed.
     654             :      */
     655          40 :     SpinLockAcquire(&PgArch->arch_lck);
     656          40 :     force_dir_scan = PgArch->force_dir_scan;
     657          40 :     PgArch->force_dir_scan = false;
     658          40 :     SpinLockRelease(&PgArch->arch_lck);
     659             : 
     660          40 :     if (force_dir_scan)
     661           0 :         arch_files_size = 0;
     662             : 
     663             :     /*
     664             :      * If we still have stored file names from the previous directory scan,
     665             :      * try to return one of those.  We check to make sure the status file
     666             :      * is still present, as the archive_command for a previous file may
     667             :      * have already marked it done.
     668             :      */
     669          40 :     while (arch_files_size > 0)
     670             :     {
     671             :         struct stat st;
     672             :         char        status_file[MAXPGPATH];
     673             :         char       *arch_file;
     674             : 
     675           0 :         arch_files_size--;
     676           0 :         arch_file = arch_files[arch_files_size];
     677           0 :         StatusFilePath(status_file, arch_file, ".ready");
     678             : 
     679           0 :         if (stat(status_file, &st) == 0)
     680             :         {
     681           0 :             strcpy(xlog, arch_file);
     682           0 :             return true;
     683             :         }
     684           0 :         else if (errno != ENOENT)
     685           0 :             ereport(ERROR,
     686             :                     (errcode_for_file_access(),
     687             :                      errmsg("could not stat file \"%s\": %m", status_file)));
     688             :     }
     689             : 
     690             :     /*
     691             :      * Open the archive status directory and read through the list of files
     692             :      * with the .ready suffix, looking for the earliest files.
     693             :      */
     694          40 :     snprintf(XLogArchiveStatusDir, MAXPGPATH, XLOGDIR "/archive_status");
     695          40 :     rldir = AllocateDir(XLogArchiveStatusDir);
     696             : 
     697         192 :     while ((rlde = ReadDir(rldir, XLogArchiveStatusDir)) != NULL)
     698             :     {
     699         152 :         int         basenamelen = (int) strlen(rlde->d_name) - 6;
     700             :         char        basename[MAX_XFN_CHARS + 1];
     701             :         char       *arch_file;
     702             : 
     703             :         /* Ignore entries with unexpected number of characters */
     704         152 :         if (basenamelen < MIN_XFN_CHARS ||
     705             :             basenamelen > MAX_XFN_CHARS)
     706         138 :             continue;
     707             : 
     708             :         /* Ignore entries with unexpected characters */
     709          72 :         if (strspn(rlde->d_name, VALID_XFN_CHARS) < basenamelen)
     710           0 :             continue;
     711             : 
     712             :         /* Ignore anything not suffixed with .ready */
     713          72 :         if (strcmp(rlde->d_name + basenamelen, ".ready") != 0)
     714          58 :             continue;
     715             : 
     716             :         /* Truncate off the .ready */
     717          14 :         memcpy(basename, rlde->d_name, basenamelen);
     718          14 :         basename[basenamelen] = '\0';
     719             : 
     720             :         /*
     721             :          * Store the file in our max-heap if it has a high enough priority.
     722             :          */
     723          14 :         if (arch_heap->bh_size < NUM_FILES_PER_DIRECTORY_SCAN)
     724             :         {
     725             :             /* If the heap isn't full yet, quickly add it. */
     726          14 :             arch_file = arch_filenames[arch_heap->bh_size];
     727          14 :             strcpy(arch_file, basename);
     728          14 :             binaryheap_add_unordered(arch_heap, CStringGetDatum(arch_file));
     729             : 
     730             :             /* If we just filled the heap, make it a valid one. */
     731          14 :             if (arch_heap->bh_size == NUM_FILES_PER_DIRECTORY_SCAN)
     732           0 :                 binaryheap_build(arch_heap);
     733             :         }
     734           0 :         else if (ready_file_comparator(binaryheap_first(arch_heap),
     735             :                                        CStringGetDatum(basename), NULL) > 0)
     736             :         {
     737             :             /*
     738             :              * Remove the lowest priority file and add the current one to
     739             :              * the heap.
     740             :              */
     741           0 :             arch_file = DatumGetCString(binaryheap_remove_first(arch_heap));
     742           0 :             strcpy(arch_file, basename);
     743           0 :             binaryheap_add(arch_heap, CStringGetDatum(arch_file));
     744             :         }
     745             :     }
     746          40 :     FreeDir(rldir);
     747             : 
     748             :     /* If no files were found, simply return. */
     749          40 :     if (arch_heap->bh_size == 0)
     750          26 :         return false;
     751             : 
     752             :     /*
     753             :      * If we didn't fill the heap, we didn't make it a valid one.  Do that
     754             :      * now.
     755             :      */
     756          14 :     if (arch_heap->bh_size < NUM_FILES_PER_DIRECTORY_SCAN)
     757          14 :         binaryheap_build(arch_heap);
     758             : 
     759             :     /*
     760             :      * Fill arch_files array with the files to archive in ascending order
     761             :      * of priority.
     762             :      */
     763          14 :     arch_files_size = arch_heap->bh_size;
     764          28 :     for (int i = 0; i < arch_files_size; i++)
     765          14 :         arch_files[i] = DatumGetCString(binaryheap_remove_first(arch_heap));
     766             : 
     767             :     /* Return the highest priority file. */
     768          14 :     arch_files_size--;
     769          14 :     strcpy(xlog, arch_files[arch_files_size]);
     770             : 
     771          14 :     return true;
     772             : }
     773             : 
     774             : /*
     775             :  * ready_file_comparator
     776             :  *
     777             :  * Compares the archival priority of the given files to archive.  If "a"
     778             :  * has a higher priority than "b", a negative value will be returned.  If
     779             :  * "b" has a higher priority than "a", a positive value will be returned.
     780             :  * If "a" and "b" have equivalent values, 0 will be returned.
     781             :  */
     782             : static int
     783           0 : ready_file_comparator(Datum a, Datum b, void *arg)
     784             : {
     785           0 :     char *a_str = DatumGetCString(a);
     786           0 :     char *b_str = DatumGetCString(b);
     787           0 :     bool a_history = IsTLHistoryFileName(a_str);
     788           0 :     bool b_history = IsTLHistoryFileName(b_str);
     789             : 
     790             :     /* Timeline history files always have the highest priority. */
     791           0 :     if (a_history != b_history)
     792           0 :         return a_history ? -1 : 1;
     793             : 
     794             :     /* Priority is given to older files. */
     795           0 :     return strcmp(a_str, b_str);
     796             : }
     797             : 
     798             : /*
     799             :  * PgArchForceDirScan
     800             :  *
     801             :  * When called, the next call to pgarch_readyXlog() will perform a
     802             :  * directory scan.  This is useful for ensuring that important files such
     803             :  * as timeline history files are archived as quickly as possible.
     804             :  */
     805             : void
     806          12 : PgArchForceDirScan(void)
     807             : {
     808          12 :     SpinLockAcquire(&PgArch->arch_lck);
     809          12 :     PgArch->force_dir_scan = true;
     810          12 :     SpinLockRelease(&PgArch->arch_lck);
     811          12 : }
     812             : 
     813             : /*
     814             :  * pgarch_archiveDone
     815             :  *
     816             :  * Emit notification that an xlog file has been successfully archived.
     817             :  * We do this by renaming the status file from NNN.ready to NNN.done.
     818             :  * Eventually, a checkpoint process will notice this and delete both the
     819             :  * NNN.done file and the xlog file itself.
     820             :  */
     821             : static void
     822          14 : pgarch_archiveDone(char *xlog)
     823             : {
     824             :     char        rlogready[MAXPGPATH];
     825             :     char        rlogdone[MAXPGPATH];
     826             : 
     827          14 :     StatusFilePath(rlogready, xlog, ".ready");
     828          14 :     StatusFilePath(rlogdone, xlog, ".done");
     829          14 :     (void) durable_rename(rlogready, rlogdone, WARNING);
     830          14 : }
     831             : 
     832             : 
     833             : /*
     834             :  * pgarch_die
     835             :  *
     836             :  * Exit-time cleanup handler
     837             :  */
     838             : static void
     839           6 : pgarch_die(int code, Datum arg)
     840             : {
     841           6 :     PgArch->pgprocno = INVALID_PGPROCNO;
     842           6 : }
     843             : 
     844             : /*
     845             :  * Interrupt handler for WAL archiver process.
     846             :  *
     847             :  * This is called in the loops pgarch_MainLoop and pgarch_ArchiverCopyLoop.
     848             :  * It checks for barrier events and config update, but not shutdown request
     849             :  * because how to handle shutdown request is different between those loops.
     850             :  */
     851             : static void
     852          40 : HandlePgArchInterrupts(void)
     853             : {
     854          40 :     if (ProcSignalBarrierPending)
     855           0 :         ProcessProcSignalBarrier();
     856             : 
     857          40 :     if (ConfigReloadPending)
     858             :     {
     859           0 :         ConfigReloadPending = false;
     860           0 :         ProcessConfigFile(PGC_SIGHUP);
     861             :     }
     862          40 : }

Generated by: LCOV version 1.14