PostgreSQL Source Code  git master
pg_rewind.c File Reference
#include "postgres_fe.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include "access/timeline.h"
#include "access/xlog_internal.h"
#include "catalog/catversion.h"
#include "catalog/pg_control.h"
#include "common/controldata_utils.h"
#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/restricted_token.h"
#include "fe_utils/recovery_gen.h"
#include "fetch.h"
#include "file_ops.h"
#include "filemap.h"
#include "getopt_long.h"
#include "pg_rewind.h"
#include "storage/bufpage.h"
Include dependency graph for pg_rewind.c:

Go to the source code of this file.

Macros

#define MAXCMDLEN   (2 * MAXPGPATH)
 

Functions

static void usage (const char *progname)
 
static void createBackupLabel (XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
 
static void digestControlFile (ControlFileData *ControlFile, char *source, size_t size)
 
static void syncTargetDirectory (void)
 
static void sanityChecks (void)
 
static void findCommonAncestorTimeline (XLogRecPtr *recptr, int *tliIndex)
 
static void ensureCleanShutdown (const char *argv0)
 
static void disconnect_atexit (void)
 
int main (int argc, char **argv)
 
void progress_report (bool force)
 
static XLogRecPtr MinXLogRecPtr (XLogRecPtr a, XLogRecPtr b)
 
static TimeLineHistoryEntrygetTimelineHistory (ControlFileData *controlFile, int *nentries)
 
static void checkControlFile (ControlFileData *ControlFile)
 

Variables

static ControlFileData ControlFile_target
 
static ControlFileData ControlFile_source
 
const char * progname
 
int WalSegSz
 
char * datadir_target = NULL
 
char * datadir_source = NULL
 
char * connstr_source = NULL
 
static bool debug = false
 
bool showprogress = false
 
bool dry_run = false
 
bool do_sync = true
 
TimeLineHistoryEntrytargetHistory
 
int targetNentries
 
uint64 fetch_size
 
uint64 fetch_done
 

Macro Definition Documentation

◆ MAXCMDLEN

#define MAXCMDLEN   (2 * MAXPGPATH)

Referenced by ensureCleanShutdown().

Function Documentation

◆ checkControlFile()

static void checkControlFile ( ControlFileData ControlFile)
static

Definition at line 749 of file pg_rewind.c.

References COMP_CRC32C, EQ_CRC32C, FIN_CRC32C, INIT_CRC32C, offsetof, and pg_fatal.

Referenced by digestControlFile().

750 {
751  pg_crc32c crc;
752 
753  /* Calculate CRC */
754  INIT_CRC32C(crc);
755  COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));
756  FIN_CRC32C(crc);
757 
758  /* And simply compare it */
759  if (!EQ_CRC32C(crc, ControlFile->crc))
760  pg_fatal("unexpected control file CRC");
761 }
#define INIT_CRC32C(crc)
Definition: pg_crc32c.h:41
uint32 pg_crc32c
Definition: pg_crc32c.h:38
#define pg_fatal(...)
Definition: pg_rewind.h:41
#define EQ_CRC32C(c1, c2)
Definition: pg_crc32c.h:42
#define COMP_CRC32C(crc, data, len)
Definition: pg_crc32c.h:89
#define FIN_CRC32C(crc)
Definition: pg_crc32c.h:94
#define offsetof(type, field)
Definition: c.h:662

◆ createBackupLabel()

static void createBackupLabel ( XLogRecPtr  startpoint,
TimeLineID  starttli,
XLogRecPtr  checkpointloc 
)
static

Definition at line 706 of file pg_rewind.c.

References buf, close_target_file(), MAXFNAMELEN, open_target_file(), pg_fatal, snprintf, tm, WalSegSz, write_target_range(), XLByteToSeg, and XLogFileName.

Referenced by main().

707 {
708  XLogSegNo startsegno;
709  time_t stamp_time;
710  char strfbuf[128];
711  char xlogfilename[MAXFNAMELEN];
712  struct tm *tmp;
713  char buf[1000];
714  int len;
715 
716  XLByteToSeg(startpoint, startsegno, WalSegSz);
717  XLogFileName(xlogfilename, starttli, startsegno, WalSegSz);
718 
719  /*
720  * Construct backup label file
721  */
722  stamp_time = time(NULL);
723  tmp = localtime(&stamp_time);
724  strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", tmp);
725 
726  len = snprintf(buf, sizeof(buf),
727  "START WAL LOCATION: %X/%X (file %s)\n"
728  "CHECKPOINT LOCATION: %X/%X\n"
729  "BACKUP METHOD: pg_rewind\n"
730  "BACKUP FROM: standby\n"
731  "START TIME: %s\n",
732  /* omit LABEL: line */
733  (uint32) (startpoint >> 32), (uint32) startpoint, xlogfilename,
734  (uint32) (checkpointloc >> 32), (uint32) checkpointloc,
735  strfbuf);
736  if (len >= sizeof(buf))
737  pg_fatal("backup label buffer too small"); /* shouldn't happen */
738 
739  /* TODO: move old file out of the way, if any. */
740  open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
741  write_target_range(buf, 0, len);
743 }
void open_target_file(const char *path, bool trunc)
Definition: file_ops.c:42
void write_target_range(char *buf, off_t begin, size_t size)
Definition: file_ops.c:83
#define pg_fatal(...)
Definition: pg_rewind.h:41
static struct pg_tm tm
Definition: localtime.c:108
static char * buf
Definition: pg_test_fsync.c:67
uint64 XLogSegNo
Definition: xlogdefs.h:41
unsigned int uint32
Definition: c.h:359
#define MAXFNAMELEN
int WalSegSz
Definition: pg_rewind.c:50
#define XLogFileName(fname, tli, logSegNo, wal_segsz_bytes)
void close_target_file(void)
Definition: file_ops.c:70
#define snprintf
Definition: port.h:192
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)

