diff --git a/contrib/Makefile b/contrib/Makefile index 7a4866e338..cdc041c7db 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -33,7 +33,6 @@ SUBDIRS = \ pg_buffercache \ pg_freespacemap \ pg_prewarm \ - pg_standby \ pg_stat_statements \ pg_surgery \ pg_trgm \ diff --git a/contrib/pg_standby/.gitignore b/contrib/pg_standby/.gitignore deleted file mode 100644 index a401b085a8..0000000000 --- a/contrib/pg_standby/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pg_standby diff --git a/contrib/pg_standby/Makefile b/contrib/pg_standby/Makefile deleted file mode 100644 index 87732bedf1..0000000000 --- a/contrib/pg_standby/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# contrib/pg_standby/Makefile - -PGFILEDESC = "pg_standby - supports creation of a warm standby" -PGAPPICON = win32 - -PROGRAM = pg_standby -OBJS = \ - $(WIN32RES) \ - pg_standby.o - -ifdef USE_PGXS -PG_CONFIG = pg_config -PGXS := $(shell $(PG_CONFIG) --pgxs) -include $(PGXS) -else -subdir = contrib/pg_standby -top_builddir = ../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c deleted file mode 100644 index c9f33e4254..0000000000 --- a/contrib/pg_standby/pg_standby.c +++ /dev/null @@ -1,907 +0,0 @@ -/* - * contrib/pg_standby/pg_standby.c - * - * - * pg_standby.c - * - * Production-ready example of how to create a Warm Standby - * database server using continuous archiving as a - * replication mechanism - * - * We separate the parameters for archive and nextWALfile - * so that we can check the archive exists, even if the - * WAL file doesn't (yet). - * - * This program will be executed once in full for each file - * requested by the warm standby server. - * - * It is designed to cater to a variety of needs, as well - * providing a customizable section. - * - * Original author: Simon Riggs simon@2ndquadrant.com - * Current maintainer: Simon Riggs - */ -#include "postgres_fe.h" - -#include -#include -#include -#include -#include -#include - -#include "access/xlog_internal.h" -#include "pg_getopt.h" - -const char *progname; - -int WalSegSz = -1; - -/* Options and defaults */ -int sleeptime = 5; /* amount of time to sleep between file checks */ -int waittime = -1; /* how long we have been waiting, -1 no wait - * yet */ -int maxwaittime = 0; /* how long are we prepared to wait for? */ -int keepfiles = 0; /* number of WAL files to keep, 0 keep all */ -int maxretries = 3; /* number of retries on restore command */ -bool debug = false; /* are we debugging? */ -bool need_cleanup = false; /* do we need to remove files from - * archive? */ - -#ifndef WIN32 -static volatile sig_atomic_t signaled = false; -#endif - -char *archiveLocation; /* where to find the archive? */ -char *triggerPath; /* where to find the trigger file? */ -char *xlogFilePath; /* where we are going to restore to */ -char *nextWALFileName; /* the file we need to get from archive */ -char *restartWALFileName; /* the file from which we can restart restore */ -char WALFilePath[MAXPGPATH * 2]; /* the file path including archive */ -char restoreCommand[MAXPGPATH]; /* run this to restore */ -char exclusiveCleanupFileName[MAXFNAMELEN]; /* the file we need to get - * from archive */ - -/* - * Two types of failover are supported (smart and fast failover). - * - * The content of the trigger file determines the type of failover. If the - * trigger file contains the word "smart" (or the file is empty), smart - * failover is chosen: pg_standby acts as cp or ln command itself, on - * successful completion all the available WAL records will be applied - * resulting in zero data loss. But, it might take a long time to finish - * recovery if there's a lot of unapplied WAL. - * - * On the other hand, if the trigger file contains the word "fast", the - * recovery is finished immediately even if unapplied WAL files remain. Any - * transactions in the unapplied WAL files are lost. - * - * An empty trigger file performs smart failover. SIGUSR or SIGINT triggers - * fast failover. A timeout causes fast failover (smart failover would have - * the same effect, since if the timeout is reached there is no unapplied WAL). - */ -#define NoFailover 0 -#define SmartFailover 1 -#define FastFailover 2 - -static int Failover = NoFailover; - -#define RESTORE_COMMAND_COPY 0 -#define RESTORE_COMMAND_LINK 1 -int restoreCommandType; - -#define XLOG_DATA 0 -#define XLOG_HISTORY 1 -int nextWALFileType; - -#define SET_RESTORE_COMMAND(cmd, arg1, arg2) \ - snprintf(restoreCommand, MAXPGPATH, cmd " \"%s\" \"%s\"", arg1, arg2) - -struct stat stat_buf; - -static bool SetWALFileNameForCleanup(void); -static bool SetWALSegSize(void); - - -/* ===================================================================== - * - * Customizable section - * - * ===================================================================== - * - * Currently, this section assumes that the Archive is a locally - * accessible directory. If you want to make other assumptions, - * such as using a vendor-specific archive and access API, these - * routines are the ones you'll need to change. You're - * encouraged to submit any changes to pgsql-hackers@lists.postgresql.org - * or personally to the current maintainer. Those changes may be - * folded in to later versions of this program. - */ - -/* - * Initialize allows customized commands into the warm standby program. - * - * As an example, and probably the common case, we use either - * cp/ln commands on *nix, or copy/move command on Windows. - */ -static void -CustomizableInitialize(void) -{ -#ifdef WIN32 - snprintf(WALFilePath, MAXPGPATH, "%s\\%s", archiveLocation, nextWALFileName); - switch (restoreCommandType) - { - case RESTORE_COMMAND_LINK: - SET_RESTORE_COMMAND("mklink", WALFilePath, xlogFilePath); - break; - case RESTORE_COMMAND_COPY: - default: - SET_RESTORE_COMMAND("copy", WALFilePath, xlogFilePath); - break; - } -#else - snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, nextWALFileName); - switch (restoreCommandType) - { - case RESTORE_COMMAND_LINK: - SET_RESTORE_COMMAND("ln -s -f", WALFilePath, xlogFilePath); - break; - case RESTORE_COMMAND_COPY: - default: - SET_RESTORE_COMMAND("cp", WALFilePath, xlogFilePath); - break; - } -#endif - - /* - * This code assumes that archiveLocation is a directory You may wish to - * add code to check for tape libraries, etc.. So, since it is a - * directory, we use stat to test if it's accessible - */ - if (stat(archiveLocation, &stat_buf) != 0) - { - fprintf(stderr, "%s: archive location \"%s\" does not exist\n", progname, archiveLocation); - fflush(stderr); - exit(2); - } -} - -/* - * CustomizableNextWALFileReady() - * - * Is the requested file ready yet? - */ -static bool -CustomizableNextWALFileReady(void) -{ - if (stat(WALFilePath, &stat_buf) == 0) - { - /* - * If we've not seen any WAL segments, we don't know the WAL segment - * size, which we need. If it looks like a WAL segment, determine size - * of segments for the cluster. - */ - if (WalSegSz == -1 && IsXLogFileName(nextWALFileName)) - { - if (SetWALSegSize()) - { - /* - * Successfully determined WAL segment size. Can compute - * cleanup cutoff now. - */ - need_cleanup = SetWALFileNameForCleanup(); - if (debug) - { - fprintf(stderr, - _("WAL segment size: %d \n"), WalSegSz); - fprintf(stderr, "Keep archive history: "); - - if (need_cleanup) - fprintf(stderr, "%s and later\n", - exclusiveCleanupFileName); - else - fprintf(stderr, "no cleanup required\n"); - } - } - } - - /* - * Return only if it's the right size already. - */ - if (WalSegSz > 0 && stat_buf.st_size == WalSegSz) - { -#ifdef WIN32 - - /* - * Windows 'cp' sets the final file size before the copy is - * complete, and not yet ready to be opened by pg_standby. So we - * wait for sleeptime secs before attempting to restore. If that - * is not enough, we will rely on the retry/holdoff mechanism. - * GNUWin32's cp does not have this problem. - */ - pg_usleep(sleeptime * 1000000L); -#endif - nextWALFileType = XLOG_DATA; - return true; - } - - /* - * If still too small, wait until it is the correct size - */ - if (WalSegSz > 0 && stat_buf.st_size > WalSegSz) - { - if (debug) - { - fprintf(stderr, "file size greater than expected\n"); - fflush(stderr); - } - exit(3); - } - } - - return false; -} - -static void -CustomizableCleanupPriorWALFiles(void) -{ - /* - * Work out name of prior file from current filename - */ - if (nextWALFileType == XLOG_DATA) - { - int rc; - DIR *xldir; - struct dirent *xlde; - - /* - * Assume it's OK to keep failing. The failure situation may change - * over time, so we'd rather keep going on the main processing than - * fail because we couldn't clean up yet. - */ - if ((xldir = opendir(archiveLocation)) != NULL) - { - while (errno = 0, (xlde = readdir(xldir)) != NULL) - { - /* - * We ignore the timeline part of the XLOG segment identifiers - * in deciding whether a segment is still needed. This - * ensures that we won't prematurely remove a segment from a - * parent timeline. We could probably be a little more - * proactive about removing segments of non-parent timelines, - * but that would be a whole lot more complicated. - * - * We use the alphanumeric sorting property of the filenames - * to decide which ones are earlier than the - * exclusiveCleanupFileName file. Note that this means files - * are not removed in the order they were originally written, - * in case this worries you. - */ - if (IsXLogFileName(xlde->d_name) && - strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0) - { -#ifdef WIN32 - snprintf(WALFilePath, sizeof(WALFilePath), "%s\\%s", archiveLocation, xlde->d_name); -#else - snprintf(WALFilePath, sizeof(WALFilePath), "%s/%s", archiveLocation, xlde->d_name); -#endif - - if (debug) - fprintf(stderr, "\nremoving file \"%s\"", WALFilePath); - - rc = unlink(WALFilePath); - if (rc != 0) - { - fprintf(stderr, "\n%s: ERROR: could not remove file \"%s\": %s\n", - progname, WALFilePath, strerror(errno)); - break; - } - } - } - - if (errno) - fprintf(stderr, "%s: could not read archive location \"%s\": %s\n", - progname, archiveLocation, strerror(errno)); - if (debug) - fprintf(stderr, "\n"); - } - else - fprintf(stderr, "%s: could not open archive location \"%s\": %s\n", - progname, archiveLocation, strerror(errno)); - - if (closedir(xldir)) - fprintf(stderr, "%s: could not close archive location \"%s\": %s\n", - progname, archiveLocation, strerror(errno)); - - fflush(stderr); - } -} - -/* ===================================================================== - * End of Customizable section - * ===================================================================== - */ - -/* - * SetWALFileNameForCleanup() - * - * Set the earliest WAL filename that we want to keep on the archive - * and decide whether we need_cleanup - */ -static bool -SetWALFileNameForCleanup(void) -{ - uint32 tli = 1, - log = 0, - seg = 0; - uint32 log_diff = 0, - seg_diff = 0; - bool cleanup = false; - int max_segments_per_logfile = (0xFFFFFFFF / WalSegSz); - - if (restartWALFileName) - { - /* - * Don't do cleanup if the restartWALFileName provided is later than - * the xlog file requested. This is an error and we must not remove - * these files from archive. This shouldn't happen, but better safe - * than sorry. - */ - if (strcmp(restartWALFileName, nextWALFileName) > 0) - return false; - - strlcpy(exclusiveCleanupFileName, restartWALFileName, sizeof(exclusiveCleanupFileName)); - return true; - } - - if (keepfiles > 0) - { - sscanf(nextWALFileName, "%08X%08X%08X", &tli, &log, &seg); - if (tli > 0 && seg > 0) - { - log_diff = keepfiles / max_segments_per_logfile; - seg_diff = keepfiles % max_segments_per_logfile; - if (seg_diff > seg) - { - log_diff++; - seg = max_segments_per_logfile - (seg_diff - seg); - } - else - seg -= seg_diff; - - if (log >= log_diff) - { - log -= log_diff; - cleanup = true; - } - else - { - log = 0; - seg = 0; - } - } - } - - XLogFileNameById(exclusiveCleanupFileName, tli, log, seg); - - return cleanup; -} - -/* - * Try to set the wal segment size from the WAL file specified by WALFilePath. - * - * Return true if size could be determined, false otherwise. - */ -static bool -SetWALSegSize(void) -{ - bool ret_val = false; - int fd; - PGAlignedXLogBlock buf; - - Assert(WalSegSz == -1); - - if ((fd = open(WALFilePath, O_RDWR, 0)) < 0) - { - fprintf(stderr, "%s: could not open WAL file \"%s\": %s\n", - progname, WALFilePath, strerror(errno)); - return false; - } - - errno = 0; - if (read(fd, buf.data, XLOG_BLCKSZ) == XLOG_BLCKSZ) - { - XLogLongPageHeader longhdr = (XLogLongPageHeader) buf.data; - - WalSegSz = longhdr->xlp_seg_size; - - if (IsValidWalSegSize(WalSegSz)) - { - /* successfully retrieved WAL segment size */ - ret_val = true; - } - else - fprintf(stderr, - "%s: WAL segment size must be a power of two between 1MB and 1GB, but the WAL file header specifies %d bytes\n", - progname, WalSegSz); - } - else - { - /* - * Don't complain loudly, this is to be expected for segments being - * created. - */ - if (errno != 0) - { - if (debug) - fprintf(stderr, "could not read file \"%s\": %s\n", - WALFilePath, strerror(errno)); - } - else - { - if (debug) - fprintf(stderr, "not enough data in file \"%s\"\n", - WALFilePath); - } - } - - fflush(stderr); - - close(fd); - return ret_val; -} - -/* - * CheckForExternalTrigger() - * - * Is there a trigger file? Sets global 'Failover' variable to indicate - * what kind of a trigger file it was. A "fast" trigger file is turned - * into a "smart" file as a side-effect. - */ -static void -CheckForExternalTrigger(void) -{ - char buf[32]; - int fd; - int len; - - /* - * Look for a trigger file, if that option has been selected - * - * We use stat() here because triggerPath is always a file rather than - * potentially being in an archive - */ - if (!triggerPath || stat(triggerPath, &stat_buf) != 0) - return; - - /* - * An empty trigger file performs smart failover. There's a little race - * condition here: if the writer of the trigger file has just created the - * file, but not yet written anything to it, we'll treat that as smart - * shutdown even if the other process was just about to write "fast" to - * it. But that's fine: we'll restore one more WAL file, and when we're - * invoked next time, we'll see the word "fast" and fail over immediately. - */ - if (stat_buf.st_size == 0) - { - Failover = SmartFailover; - fprintf(stderr, "trigger file found: smart failover\n"); - fflush(stderr); - return; - } - - if ((fd = open(triggerPath, O_RDWR, 0)) < 0) - { - fprintf(stderr, "WARNING: could not open \"%s\": %s\n", - triggerPath, strerror(errno)); - fflush(stderr); - return; - } - - if ((len = read(fd, buf, sizeof(buf) - 1)) < 0) - { - fprintf(stderr, "WARNING: could not read \"%s\": %s\n", - triggerPath, strerror(errno)); - fflush(stderr); - close(fd); - return; - } - buf[len] = '\0'; - - if (strncmp(buf, "smart", 5) == 0) - { - Failover = SmartFailover; - fprintf(stderr, "trigger file found: smart failover\n"); - fflush(stderr); - close(fd); - return; - } - - if (strncmp(buf, "fast", 4) == 0) - { - Failover = FastFailover; - - fprintf(stderr, "trigger file found: fast failover\n"); - fflush(stderr); - - /* - * Turn it into a "smart" trigger by truncating the file. Otherwise if - * the server asks us again to restore a segment that was restored - * already, we would return "not found" and upset the server. - */ - if (ftruncate(fd, 0) < 0) - { - fprintf(stderr, "WARNING: could not read \"%s\": %s\n", - triggerPath, strerror(errno)); - fflush(stderr); - } - close(fd); - - return; - } - close(fd); - - fprintf(stderr, "WARNING: invalid content in \"%s\"\n", triggerPath); - fflush(stderr); -} - -/* - * RestoreWALFileForRecovery() - * - * Perform the action required to restore the file from archive - */ -static bool -RestoreWALFileForRecovery(void) -{ - int rc = 0; - int numretries = 0; - - if (debug) - { - fprintf(stderr, "running restore: "); - fflush(stderr); - } - - while (numretries <= maxretries) - { - rc = system(restoreCommand); - if (rc == 0) - { - if (debug) - { - fprintf(stderr, "OK\n"); - fflush(stderr); - } - return true; - } - pg_usleep(numretries++ * sleeptime * 1000000L); - } - - /* - * Allow caller to add additional info - */ - if (debug) - fprintf(stderr, "not restored\n"); - return false; -} - -static void -usage(void) -{ - printf("%s allows PostgreSQL warm standby servers to be configured.\n\n", progname); - printf("Usage:\n"); - printf(" %s [OPTION]... ARCHIVELOCATION NEXTWALFILE XLOGFILEPATH [RESTARTWALFILE]\n", progname); - printf("\nOptions:\n"); - printf(" -c copy file from archive (default)\n"); - printf(" -d generate lots of debugging output (testing only)\n"); - printf(" -k NUMFILESTOKEEP if RESTARTWALFILE is not used, remove files prior to limit\n" - " (0 keeps all)\n"); - printf(" -l does nothing; use of link is now deprecated\n"); - printf(" -r MAXRETRIES max number of times to retry, with progressive wait\n" - " (default=3)\n"); - printf(" -s SLEEPTIME seconds to wait between file checks (min=1, max=60,\n" - " default=5)\n"); - printf(" -t TRIGGERFILE trigger file to initiate failover (no default)\n"); - printf(" -V, --version output version information, then exit\n"); - printf(" -w MAXWAITTIME max seconds to wait for a file (0=no limit) (default=0)\n"); - printf(" -?, --help show this help, then exit\n"); - printf("\n" - "Main intended use as restore_command in postgresql.conf:\n" - " restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n" - "e.g.\n" - " restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n"); - printf("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT); - printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL); -} - -#ifndef WIN32 -static void -sighandler(int sig) -{ - signaled = true; -} - -/* We don't want SIGQUIT to core dump */ -static void -sigquit_handler(int sig) -{ - pqsignal(SIGINT, SIG_DFL); - kill(getpid(), SIGINT); -} -#endif - -/*------------ MAIN ----------------------------------------*/ -int -main(int argc, char **argv) -{ - int c; - - progname = get_progname(argv[0]); - - if (argc > 1) - { - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - { - usage(); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - puts("pg_standby (PostgreSQL) " PG_VERSION); - exit(0); - } - } - -#ifndef WIN32 - - /* - * You can send SIGUSR1 to trigger failover. - * - * Postmaster uses SIGQUIT to request immediate shutdown. The default - * action is to core dump, but we don't want that, so trap it and commit - * suicide without core dump. - * - * We used to use SIGINT and SIGQUIT to trigger failover, but that turned - * out to be a bad idea because postmaster uses SIGQUIT to request - * immediate shutdown. We still trap SIGINT, but that may change in a - * future release. - * - * There's no way to trigger failover via signal on Windows. - */ - (void) pqsignal(SIGUSR1, sighandler); - (void) pqsignal(SIGINT, sighandler); /* deprecated, use SIGUSR1 */ - (void) pqsignal(SIGQUIT, sigquit_handler); -#endif - - while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1) - { - switch (c) - { - case 'c': /* Use copy */ - restoreCommandType = RESTORE_COMMAND_COPY; - break; - case 'd': /* Debug mode */ - debug = true; - break; - case 'k': /* keepfiles */ - keepfiles = atoi(optarg); - if (keepfiles < 0) - { - fprintf(stderr, "%s: -k keepfiles must be >= 0\n", progname); - exit(2); - } - break; - case 'l': /* Use link */ - - /* - * Link feature disabled, possibly permanently. Linking causes - * a problem after recovery ends that is not currently - * resolved by PostgreSQL. 25 Jun 2009 - */ -#ifdef NOT_USED - restoreCommandType = RESTORE_COMMAND_LINK; -#endif - break; - case 'r': /* Retries */ - maxretries = atoi(optarg); - if (maxretries < 0) - { - fprintf(stderr, "%s: -r maxretries must be >= 0\n", progname); - exit(2); - } - break; - case 's': /* Sleep time */ - sleeptime = atoi(optarg); - if (sleeptime <= 0 || sleeptime > 60) - { - fprintf(stderr, "%s: -s sleeptime incorrectly set\n", progname); - exit(2); - } - break; - case 't': /* Trigger file */ - triggerPath = pg_strdup(optarg); - break; - case 'w': /* Max wait time */ - maxwaittime = atoi(optarg); - if (maxwaittime < 0) - { - fprintf(stderr, "%s: -w maxwaittime incorrectly set\n", progname); - exit(2); - } - break; - default: - fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); - exit(2); - break; - } - } - - /* - * Parameter checking - after checking to see if trigger file present - */ - if (argc == 1) - { - fprintf(stderr, "%s: not enough command-line arguments\n", progname); - exit(2); - } - - /* - * We will go to the archiveLocation to get nextWALFileName. - * nextWALFileName may not exist yet, which would not be an error, so we - * separate the archiveLocation and nextWALFileName so we can check - * separately whether archiveLocation exists, if not that is an error - */ - if (optind < argc) - { - archiveLocation = argv[optind]; - optind++; - } - else - { - fprintf(stderr, "%s: must specify archive location\n", progname); - fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); - exit(2); - } - - if (optind < argc) - { - nextWALFileName = argv[optind]; - optind++; - } - else - { - fprintf(stderr, "%s: must specify WAL file name as second non-option argument (use \"%%f\")\n", progname); - fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); - exit(2); - } - - if (optind < argc) - { - xlogFilePath = argv[optind]; - optind++; - } - else - { - fprintf(stderr, "%s: must specify xlog destination as third non-option argument (use \"%%p\")\n", progname); - fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); - exit(2); - } - - if (optind < argc) - { - restartWALFileName = argv[optind]; - optind++; - } - - CustomizableInitialize(); - - if (debug) - { - fprintf(stderr, "Trigger file: %s\n", triggerPath ? triggerPath : ""); - fprintf(stderr, "Waiting for WAL file: %s\n", nextWALFileName); - fprintf(stderr, "WAL file path: %s\n", WALFilePath); - fprintf(stderr, "Restoring to: %s\n", xlogFilePath); - fprintf(stderr, "Sleep interval: %d second%s\n", - sleeptime, (sleeptime > 1 ? "s" : " ")); - fprintf(stderr, "Max wait interval: %d %s\n", - maxwaittime, (maxwaittime > 0 ? "seconds" : "forever")); - fprintf(stderr, "Command for restore: %s\n", restoreCommand); - fflush(stderr); - } - - /* - * Check for initial history file: always the first file to be requested - * It's OK if the file isn't there - all other files need to wait - */ - if (IsTLHistoryFileName(nextWALFileName)) - { - nextWALFileType = XLOG_HISTORY; - if (RestoreWALFileForRecovery()) - exit(0); - else - { - if (debug) - { - fprintf(stderr, "history file not found\n"); - fflush(stderr); - } - exit(1); - } - } - - /* - * Main wait loop - */ - for (;;) - { - /* Check for trigger file or signal first */ - CheckForExternalTrigger(); -#ifndef WIN32 - if (signaled) - { - Failover = FastFailover; - if (debug) - { - fprintf(stderr, "signaled to exit: fast failover\n"); - fflush(stderr); - } - } -#endif - - /* - * Check for fast failover immediately, before checking if the - * requested WAL file is available - */ - if (Failover == FastFailover) - exit(1); - - if (CustomizableNextWALFileReady()) - { - /* - * Once we have restored this file successfully we can remove some - * prior WAL files. If this restore fails we mustn't remove any - * file because some of them will be requested again immediately - * after the failed restore, or when we restart recovery. - */ - if (RestoreWALFileForRecovery()) - { - if (need_cleanup) - CustomizableCleanupPriorWALFiles(); - - exit(0); - } - else - { - /* Something went wrong in copying the file */ - exit(1); - } - } - - /* Check for smart failover if the next WAL file was not available */ - if (Failover == SmartFailover) - exit(1); - - if (sleeptime <= 60) - pg_usleep(sleeptime * 1000000L); - - waittime += sleeptime; - if (waittime >= maxwaittime && maxwaittime > 0) - { - Failover = FastFailover; - if (debug) - { - fprintf(stderr, "Timed out after %d seconds: fast failover\n", - waittime); - fflush(stderr); - } - } - if (debug) - { - fprintf(stderr, "WAL file not present yet."); - if (triggerPath) - fprintf(stderr, " Checking for trigger file..."); - fprintf(stderr, "\n"); - fflush(stderr); - } - } -} diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml index ae2759be55..d3ca4b6932 100644 --- a/doc/src/sgml/contrib.sgml +++ b/doc/src/sgml/contrib.sgml @@ -192,13 +192,12 @@ pages. Server Applications - This section covers PostgreSQL server-related - applications in contrib. They are typically run on the - host where the database server resides. See also PostgreSQL server + itself. Currently, no such applications are included in the + contrib directory. See also for information about server applications that are part of the core PostgreSQL distribution. - &pgstandby; diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index 38e8aa0bbf..db1d369743 100644 --- a/doc/src/sgml/filelist.sgml +++ b/doc/src/sgml/filelist.sgml @@ -138,7 +138,6 @@ - diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml index dc263e4106..9364dc74f7 100644 --- a/doc/src/sgml/high-availability.sgml +++ b/doc/src/sgml/high-availability.sgml @@ -699,11 +699,9 @@ protocol to make nodes agree on a serializable transactional order. - Do not use pg_standby or similar tools with the built-in standby mode - described here. should return immediately + should return immediately if the file does not exist; the server will retry the command again if - necessary. See - for using tools like pg_standby. + necessary. @@ -1494,8 +1492,7 @@ synchronous_standby_names = 'ANY 2 (s1, s2, s3)' An alternative to the built-in standby mode described in the previous sections is to use a restore_command that polls the archive location. - This was the only option available in versions 8.4 and below. See the - module for a reference implementation of this. + This was the only option available in versions 8.4 and below. @@ -1551,14 +1548,6 @@ if (!triggered) - - A working example of a waiting restore_command is provided - in the module. It - should be used as a reference on how to correctly implement the logic - described above. It can also be extended as needed to support specific - configurations and environments. - - The method for triggering failover is an important part of planning and design. One potential option is the restore_command diff --git a/doc/src/sgml/pgstandby.sgml b/doc/src/sgml/pgstandby.sgml deleted file mode 100644 index 66a6255930..0000000000 --- a/doc/src/sgml/pgstandby.sgml +++ /dev/null @@ -1,394 +0,0 @@ - - - - - pg_standby - - - - pg_standby - 1 - Application - - - - pg_standby - supports the creation of a PostgreSQL warm standby server - - - - - pg_standby - option - archivelocation - nextwalfile - walfilepath - restartwalfile - - - - - Description - - - pg_standby supports creation of a warm standby - database server. It is designed to be a production-ready program, as well - as a customizable template should you require specific modifications. - - - - pg_standby is designed to be a waiting - restore_command, which is needed to turn a standard - archive recovery into a warm standby operation. Other - configuration is required as well, all of which is described in the main - server manual (see ). - - - - To configure a standby - server to use pg_standby, put this into its - postgresql.conf configuration file: - -restore_command = 'pg_standby archiveDir %f %p %r' - - where archiveDir is the directory from which WAL segment - files should be restored. - - - If restartwalfile is specified, normally by using the - %r macro, then all WAL files logically preceding this - file will be removed from archivelocation. This minimizes - the number of files that need to be retained, while preserving - crash-restart capability. Use of this parameter is appropriate if the - archivelocation is a transient staging area for this - particular standby server, but not when the - archivelocation is intended as a long-term WAL archive area. - - - pg_standby assumes that - archivelocation is a directory readable by the - server-owning user. If restartwalfile (or -k) - is specified, - the archivelocation directory must be writable too. - - - There are two ways to fail over to a warm standby database server - when the primary server fails: - - - - Smart Failover - - - In smart failover, the server is brought up after applying all WAL - files available in the archive. This results in zero data loss, even if - the standby server has fallen behind, but if there is a lot of - unapplied WAL it can be a long time before the standby server becomes - ready. To trigger a smart failover, create a trigger file containing - the word smart, or just create it and leave it empty. - - - - - Fast Failover - - - In fast failover, the server is brought up immediately. Any WAL files - in the archive that have not yet been applied will be ignored, and - all transactions in those files are lost. To trigger a fast failover, - create a trigger file and write the word fast into it. - pg_standby can also be configured to execute a fast - failover automatically if no new WAL file appears within a defined - interval. - - - - - - - - - - Options - - - pg_standby accepts the following command-line arguments: - - - - - - - - Use cp or copy command to restore WAL files - from archive. This is the only supported behavior so this option is useless. - - - - - - - - - Print lots of debug logging output on stderr. - - - - - - - - - Remove files from archivelocation so that - no more than this many WAL files before the current one are kept in the - archive. Zero (the default) means not to remove any files from - archivelocation. - This parameter will be silently ignored if - restartwalfile is specified, since that - specification method is more accurate in determining the correct - archive cut-off point. - Use of this parameter is deprecated as of - PostgreSQL 8.3; it is safer and more efficient to - specify a restartwalfile parameter. A too - small setting could result in removal of files that are still needed - for a restart of the standby server, while a too large setting wastes - archive space. - - - - - - maxretries - - - Set the maximum number of times to retry the copy command if - it fails (default 3). After each failure, we wait for - sleeptime * num_retries - so that the wait time increases progressively. So by default, - we will wait 5 secs, 10 secs, then 15 secs before reporting - the failure back to the standby server. This will be - interpreted as end of recovery and the standby will come - up fully as a result. - - - - - - sleeptime - - - Set the number of seconds (up to 60, default 5) to sleep between - tests to see if the WAL file to be restored is available in - the archive yet. The default setting is not necessarily - recommended; consult for discussion. - - - - - - triggerfile - - - Specify a trigger file whose presence should cause failover. - It is recommended that you use a structured file name to - avoid confusion as to which server is being triggered - when multiple servers exist on the same system; for example - /tmp/pgsql.trigger.5432. - - - - - - - - - - Print the pg_standby version and exit. - - - - - - maxwaittime - - - Set the maximum number of seconds to wait for the next WAL file, - after which a fast failover will be performed. - A setting of zero (the default) means wait forever. - The default setting is not necessarily recommended; - consult for discussion. - - - - - - - - - - Show help about pg_standby command line - arguments, and exit. - - - - - - - - - - Notes - - - pg_standby is designed to work with - PostgreSQL 8.2 and later. - - - PostgreSQL 8.3 provides the %r macro, - which is designed to let pg_standby know the - last file it needs to keep. With PostgreSQL 8.2, the - -k option must be used if archive cleanup is - required. This option remains available in 8.3, but its use is deprecated. - - - PostgreSQL 8.4 provides the - recovery_end_command option. Without this option - a leftover trigger file can be hazardous. - - - - pg_standby is written in C and has an - easy-to-modify source code, with specifically designated sections to modify - for your own needs - - - - - Examples - - On Linux or Unix systems, you might use: - - -archive_command = 'cp %p .../archive/%f' - -restore_command = 'pg_standby -d -s 2 -t /tmp/pgsql.trigger.5442 .../archive %f %p %r 2>>standby.log' - -recovery_end_command = 'rm -f /tmp/pgsql.trigger.5442' - - where the archive directory is physically located on the standby server, - so that the archive_command is accessing it across NFS, - but the files are local to the standby (enabling use of ln). - This will: - - - - produce debugging output in standby.log - - - - - sleep for 2 seconds between checks for next WAL file availability - - - - - stop waiting only when a trigger file called - /tmp/pgsql.trigger.5442 appears, - and perform failover according to its content - - - - - remove the trigger file when recovery ends - - - - - remove no-longer-needed files from the archive directory - - - - - - On Windows, you might use: - - -archive_command = 'copy %p ...\\archive\\%f' - -restore_command = 'pg_standby -d -s 5 -t C:\pgsql.trigger.5442 ...\archive %f %p %r 2>>standby.log' - -recovery_end_command = 'del C:\pgsql.trigger.5442' - - Note that backslashes need to be doubled in the - archive_command, but not in the - restore_command or recovery_end_command. - This will: - - - - use the copy command to restore WAL files from archive - - - - - produce debugging output in standby.log - - - - - sleep for 5 seconds between checks for next WAL file availability - - - - - stop waiting only when a trigger file called - C:\pgsql.trigger.5442 appears, - and perform failover according to its content - - - - - remove the trigger file when recovery ends - - - - - remove no-longer-needed files from the archive directory - - - - - - - The copy command on Windows sets the final file size - before the file is completely copied, which would ordinarily confuse - pg_standby. Therefore - pg_standby waits sleeptime - seconds once it sees the proper file size. GNUWin32's cp - sets the file size only after the file copy is complete. - - - - Since the Windows example uses copy at both ends, either - or both servers might be accessing the archive directory across the - network. - - - - - - Author - - - Simon Riggs simon@2ndquadrant.com - - - - - See Also - - - - - - diff --git a/doc/src/sgml/ref/pgarchivecleanup.sgml b/doc/src/sgml/ref/pgarchivecleanup.sgml index 56f02fc0e6..e27db3c077 100644 --- a/doc/src/sgml/ref/pgarchivecleanup.sgml +++ b/doc/src/sgml/ref/pgarchivecleanup.sgml @@ -205,11 +205,4 @@ archive_cleanup_command = 'pg_archivecleanup -d /mnt/standby/archive %r 2>>clean - - See Also - - - - - diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 236a66f638..f03bd473e2 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -4199,7 +4199,7 @@ RemoveXlogFile(const char *segname, XLogSegNo recycleSegNo, /* * Before deleting the file, see if it can be recycled as a future log - * segment. Only recycle normal files, pg_standby for example can create + * segment. Only recycle normal files, because we don't want to recycle * symbolic links pointing to a separate archive directory. */ if (wal_recycle && diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 7213e65e08..90328db04e 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -34,8 +34,8 @@ my @unlink_on_exit; # Set of variables for modules in contrib/ and src/test/modules/ my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' }; my @contrib_uselibpq = ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo'); -my @contrib_uselibpgport = ('oid2name', 'pg_standby', 'vacuumlo'); -my @contrib_uselibpgcommon = ('oid2name', 'pg_standby', 'vacuumlo'); +my @contrib_uselibpgport = ('oid2name', 'vacuumlo'); +my @contrib_uselibpgcommon = ('oid2name', 'vacuumlo'); my $contrib_extralibs = undef; my $contrib_extraincludes = { 'dblink' => ['src/backend'] }; my $contrib_extrasource = {