46 const char *content,
size_t size);
94 printf(
_(
"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"),
progname);
97 printf(
_(
" -c, --restore-target-wal use \"restore_command\" in target configuration to\n"
98 " retrieve WAL files from archives\n"));
99 printf(
_(
" -D, --target-pgdata=DIRECTORY existing data directory to modify\n"));
100 printf(
_(
" --source-pgdata=DIRECTORY source data directory to synchronize with\n"));
101 printf(
_(
" --source-server=CONNSTR source server to synchronize with\n"));
102 printf(
_(
" -n, --dry-run stop before modifying anything\n"));
103 printf(
_(
" -N, --no-sync do not wait for changes to be written\n"
104 " safely to disk\n"));
105 printf(
_(
" -P, --progress write progress messages\n"));
106 printf(
_(
" -R, --write-recovery-conf write configuration for replication\n"
107 " (requires --source-server)\n"));
108 printf(
_(
" --config-file=FILENAME use specified main server configuration\n"
109 " file when running target cluster\n"));
110 printf(
_(
" --debug write a lot of debug messages\n"));
111 printf(
_(
" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
112 printf(
_(
" --sync-method=METHOD set method for syncing files to disk\n"));
113 printf(
_(
" -V, --version output version information, then exit\n"));
114 printf(
_(
" -?, --help show this help, then exit\n"));
115 printf(
_(
"\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
116 printf(
_(
"%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
123 static struct option long_options[] = {
143 int lastcommontliIndex;
152 bool no_ensure_shutdown =
false;
164 if (strcmp(argv[1],
"--help") == 0 || strcmp(argv[1],
"-?") == 0)
169 if (strcmp(argv[1],
"--version") == 0 || strcmp(argv[1],
"-V") == 0)
171 puts(
"pg_rewind (PostgreSQL) " PG_VERSION);
176 while ((
c =
getopt_long(argc, argv,
"cD:nNPR", long_options, &option_index)) != -1)
218 no_ensure_shutdown =
true;
239 pg_log_error(
"no source specified (--source-pgdata or --source-server)");
246 pg_log_error(
"only one of --source-pgdata or --source-server can be specified");
253 pg_log_error(
"no target data directory specified (--target-pgdata)");
260 pg_log_error(
"no source server information (--source-server) specified for --write-recovery-conf");
267 pg_log_error(
"too many command-line arguments (first is \"%s\")",
293 pg_fatal(
"could not read permissions of directory \"%s\": %m",
335 if (!no_ensure_shutdown &&
373 if (target_tli == source_tli)
375 pg_log_info(
"source and target cluster are on the same timeline");
376 rewind_needed =
false;
377 target_wal_endrec = 0;
394 &divergerec, &lastcommontliIndex);
396 pg_log_info(
"servers diverged at WAL location %X/%X on timeline %u",
404 pfree(sourceHistory);
428 target_wal_endrec = chkptendrec;
436 if (target_wal_endrec > divergerec)
438 rewind_needed =
true;
443 Assert(target_wal_endrec == divergerec);
445 rewind_needed =
false;
460 pg_log_info(
"rewinding from last common checkpoint at %X/%X on timeline %u",
504 pg_log_info(
"need to copy %lu MB (total source directory size is %lu MB)",
505 (
unsigned long) (filemap->
fetch_size / (1024 * 1024)),
506 (
unsigned long) (filemap->
total_size / (1024 * 1024)));
583 offset = blkno * BLCKSZ;
618 pg_fatal(
"no action decided for file \"%s\"", entry->
path);
650 pg_fatal(
"source system was modified while pg_rewind was running");
654 pg_log_info(
"creating backup label and updating control file");
704 pg_fatal(
"source system was in unexpected state at end of rewind");
736 pg_fatal(
"source and target clusters are from different systems");
744 pg_fatal(
"clusters are not compatible with this version of pg_rewind");
754 pg_fatal(
"target server needs to use either data checksums or \"wal_log_hints = on\"");
765 pg_fatal(
"target server must be shut down cleanly");
775 pg_fatal(
"source data directory must be shut down cleanly");
792 char fetch_done_str[32];
793 char fetch_size_str[32];
822 fprintf(stderr,
_(
"%*s/%s kB (%d%%) copied"),
823 (
int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
830 fputc((!finished && isatty(fileno(stderr))) ?
'\r' :
'\n', stderr);
895 for (
i = 0;
i < *nentries;
i++)
931 n =
Min(a_nentries, b_nentries);
932 for (
i = 0;
i < n;
i++)
934 if (a_history[
i].tli != b_history[
i].tli ||
935 a_history[
i].begin != b_history[
i].begin)
948 pg_fatal(
"could not find common ancestor of the source and target cluster's timelines");
974 stamp_time = time(NULL);
975 tmp = localtime(&stamp_time);
976 strftime(strfbuf,
sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z", tmp);
979 "START WAL LOCATION: %X/%X (file %s)\n"
980 "CHECKPOINT LOCATION: %X/%X\n"
981 "BACKUP METHOD: pg_rewind\n"
982 "BACKUP FROM: standby\n"
989 pg_fatal(
"backup label buffer too small");
1012 pg_fatal(
"unexpected control file CRC");
1024 pg_fatal(
"unexpected control file size %d, expected %d",
1035 "invalid WAL segment size in control file (%d bytes)",
1038 pg_log_error_detail(
"The WAL segment size must be a power of two between 1 MB and 1 GB.");
1065 postgres_exec_path);
1075 pg_fatal(
"program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
1078 pg_fatal(
"program \"%s\" was found by \"%s\" but was not the same version as %s",
1107 pg_fatal(
"could not read \"restore_command\" from target cluster");
1112 pg_fatal(
"\"restore_command\" is not set in the target cluster");
1114 pg_log_debug(
"using for rewind \"restore_command = \'%s\'\"",
1143 pg_fatal(
"program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
1146 pg_fatal(
"program \"%s\" was found by \"%s\" but was not the same version as %s",
1150 pg_log_info(
"executing \"%s\" for target server to complete crash recovery",
1186 if (system(postgres_cmd->
data) != 0)
1188 pg_log_error(
"postgres single-user mode in target cluster failed");
Datum now(PG_FUNCTION_ARGS)
#define PG_DATA_CHECKSUM_VERSION
#define ngettext(s, p, n)
#define Assert(condition)
#define PG_TEXTDOMAIN(domain)
#define CATALOG_VERSION_NO
int find_my_exec(const char *argv0, char *retpath)
char * pipe_read_line(char *cmd)
void set_pglocale_pgservice(const char *argv0, const char *app)
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
void update_controlfile(const char *DataDir, ControlFileData *ControlFile, bool do_sync)
bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
datapagemap_iterator_t * datapagemap_iterate(datapagemap_t *map)
char * PQerrorMessage(const PGconn *conn)
ConnStatusType PQstatus(const PGconn *conn)
void PQfinish(PGconn *conn)
PGconn * PQconnectdb(const char *conninfo)
char * pg_strdup(const char *in)
void * pg_malloc(size_t size)
void traverse_datadir(const char *datadir, process_file_callback_t callback)
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
void close_target_file(void)
void truncate_target_file(const char *path, off_t newsize)
void remove_target(file_entry_t *entry)
void sync_target_dir(void)
void create_target(file_entry_t *entry)
void open_target_file(const char *path, bool trunc)
void write_target_range(char *buf, off_t begin, size_t size)
bool GetDataDirectoryCreatePerm(const char *dataDir)
@ DATA_DIR_SYNC_METHOD_FSYNC
void process_source_file(const char *path, file_type_t type, size_t size, const char *link_target)
void print_filemap(filemap_t *filemap)
void process_target_file(const char *path, file_type_t type, size_t size, const char *link_target)
filemap_t * decide_file_actions(void)
void calculate_totals(filemap_t *filemap)
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
#define required_argument
static void const char fflush(stdout)
rewind_source * init_libpq_source(PGconn *conn)
rewind_source * init_local_source(const char *datadir)
void pg_logging_increase_verbosity(void)
void pg_logging_init(const char *argv0)
#define pg_log_error(...)
#define pg_log_error_hint(...)
#define pg_log_error_detail(...)
#define pg_log_debug(...)
void pfree(void *pointer)
bool parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
void extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex, XLogRecPtr endpoint, const char *restoreCommand)
void findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex, XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli, XLogRecPtr *lastchkptredo, const char *restoreCommand)
XLogRecPtr readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex, const char *restoreCommand)
static pg_time_t last_progress_report
static bool writerecoveryconf
#define PG_CONTROL_VERSION
@ DB_SHUTDOWNED_IN_RECOVERY
#define PG_CONTROL_FILE_SIZE
#define COMP_CRC32C(crc, data, len)
#define EQ_CRC32C(c1, c2)
PGDLLIMPORT char * optarg
static ControlFileData ControlFile_source
static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
static void usage(const char *progname)
static void sanityChecks(void)
static char * datadir_source
static void findCommonAncestorTimeline(TimeLineHistoryEntry *a_history, int a_nentries, TimeLineHistoryEntry *b_history, int b_nentries, XLogRecPtr *recptr, int *tliIndex)
static ControlFileData ControlFile_source_after
static char * restore_command
int main(int argc, char **argv)
static XLogRecPtr MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
static void ensureCleanShutdown(const char *argv0)
TimeLineHistoryEntry * targetHistory
static rewind_source * source
static ControlFileData ControlFile_target
void progress_report(bool finished)
static TimeLineHistoryEntry * getTimelineHistory(TimeLineID tli, bool is_source, int *nentries)
static void digestControlFile(ControlFileData *ControlFile, const char *content, size_t size)
static char * connstr_source
static void checkControlFile(ControlFileData *ControlFile)
static void getRestoreCommand(const char *argv0)
static char * config_file
DataDirSyncMethod sync_method
static const char * progname
static void perform_rewind(filemap_t *filemap, rewind_source *source, XLogRecPtr chkptrec, TimeLineID chkpttli, XLogRecPtr chkptredo)
static void disconnect_atexit(void)
TimeLineHistoryEntry * rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
const char * get_progname(const char *argv0)
#define PG_BACKEND_VERSIONSTR
size_t strlcpy(char *dst, const char *src, size_t siz)
PQExpBuffer createPQExpBuffer(void)
void destroyPQExpBuffer(PQExpBuffer str)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void WriteRecoveryConfig(PGconn *pgconn, const char *target_dir, PQExpBuffer contents)
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, const char *replication_slot, char *dbname)
void get_restricted_token(void)
static pg_noinline void Size size
int pg_strip_crlf(char *str)
void appendShellString(PQExpBuffer buf, const char *str)
TimeLineID ThisTimeLineID
uint32 pg_control_version
CheckPoint checkPointCopy
XLogRecPtr minRecoveryPoint
uint32 data_checksum_version
uint32 catalog_version_no
TimeLineID minRecoveryPointTLI
datapagemap_t target_pages_to_overwrite
file_entry_t * entries[FLEXIBLE_ARRAY_MEMBER]
void(* queue_fetch_file)(struct rewind_source *, const char *path, size_t len)
void(* traverse_files)(struct rewind_source *, process_file_callback_t callback)
void(* finish_fetch)(struct rewind_source *)
XLogRecPtr(* get_current_wal_insert_lsn)(struct rewind_source *)
void(* queue_fetch_range)(struct rewind_source *, const char *path, off_t offset, size_t len)
char *(* fetch_file)(struct rewind_source *, const char *path, size_t *filesize)
void(* destroy)(struct rewind_source *)
static ControlFileData * ControlFile
#define IsValidWalSegSize(size)
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)
static void XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
static void TLHistoryFilePath(char *path, TimeLineID tli)
#define LSN_FORMAT_ARGS(lsn)
#define XLogRecPtrIsInvalid(r)
#define InvalidXLogRecPtr