◆ digestControlFile()

static void digestControlFile ( ControlFileData ControlFile,
char *  source,
size_t  size 
)
static

Definition at line 767 of file pg_rewind.c.

References checkControlFile(), IsValidWalSegSize, ngettext, PG_CONTROL_FILE_SIZE, pg_fatal, WalSegSz, and ControlFileData::xlog_seg_size.

Referenced by main().

768 {
769  if (size != PG_CONTROL_FILE_SIZE)
770  pg_fatal("unexpected control file size %d, expected %d",
771  (int) size, PG_CONTROL_FILE_SIZE);
772 
773  memcpy(ControlFile, src, sizeof(ControlFileData));
774 
775  /* set and validate WalSegSz */
776  WalSegSz = ControlFile->xlog_seg_size;
777 
779  pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte",
780  "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes",
781  WalSegSz),
782  WalSegSz);
783 
784  /* Additional checks on control file */
785  checkControlFile(ControlFile);
786 }
#define IsValidWalSegSize(size)
Definition: xlog_internal.h:97
#define pg_fatal(...)
Definition: pg_rewind.h:41
uint32 xlog_seg_size
Definition: pg_control.h:209
static void checkControlFile(ControlFileData *ControlFile)
Definition: pg_rewind.c:749
int WalSegSz
Definition: pg_rewind.c:50
#define ngettext(s, p, n)
Definition: c.h:1134
#define PG_CONTROL_FILE_SIZE
Definition: pg_control.h:248

◆ disconnect_atexit()

static void disconnect_atexit ( void  )
static

Definition at line 866 of file pg_rewind.c.

References conn, and PQfinish().

Referenced by main().

867 {
868  if (conn != NULL)
869  PQfinish(conn);
870 }
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4125
PGconn * conn
Definition: streamutil.c:54

◆ ensureCleanShutdown()

static void ensureCleanShutdown ( const char *  argv0)
static

Definition at line 811 of file pg_rewind.c.

References datadir_target, DEVNULL, dry_run, exec_path, find_my_exec(), find_other_exec(), MAXCMDLEN, MAXPGPATH, PG_BACKEND_VERSIONSTR, pg_fatal, pg_log_error, pg_log_info, progname, snprintf, and strlcpy().

Referenced by main().

812 {
813  int ret;
814 #define MAXCMDLEN (2 * MAXPGPATH)
815  char exec_path[MAXPGPATH];
816  char cmd[MAXCMDLEN];
817 
818  /* locate postgres binary */
819  if ((ret = find_other_exec(argv0, "postgres",
821  exec_path)) < 0)
822  {
823  char full_path[MAXPGPATH];
824 
825  if (find_my_exec(argv0, full_path) < 0)
826  strlcpy(full_path, progname, sizeof(full_path));
827 
828  if (ret == -1)
829  pg_fatal("The program \"%s\" is needed by %s but was\n"
830  "not found in the same directory as \"%s\".\n"
831  "Check your installation.",
832  "postgres", progname, full_path);
833  else
834  pg_fatal("The program \"%s\" was found by \"%s\" but was\n"
835  "not the same version as %s.\n"
836  "Check your installation.",
837  "postgres", full_path, progname);
838  }
839 
840  pg_log_info("executing \"%s\" for target server to complete crash recovery",
841  exec_path);
842 
843  /*
844  * Skip processing if requested, but only after ensuring presence of
845  * postgres.
846  */
847  if (dry_run)
848  return;
849 
850  /*
851  * Finally run postgres in single-user mode. There is no need to use
852  * fsync here. This makes the recovery faster, and the target data folder
853  * is synced at the end anyway.
854  */
855  snprintf(cmd, MAXCMDLEN, "\"%s\" --single -F -D \"%s\" template1 < \"%s\"",
856  exec_path, datadir_target, DEVNULL);
857 
858  if (system(cmd) != 0)
859  {
860  pg_log_error("postgres single-user mode of target instance failed");
861  pg_fatal("Command was: %s", cmd);
862  }
863 }
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
Definition: exec.c:324
char * datadir_target
Definition: pg_rewind.c:53
static char * argv0
Definition: pg_ctl.c:97
#define MAXCMDLEN
bool dry_run
Definition: pg_rewind.c:59
#define pg_log_error(...)
Definition: logging.h:79
#define pg_fatal(...)
Definition: pg_rewind.h:41
#define MAXPGPATH
int find_my_exec(const char *argv0, char *retpath)
Definition: exec.c:129
#define DEVNULL
Definition: port.h:123
#define PG_BACKEND_VERSIONSTR
Definition: port.h:111
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static char * exec_path
Definition: pg_ctl.c:92
#define snprintf
Definition: port.h:192
#define pg_log_info(...)
Definition: logging.h:87
const char * progname
Definition: pg_rewind.c:49

◆ findCommonAncestorTimeline()

static void findCommonAncestorTimeline ( XLogRecPtr recptr,
int *  tliIndex 
)
static

Definition at line 658 of file pg_rewind.c.

References getTimelineHistory(), i, Min, MinXLogRecPtr(), pg_fatal, pg_free(), and targetNentries.

Referenced by main().

659 {
660  TimeLineHistoryEntry *sourceHistory;
661  int sourceNentries;
662  int i,
663  n;
664 
665  /* Retrieve timelines for both source and target */
666  sourceHistory = getTimelineHistory(&ControlFile_source, &sourceNentries);
668 
669  /*
670  * Trace the history forward, until we hit the timeline diverge. It may
671  * still be possible that the source and target nodes used the same
672  * timeline number in their history but with different start position
673  * depending on the history files that each node has fetched in previous
674  * recovery processes. Hence check the start position of the new timeline
675  * as well and move down by one extra timeline entry if they do not match.
676  */
677  n = Min(sourceNentries, targetNentries);
678  for (i = 0; i < n; i++)
679  {
680  if (sourceHistory[i].tli != targetHistory[i].tli ||
681  sourceHistory[i].begin != targetHistory[i].begin)
682  break;
683  }
684 
685  if (i > 0)
686  {
687  i--;
688  *recptr = MinXLogRecPtr(sourceHistory[i].end, targetHistory[i].end);
689  *tliIndex = i;
690 
691  pg_free(sourceHistory);
692  return;
693  }
694  else
695  {
696  pg_fatal("could not find common ancestor of the source and target cluster's timelines");
697  }
698 }
TimeLineHistoryEntry * targetHistory
Definition: pg_rewind.c:63
#define Min(x, y)
Definition: c.h:911
static XLogRecPtr MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
Definition: pg_rewind.c:566
#define pg_fatal(...)
Definition: pg_rewind.h:41
int targetNentries
Definition: pg_rewind.c:64
void pg_free(void *ptr)
Definition: fe_memutils.c:105
static TimeLineHistoryEntry * getTimelineHistory(ControlFileData *controlFile, int *nentries)
Definition: pg_rewind.c:581
int i
static ControlFileData ControlFile_target
Definition: pg_rewind.c:46
static ControlFileData ControlFile_source
Definition: pg_rewind.c:47

◆ getTimelineHistory()

static TimeLineHistoryEntry* getTimelineHistory ( ControlFileData controlFile,
int *  nentries 
)
static

Definition at line 581 of file pg_rewind.c.

References Assert, TimeLineHistoryEntry::begin, ControlFileData::checkPointCopy, datadir_target, debug, TimeLineHistoryEntry::end, fetchFile(), i, InvalidXLogRecPtr, MAXPGPATH, pg_fatal, pg_free(), pg_log_debug, pg_malloc(), rewind_parseTimeLineHistory(), slurpFile(), targetNentries, CheckPoint::ThisTimeLineID, TLHistoryFilePath, and TimeLineHistoryEntry::tli.

Referenced by findCommonAncestorTimeline().

582 {
583  TimeLineHistoryEntry *history;
584  TimeLineID tli;
585 
586  tli = controlFile->checkPointCopy.ThisTimeLineID;
587 
588  /*
589  * Timeline 1 does not have a history file, so there is no need to check
590  * and fake an entry with infinite start and end positions.
591  */
592  if (tli == 1)
593  {
594  history = (TimeLineHistoryEntry *) pg_malloc(sizeof(TimeLineHistoryEntry));
595  history->tli = tli;
596  history->begin = history->end = InvalidXLogRecPtr;
597  *nentries = 1;
598  }
599  else
600  {
601  char path[MAXPGPATH];
602  char *histfile;
603 
604  TLHistoryFilePath(path, tli);
605 
606  /* Get history file from appropriate source */
607  if (controlFile == &ControlFile_source)
608  histfile = fetchFile(path, NULL);
609  else if (controlFile == &ControlFile_target)
610  histfile = slurpFile(datadir_target, path, NULL);
611  else
612  pg_fatal("invalid control file");
613 
614  history = rewind_parseTimeLineHistory(histfile, tli, nentries);
615  pg_free(histfile);
616  }
617 
618  if (debug)
619  {
620  int i;
621 
622  if (controlFile == &ControlFile_source)
623  pg_log_debug("Source timeline history:");
624  else if (controlFile == &ControlFile_target)
625  pg_log_debug("Target timeline history:");
626  else
627  Assert(false);
628 
629  /*
630  * Print the target timeline history.
631  */
632  for (i = 0; i < targetNentries; i++)
633  {
634  TimeLineHistoryEntry *entry;
635 
636  entry = &history[i];
637  pg_log_debug("%d: %X/%X - %X/%X", entry->tli,
638  (uint32) (entry->begin >> 32), (uint32) (entry->begin),
639  (uint32) (entry->end >> 32), (uint32) (entry->end));
640  }
641  }
642 
643  return history;
644 }
char * datadir_target
Definition: pg_rewind.c:53
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
uint32 TimeLineID
Definition: xlogdefs.h:52
static bool debug
Definition: pg_rewind.c:57
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
#define pg_fatal(...)
Definition: pg_rewind.h:41
TimeLineID tli
Definition: timeline.h:27
CheckPoint checkPointCopy
Definition: pg_control.h:131
char * fetchFile(const char *filename, size_t *filesize)
Definition: fetch.c:54
#define pg_log_debug(...)
Definition: logging.h:91
#define MAXPGPATH
unsigned int uint32
Definition: c.h:359
int targetNentries
Definition: pg_rewind.c:64
XLogRecPtr end
Definition: timeline.h:29
#define Assert(condition)
Definition: c.h:739
void pg_free(void *ptr)
Definition: fe_memutils.c:105
TimeLineID ThisTimeLineID
Definition: pg_control.h:39
XLogRecPtr begin
Definition: timeline.h:28
int i
TimeLineHistoryEntry * rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
Definition: timeline.c:29
static ControlFileData ControlFile_target
Definition: pg_rewind.c:46
static ControlFileData ControlFile_source
Definition: pg_rewind.c:47
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
Definition: file_ops.c:284
#define TLHistoryFilePath(path, tli)

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 95 of file pg_rewind.c.

References _, calculate_totals(), ControlFileData::checkPoint, ControlFileData::checkPointCopy, conn, connstr_source, createBackupLabel(), datadir_source, datadir_target, DB_IN_ARCHIVE_RECOVERY, DB_SHUTDOWNED, DB_SHUTDOWNED_IN_RECOVERY, debug, digestControlFile(), disconnect_atexit(), do_sync, dry_run, ensureCleanShutdown(), executeFileMap(), extractPageMap(), fetch_done, fetch_size, filemap_t::fetch_size, fetchFile(), fetchSourceFileList(), filemap, filemap_create(), filemap_finalize(), findCommonAncestorTimeline(), findLastCheckpoint(), fprintf, GenerateRecoveryConfig(), get_progname(), get_restricted_token(), GetDataDirectoryCreatePerm(), getopt_long(), libpqConnect(), libpqGetCurrentXlogInsertLocation(), ControlFileData::minRecoveryPoint, ControlFileData::minRecoveryPointTLI, no_argument, optarg, optind, pg_free(), PG_LOG_DEBUG, pg_log_error, pg_log_info, pg_logging_init(), pg_logging_set_level(), pg_mode_mask, pg_strdup(), PG_TEXTDOMAIN, print_filemap(), printf, process_target_file(), progname, progress_report(), readOneRecord(), required_argument, sanityChecks(), set_pglocale_pgservice(), showprogress, slurpFile(), ControlFileData::state, syncTargetDirectory(), targetNentries, CheckPoint::ThisTimeLineID, filemap_t::total_size, traverse_datadir(), update_controlfile(), usage(), writerecoveryconf, and WriteRecoveryConfig().

96 {
97  static struct option long_options[] = {
98  {"help", no_argument, NULL, '?'},
99  {"target-pgdata", required_argument, NULL, 'D'},
100  {"write-recovery-conf", no_argument, NULL, 'R'},
101  {"source-pgdata", required_argument, NULL, 1},
102  {"source-server", required_argument, NULL, 2},
103  {"no-ensure-shutdown", no_argument, NULL, 4},
104  {"version", no_argument, NULL, 'V'},
105  {"dry-run", no_argument, NULL, 'n'},
106  {"no-sync", no_argument, NULL, 'N'},
107  {"progress", no_argument, NULL, 'P'},
108  {"debug", no_argument, NULL, 3},
109  {NULL, 0, NULL, 0}
110  };
111  int option_index;
112  int c;
113  XLogRecPtr divergerec;
114  int lastcommontliIndex;
115  XLogRecPtr chkptrec;
116  TimeLineID chkpttli;
117  XLogRecPtr chkptredo;
118  size_t size;
119  char *buffer;
120  bool no_ensure_shutdown = false;
121  bool rewind_needed;
122  XLogRecPtr endrec;
123  TimeLineID endtli;
124  ControlFileData ControlFile_new;
125  bool writerecoveryconf = false;
126 
127  pg_logging_init(argv[0]);
128  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
129  progname = get_progname(argv[0]);
130 
131  /* Process command-line arguments */
132  if (argc > 1)
133  {
134  if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
135  {
136  usage(progname);
137  exit(0);
138  }
139  if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
140  {
141  puts("pg_rewind (PostgreSQL) " PG_VERSION);
142  exit(0);
143  }
144  }
145 
146  while ((c = getopt_long(argc, argv, "D:nNPR", long_options, &option_index)) != -1)
147  {
148  switch (c)
149  {
150  case '?':
151  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
152  exit(1);
153 
154  case 'P':
155  showprogress = true;
156  break;
157 
158  case 'n':
159  dry_run = true;
160  break;
161 
162  case 'N':
163  do_sync = false;
164  break;
165 
166  case 'R':
167  writerecoveryconf = true;
168  break;
169 
170  case 3:
171  debug = true;
173  break;
174 
175  case 'D': /* -D or --target-pgdata */
177  break;
178 
179  case 1: /* --source-pgdata */
181  break;
182 
183  case 2: /* --source-server */
185  break;
186 
187  case 4:
188  no_ensure_shutdown = true;
189  break;
190  }
191  }
192 
193  if (datadir_source == NULL && connstr_source == NULL)
194  {
195  pg_log_error("no source specified (--source-pgdata or --source-server)");
196  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
197  exit(1);
198  }
199 
200  if (datadir_source != NULL && connstr_source != NULL)
201  {
202  pg_log_error("only one of --source-pgdata or --source-server can be specified");
203  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
204  exit(1);
205  }
206 
207  if (datadir_target == NULL)
208  {
209  pg_log_error("no target data directory specified (--target-pgdata)");
210  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
211  exit(1);
212  }
213 
214  if (writerecoveryconf && connstr_source == NULL)
215  {
216  pg_log_error("no source server information (--source--server) specified for --write-recovery-conf");
217  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
218  exit(1);
219  }
220 
221  if (optind < argc)
222  {
223  pg_log_error("too many command-line arguments (first is \"%s\")",
224  argv[optind]);
225  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
226  exit(1);
227  }
228 
229  /*
230  * Don't allow pg_rewind to be run as root, to avoid overwriting the
231  * ownership of files in the data directory. We need only check for root
232  * -- any other user won't have sufficient permissions to modify files in
233  * the data directory.
234  */
235 #ifndef WIN32
236  if (geteuid() == 0)
237  {
238  pg_log_error("cannot be executed by \"root\"");
239  fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
240  progname);
241  exit(1);
242  }
243 #endif
244 
246 
247  /* Set mask based on PGDATA permissions */
249  {
250  pg_log_error("could not read permissions of directory \"%s\": %m",
252  exit(1);
253  }
254 
255  umask(pg_mode_mask);
256 
257  atexit(disconnect_atexit);
258 
259  /* Connect to remote server */
260  if (connstr_source)
262 
263  /*
264  * Ok, we have all the options and we're ready to start. Read in all the
265  * information we need from both clusters.
266  */
267  buffer = slurpFile(datadir_target, "global/pg_control", &size);
268  digestControlFile(&ControlFile_target, buffer, size);
269  pg_free(buffer);
270 
271  /*
272  * If the target instance was not cleanly shut down, start and stop the
273  * target cluster once in single-user mode to enforce recovery to finish,
274  * ensuring that the cluster can be used by pg_rewind. Note that if
275  * no_ensure_shutdown is specified, pg_rewind ignores this step, and users
276  * need to make sure by themselves that the target cluster is in a clean
277  * state.
278  */
279  if (!no_ensure_shutdown &&
282  {
283  ensureCleanShutdown(argv[0]);
284 
285  buffer = slurpFile(datadir_target, "global/pg_control", &size);
286  digestControlFile(&ControlFile_target, buffer, size);
287  pg_free(buffer);
288  }
289 
290  buffer = fetchFile("global/pg_control", &size);
291  digestControlFile(&ControlFile_source, buffer, size);
292  pg_free(buffer);
293 
294  sanityChecks();
295 
296  /*
297  * If both clusters are already on the same timeline, there's nothing to
298  * do.
299  */
301  {
302  pg_log_info("source and target cluster are on the same timeline");
303  rewind_needed = false;
304  }
305  else
306  {
307  findCommonAncestorTimeline(&divergerec, &lastcommontliIndex);
308  pg_log_info("servers diverged at WAL location %X/%X on timeline %u",
309  (uint32) (divergerec >> 32), (uint32) divergerec,
310  targetHistory[lastcommontliIndex].tli);
311 
312  /*
313  * Check for the possibility that the target is in fact a direct
314  * ancestor of the source. In that case, there is no divergent history
315  * in the target that needs rewinding.
316  */
317  if (ControlFile_target.checkPoint >= divergerec)
318  {
319  rewind_needed = true;
320  }
321  else
322  {
323  XLogRecPtr chkptendrec;
324 
325  /* Read the checkpoint record on the target to see where it ends. */
326  chkptendrec = readOneRecord(datadir_target,
328  targetNentries - 1);
329 
330  /*
331  * If the histories diverged exactly at the end of the shutdown
332  * checkpoint record on the target, there are no WAL records in
333  * the target that don't belong in the source's history, and no
334  * rewind is needed.
335  */
336  if (chkptendrec == divergerec)
337  rewind_needed = false;
338  else
339  rewind_needed = true;
340  }
341  }
342 
343  if (!rewind_needed)
344  {
345  pg_log_info("no rewind required");
346  if (writerecoveryconf && !dry_run)
349  exit(0);
350  }
351 
353  lastcommontliIndex,
354  &chkptrec, &chkpttli, &chkptredo);
355  pg_log_info("rewinding from last common checkpoint at %X/%X on timeline %u",
356  (uint32) (chkptrec >> 32), (uint32) chkptrec,
357  chkpttli);
358 
359  /*
360  * Build the filemap, by comparing the source and target data directories.
361  */
362  filemap_create();
363  if (showprogress)
364  pg_log_info("reading source file list");
366  if (showprogress)
367  pg_log_info("reading target file list");
369 
370  /*
371  * Read the target WAL from last checkpoint before the point of fork, to
372  * extract all the pages that were modified on the target cluster after
373  * the fork. We can stop reading after reaching the final shutdown record.
374  * XXX: If we supported rewinding a server that was not shut down cleanly,
375  * we would need to replay until the end of WAL here.
376  */
377  if (showprogress)
378  pg_log_info("reading WAL in target");
379  extractPageMap(datadir_target, chkptrec, lastcommontliIndex,
382 
383  if (showprogress)
385 
386  /* this is too verbose even for verbose mode */
387  if (debug)
388  print_filemap();
389 
390  /*
391  * Ok, we're ready to start copying things over.
392  */
393  if (showprogress)
394  {
395  pg_log_info("need to copy %lu MB (total source directory size is %lu MB)",
396  (unsigned long) (filemap->fetch_size / (1024 * 1024)),
397  (unsigned long) (filemap->total_size / (1024 * 1024)));
398 
400  fetch_done = 0;
401  }
402 
403  /*
404  * This is the point of no return. Once we start copying things, we have
405  * modified the target directory and there is no turning back!
406  */
407 
408  executeFileMap();
409 
410  progress_report(true);
411  printf("\n");
412 
413  if (showprogress)
414  pg_log_info("creating backup label and updating control file");
415  createBackupLabel(chkptredo, chkpttli, chkptrec);
416 
417  /*
418  * Update control file of target. Make it ready to perform archive
419  * recovery when restarting.
420  *
421  * minRecoveryPoint is set to the current WAL insert location in the
422  * source server. Like in an online backup, it's important that we recover
423  * all the WAL that was generated while we copied the files over.
424  */
425  memcpy(&ControlFile_new, &ControlFile_source, sizeof(ControlFileData));
426 
427  if (connstr_source)
428  {
431  }
432  else
433  {
436  }
437  ControlFile_new.minRecoveryPoint = endrec;
438  ControlFile_new.minRecoveryPointTLI = endtli;
439  ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
440  if (!dry_run)
441  update_controlfile(datadir_target, &ControlFile_new, do_sync);
442 
443  if (showprogress)
444  pg_log_info("syncing target data directory");
446 
447  if (writerecoveryconf && !dry_run)
450 
451  pg_log_info("Done!");
452 
453  return 0;
454 }
void calculate_totals(void)
Definition: filemap.c:606
static void syncTargetDirectory(void)
Definition: pg_rewind.c:798
char * datadir_target
Definition: pg_rewind.c:53
uint32 TimeLineID
Definition: xlogdefs.h:52
static bool debug
Definition: pg_rewind.c:57
TimeLineID minRecoveryPointTLI
Definition: pg_control.h:167
TimeLineHistoryEntry * targetHistory
Definition: pg_rewind.c:63
bool dry_run
Definition: pg_rewind.c:59
const char * get_progname(const char *argv0)
Definition: path.c:453
void process_target_file(const char *path, file_type_t type, size_t oldsize, const char *link_target)
Definition: filemap.c:326
#define pg_log_error(...)
Definition: logging.h:79
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
void get_restricted_token(void)
void pg_logging_init(const char *argv0)
Definition: logging.c:39
void WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
Definition: recovery_gen.c:117
void progress_report(bool force)
Definition: pg_rewind.c:512
static void ensureCleanShutdown(const char *argv0)
Definition: pg_rewind.c:811
void extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex, XLogRecPtr endpoint)
Definition: parsexlog.c:57
static bool writerecoveryconf
#define printf(...)
Definition: port.h:198
CheckPoint checkPointCopy
Definition: pg_control.h:131
void fetchSourceFileList(void)
Definition: fetch.c:28
void filemap_create(void)
Definition: filemap.c:113
#define fprintf
Definition: port.h:196
char * fetchFile(const char *filename, size_t *filesize)
Definition: fetch.c:54
void filemap_finalize(void)
Definition: filemap.c:570
void executeFileMap(void)
Definition: fetch.c:40
uint64 fetch_size
Definition: filemap.h:86
bool showprogress
Definition: pg_rewind.c:58
void update_controlfile(const char *DataDir, ControlFileData *ControlFile, bool do_sync)
#define required_argument
Definition: getopt_long.h:25
int optind
Definition: getopt.c:50
uint64 fetch_size
Definition: pg_rewind.c:67
PGconn * conn
Definition: streamutil.c:54
XLogRecPtr libpqGetCurrentXlogInsertLocation(void)
Definition: libpq_fetch.c:147
char * c
static void usage(const char *progname)
Definition: pg_rewind.c:72
XLogRecPtr readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex)
Definition: parsexlog.c:109
filemap_t * filemap
Definition: filemap.c:23
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
uint64 total_size
Definition: filemap.h:85
unsigned int uint32
Definition: c.h:359
void print_filemap(void)
Definition: filemap.c:648
static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex)
Definition: pg_rewind.c:658
void traverse_datadir(const char *datadir, process_file_callback_t callback)
Definition: copy_fetch.c:33
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
Definition: recovery_gen.c:23
char * connstr_source
Definition: pg_rewind.c:55
#define no_argument
Definition: getopt_long.h:24
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1166
static void disconnect_atexit(void)
Definition: pg_rewind.c:866
int targetNentries
Definition: pg_rewind.c:64
char * datadir_source
Definition: pg_rewind.c:54
bool do_sync
Definition: pg_rewind.c:60
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void pg_free(void *ptr)
Definition: fe_memutils.c:105
static void sanityChecks(void)
Definition: pg_rewind.c:457
bool GetDataDirectoryCreatePerm(const char *dataDir)
TimeLineID ThisTimeLineID
Definition: pg_control.h:39
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:565
void findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex, XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli, XLogRecPtr *lastchkptredo)
Definition: parsexlog.c:149
char * optarg
Definition: getopt.c:52
void pg_logging_set_level(enum pg_log_level new_level)
Definition: logging.c:108
static ControlFileData ControlFile_target
Definition: pg_rewind.c:46
uint64 fetch_done
Definition: pg_rewind.c:68
static ControlFileData ControlFile_source
Definition: pg_rewind.c:47
static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
Definition: pg_rewind.c:706
static void digestControlFile(ControlFileData *ControlFile, char *source, size_t size)
Definition: pg_rewind.c:767
#define _(x)
Definition: elog.c:87
int pg_mode_mask
Definition: file_perm.c:25
XLogRecPtr checkPoint
Definition: pg_control.h:129
#define pg_log_info(...)
Definition: logging.h:87
const char * progname
Definition: pg_rewind.c:49
XLogRecPtr minRecoveryPoint
Definition: pg_control.h:166
void libpqConnect(const char *connstr)
Definition: libpq_fetch.c:43
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
Definition: file_ops.c:284

◆ MinXLogRecPtr()

static XLogRecPtr MinXLogRecPtr ( XLogRecPtr  a,
XLogRecPtr  b 
)
static

Definition at line 566 of file pg_rewind.c.

References Min, and XLogRecPtrIsInvalid.

Referenced by findCommonAncestorTimeline().

567 {
568  if (XLogRecPtrIsInvalid(a))
569  return b;
570  else if (XLogRecPtrIsInvalid(b))
571  return a;
572  else
573  return Min(a, b);
574 }
#define Min(x, y)
Definition: c.h:911
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29

◆ progress_report()

void progress_report ( bool  force)

Definition at line 512 of file pg_rewind.c.

References _, fetch_done, fetch_size, fprintf, INT64_FORMAT, last_progress_report, now(), showprogress, and snprintf.

Referenced by main().

513 {
514  static pg_time_t last_progress_report = 0;
515  int percent;
516  char fetch_done_str[32];
517  char fetch_size_str[32];
518  pg_time_t now;
519 
520  if (!showprogress)
521  return;
522 
523  now = time(NULL);
524  if (now == last_progress_report && !force)
525  return; /* Max once per second */
526 
527  last_progress_report = now;
528  percent = fetch_size ? (int) ((fetch_done) * 100 / fetch_size) : 0;
529 
530  /*
531  * Avoid overflowing past 100% or the full size. This may make the total
532  * size number change as we approach the end of the backup (the estimate
533  * will always be wrong if WAL is included), but that's better than having
534  * the done column be bigger than the total.
535  */
536  if (percent > 100)
537  percent = 100;
538  if (fetch_done > fetch_size)
540 
541  /*
542  * Separate step to keep platform-dependent format code out of
543  * translatable strings. And we only test for INT64_FORMAT availability
544  * in snprintf, not fprintf.
545  */
546  snprintf(fetch_done_str, sizeof(fetch_done_str), INT64_FORMAT,
547  fetch_done / 1024);
548  snprintf(fetch_size_str, sizeof(fetch_size_str), INT64_FORMAT,
549  fetch_size / 1024);
550 
551  fprintf(stderr, _("%*s/%s kB (%d%%) copied"),
552  (int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
553  percent);
554  if (isatty(fileno(stderr)))
555  fprintf(stderr, "\r");
556  else
557  fprintf(stderr, "\n");
558 }
int64 pg_time_t
Definition: pgtime.h:23
static pg_time_t last_progress_report
#define fprintf
Definition: port.h:196
bool showprogress
Definition: pg_rewind.c:58
uint64 fetch_size
Definition: pg_rewind.c:67
#define INT64_FORMAT
Definition: c.h:401
uint64 fetch_done
Definition: pg_rewind.c:68
#define snprintf
Definition: port.h:192
#define _(x)
Definition: elog.c:87
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1547

◆ sanityChecks()

static void sanityChecks ( void  )
static

Definition at line 457 of file pg_rewind.c.

References CATALOG_VERSION_NO, ControlFileData::catalog_version_no, ControlFileData::data_checksum_version, datadir_source, DB_SHUTDOWNED, DB_SHUTDOWNED_IN_RECOVERY, PG_CONTROL_VERSION, ControlFileData::pg_control_version, PG_DATA_CHECKSUM_VERSION, pg_fatal, ControlFileData::state, ControlFileData::system_identifier, and ControlFileData::wal_log_hints.

Referenced by main().

458 {
459  /* TODO Check that there's no backup_label in either cluster */
460 
461  /* Check system_identifier match */
463  pg_fatal("source and target clusters are from different systems");
464 
465  /* check version */
470  {
471  pg_fatal("clusters are not compatible with this version of pg_rewind");
472  }
473 
474  /*
475  * Target cluster need to use checksums or hint bit wal-logging, this to
476  * prevent from data corruption that could occur because of hint bits.
477  */
480  {
481  pg_fatal("target server needs to use either data checksums or \"wal_log_hints = on\"");
482  }
483 
484  /*
485  * Target cluster better not be running. This doesn't guard against
486  * someone starting the cluster concurrently. Also, this is probably more
487  * strict than necessary; it's OK if the target node was not shut down
488  * cleanly, as long as it isn't running at the moment.
489  */
492  pg_fatal("target server must be shut down cleanly");
493 
494  /*
495  * When the source is a data directory, also require that the source
496  * server is shut down. There isn't any very strong reason for this
497  * limitation, but better safe than sorry.
498  */
499  if (datadir_source &&
502  pg_fatal("source data directory must be shut down cleanly");
503 }
#define pg_fatal(...)
Definition: pg_rewind.h:41
#define CATALOG_VERSION_NO
Definition: catversion.h:56
#define PG_CONTROL_VERSION
Definition: pg_control.h:25
uint32 pg_control_version
Definition: pg_control.h:121
uint64 system_identifier
Definition: pg_control.h:106
uint32 data_checksum_version
Definition: pg_control.h:220
char * datadir_source
Definition: pg_rewind.c:54
uint32 catalog_version_no
Definition: pg_control.h:122
#define PG_DATA_CHECKSUM_VERSION
Definition: bufpage.h:200
static ControlFileData ControlFile_target
Definition: pg_rewind.c:46
static ControlFileData ControlFile_source
Definition: pg_rewind.c:47

◆ syncTargetDirectory()

static void syncTargetDirectory ( void  )
static

Definition at line 798 of file pg_rewind.c.

References datadir_target, do_sync, dry_run, and fsync_pgdata().

Referenced by main().

799 {
800  if (!do_sync || dry_run)
801  return;
802 
803  fsync_pgdata(datadir_target, PG_VERSION_NUM);
804 }
char * datadir_target
Definition: pg_rewind.c:53
bool dry_run
Definition: pg_rewind.c:59
void fsync_pgdata(const char *pg_data, int serverVersion)
Definition: file_utils.c:58
bool do_sync
Definition: pg_rewind.c:60

◆ usage()

static void usage ( const char *  progname)
static

Definition at line 72 of file pg_rewind.c.

References _, and printf.

Referenced by main().

73 {
74  printf(_("%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"), progname);
75  printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
76  printf(_("Options:\n"));
77  printf(_(" -D, --target-pgdata=DIRECTORY existing data directory to modify\n"));
78  printf(_(" --source-pgdata=DIRECTORY source data directory to synchronize with\n"));
79  printf(_(" --source-server=CONNSTR source server to synchronize with\n"));
80  printf(_(" -R, --write-recovery-conf write configuration for replication\n"
81  " (requires --source-server)\n"));
82  printf(_(" -n, --dry-run stop before modifying anything\n"));
83  printf(_(" -N, --no-sync do not wait for changes to be written\n"
84  " safely to disk\n"));
85  printf(_(" -P, --progress write progress messages\n"));
86  printf(_(" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
87  printf(_(" --debug write a lot of debug messages\n"));
88  printf(_(" -V, --version output version information, then exit\n"));
89  printf(_(" -?, --help show this help, then exit\n"));
90  printf(_("\nReport bugs to <pgsql-bugs@lists.postgresql.org>.\n"));
91 }
#define printf(...)
Definition: port.h:198
#define _(x)
Definition: elog.c:87
const char * progname
Definition: pg_rewind.c:49

Variable Documentation

◆ connstr_source

char* connstr_source = NULL

Definition at line 55 of file pg_rewind.c.

Referenced by main().

◆ ControlFile_source

ControlFileData ControlFile_source
static

Definition at line 47 of file pg_rewind.c.

◆ ControlFile_target

ControlFileData ControlFile_target
static

Definition at line 46 of file pg_rewind.c.

◆ datadir_source

char* datadir_source = NULL

◆ datadir_target

◆ debug

bool debug = false
static

Definition at line 57 of file pg_rewind.c.

Referenced by getTimelineHistory(), and main().

◆ do_sync

bool do_sync = true

Definition at line 60 of file pg_rewind.c.

Referenced by main(), and syncTargetDirectory().

◆ dry_run

◆ fetch_done

uint64 fetch_done

Definition at line 68 of file pg_rewind.c.

Referenced by main(), progress_report(), and write_target_range().

◆ fetch_size

uint64 fetch_size

Definition at line 67 of file pg_rewind.c.

Referenced by main(), postgres_fdw_validator(), and progress_report().

◆ progname

const char* progname

Definition at line 49 of file pg_rewind.c.

Referenced by ensureCleanShutdown(), and main().

◆ showprogress

bool showprogress = false

Definition at line 58 of file pg_rewind.c.

Referenced by main(), and progress_report().

◆ targetHistory

TimeLineHistoryEntry* targetHistory

Definition at line 63 of file pg_rewind.c.

Referenced by SimpleXLogPageRead().

◆ targetNentries

int targetNentries

◆ WalSegSz

int WalSegSz

Definition at line 50 of file pg_rewind.c.

Referenced by createBackupLabel(), and digestControlFile().