mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2026-06-21 01:34:15 +02:00
Merge branch 'issue_205' into issue_201
This commit is contained in:
+15
-5
@@ -944,7 +944,7 @@ setup_push_filelist(const char *archive_status_dir, const char *first_file,
|
||||
|
||||
/* get list of files from archive_status */
|
||||
status_files = parray_new();
|
||||
dir_list_file(status_files, archive_status_dir, false, false, false, 0, FIO_DB_HOST);
|
||||
dir_list_file(status_files, archive_status_dir, false, false, false, false, 0, FIO_DB_HOST);
|
||||
parray_qsort(status_files, pgFileCompareName);
|
||||
|
||||
for (i = 0; i < parray_num(status_files); i++)
|
||||
@@ -1387,15 +1387,16 @@ get_wal_file(const char *filename, const char *from_fullpath,
|
||||
*/
|
||||
if (fio_is_remote(FIO_BACKUP_HOST))
|
||||
{
|
||||
char *errmsg = NULL;
|
||||
/* get file via ssh */
|
||||
#ifdef HAVE_LIBZ
|
||||
/* If requested file is regular WAL segment, then try to open it with '.gz' suffix... */
|
||||
if (IsXLogFileName(filename))
|
||||
rc = fio_send_file_gz(from_fullpath_gz, to_fullpath, out, thread_num);
|
||||
rc = fio_send_file_gz(from_fullpath_gz, to_fullpath, out, NULL, &errmsg);
|
||||
if (rc == FILE_MISSING)
|
||||
#endif
|
||||
/* ... failing that, use uncompressed */
|
||||
rc = fio_send_file(from_fullpath, to_fullpath, out, thread_num);
|
||||
rc = fio_send_file(from_fullpath, to_fullpath, out, NULL, &errmsg);
|
||||
|
||||
/* When not in prefetch mode, try to use partial file */
|
||||
if (rc == FILE_MISSING && !prefetch_mode && IsXLogFileName(filename))
|
||||
@@ -1405,18 +1406,27 @@ get_wal_file(const char *filename, const char *from_fullpath,
|
||||
#ifdef HAVE_LIBZ
|
||||
/* '.gz.partial' goes first ... */
|
||||
snprintf(from_partial, sizeof(from_partial), "%s.gz.partial", from_fullpath);
|
||||
rc = fio_send_file_gz(from_partial, to_fullpath, out, thread_num);
|
||||
rc = fio_send_file_gz(from_partial, to_fullpath, out, NULL, &errmsg);
|
||||
if (rc == FILE_MISSING)
|
||||
#endif
|
||||
{
|
||||
/* ... failing that, use '.partial' */
|
||||
snprintf(from_partial, sizeof(from_partial), "%s.partial", from_fullpath);
|
||||
rc = fio_send_file(from_partial, to_fullpath, out, thread_num);
|
||||
rc = fio_send_file(from_partial, to_fullpath, out, NULL, &errmsg);
|
||||
}
|
||||
|
||||
if (rc == SEND_OK)
|
||||
src_partial = true;
|
||||
}
|
||||
|
||||
if (rc == WRITE_FAILED)
|
||||
elog(WARNING, "Thread [%d]: Cannot write to file '%s': %s",
|
||||
thread_num, to_fullpath, strerror(errno));
|
||||
|
||||
if (errmsg)
|
||||
elog(WARNING, "Thread [%d]: %s", thread_num, errmsg);
|
||||
|
||||
pg_free(errmsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+28
-26
@@ -80,7 +80,7 @@ static void backup_cleanup(bool fatal, void *userdata);
|
||||
|
||||
static void *backup_files(void *arg);
|
||||
|
||||
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync);
|
||||
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs);
|
||||
|
||||
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||
PGNodeInfo *nodeInfo, PGconn *backup_conn, PGconn *master_conn);
|
||||
@@ -129,7 +129,7 @@ backup_stopbackup_callback(bool fatal, void *userdata)
|
||||
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
|
||||
*/
|
||||
static void
|
||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync)
|
||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
|
||||
{
|
||||
int i;
|
||||
char database_path[MAXPGPATH];
|
||||
@@ -331,8 +331,12 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync)
|
||||
backup_files_list = parray_new();
|
||||
|
||||
/* list files with the logical path. omit $PGDATA */
|
||||
dir_list_file(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, 0, FIO_DB_HOST);
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
fio_list_dir(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, backup_logs, 0);
|
||||
else
|
||||
dir_list_file(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, backup_logs, 0, FIO_LOCAL_HOST);
|
||||
|
||||
/*
|
||||
* Get database_map (name to oid) for use in partial restore feature.
|
||||
@@ -345,11 +349,19 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync)
|
||||
* from external directory option
|
||||
*/
|
||||
if (external_dirs)
|
||||
{
|
||||
for (i = 0; i < parray_num(external_dirs); i++)
|
||||
{
|
||||
/* External dirs numeration starts with 1.
|
||||
* 0 value is not external dir */
|
||||
dir_list_file(backup_files_list, parray_get(external_dirs, i),
|
||||
false, true, false, i+1, FIO_DB_HOST);
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
fio_list_dir(backup_files_list, parray_get(external_dirs, i),
|
||||
false, true, false, false, i+1);
|
||||
else
|
||||
dir_list_file(backup_files_list, parray_get(external_dirs, i),
|
||||
false, true, false, false, i+1, FIO_LOCAL_HOST);
|
||||
}
|
||||
}
|
||||
|
||||
/* close ssh session in main thread */
|
||||
fio_disconnect();
|
||||
@@ -551,19 +563,6 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync)
|
||||
elog(ERROR, "Data files transferring failed, time elapsed: %s",
|
||||
pretty_time);
|
||||
|
||||
/* Remove disappeared during backup files from backup_list */
|
||||
for (i = 0; i < parray_num(backup_files_list); i++)
|
||||
{
|
||||
pgFile *tmp_file = (pgFile *) parray_get(backup_files_list, i);
|
||||
|
||||
if (tmp_file->write_size == FILE_NOT_FOUND)
|
||||
{
|
||||
pgFileFree(tmp_file);
|
||||
parray_remove(backup_files_list, i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
/* clean previous backup file list */
|
||||
if (prev_backup_filelist)
|
||||
{
|
||||
@@ -616,7 +615,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync)
|
||||
/* Scan backup PG_XLOG_DIR */
|
||||
xlog_files_list = parray_new();
|
||||
join_path_components(pg_xlog_path, database_path, PG_XLOG_DIR);
|
||||
dir_list_file(xlog_files_list, pg_xlog_path, false, true, false, 0,
|
||||
dir_list_file(xlog_files_list, pg_xlog_path, false, true, false, false, 0,
|
||||
FIO_BACKUP_HOST);
|
||||
|
||||
/* TODO: Drop streamed WAL segments greater than stop_lsn */
|
||||
@@ -802,8 +801,8 @@ pgdata_basic_setup(ConnectionOptions conn_opt, PGNodeInfo *nodeInfo)
|
||||
* Entry point of pg_probackup BACKUP subcommand.
|
||||
*/
|
||||
int
|
||||
do_backup(time_t start_time, bool no_validate,
|
||||
pgSetBackupParams *set_backup_params, bool no_sync)
|
||||
do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||
bool no_validate, bool no_sync, bool backup_logs)
|
||||
{
|
||||
PGconn *backup_conn = NULL;
|
||||
PGNodeInfo nodeInfo;
|
||||
@@ -902,7 +901,7 @@ do_backup(time_t start_time, bool no_validate,
|
||||
add_note(¤t, set_backup_params->note);
|
||||
|
||||
/* backup data */
|
||||
do_backup_instance(backup_conn, &nodeInfo, no_sync);
|
||||
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs);
|
||||
pgut_atexit_pop(backup_cleanup, NULL);
|
||||
|
||||
/* compute size of wal files of this backup stored in the archive */
|
||||
@@ -2146,6 +2145,12 @@ backup_files(void *arg)
|
||||
current.backup_mode, current.parent_backup, true);
|
||||
}
|
||||
|
||||
/* No point in storing empty, missing or not changed files */
|
||||
if (file->write_size <= 0)
|
||||
unlink(to_fullpath);
|
||||
// elog(ERROR, "Cannot remove file \"%s\": %s", to_fullpath,
|
||||
// strerror(errno));
|
||||
|
||||
if (file->write_size == FILE_NOT_FOUND)
|
||||
continue;
|
||||
|
||||
@@ -2188,11 +2193,8 @@ parse_filelist_filenames(parray *files, const char *root)
|
||||
while (i < parray_num(files))
|
||||
{
|
||||
pgFile *file = (pgFile *) parray_get(files, i);
|
||||
// char *relative;
|
||||
int sscanf_result;
|
||||
|
||||
// relative = GetRelativePath(file->rel_path, root);
|
||||
|
||||
if (S_ISREG(file->mode) &&
|
||||
path_is_prefix_of_path(PG_TBLSPC_DIR, file->rel_path))
|
||||
{
|
||||
|
||||
+6
-1
@@ -878,7 +878,7 @@ catalog_get_timelines(InstanceConfig *instance)
|
||||
|
||||
/* read all xlog files that belong to this archive */
|
||||
sprintf(arclog_path, "%s/%s/%s", backup_path, "wal", instance->name);
|
||||
dir_list_file(xlog_files_list, arclog_path, false, false, false, 0, FIO_BACKUP_HOST);
|
||||
dir_list_file(xlog_files_list, arclog_path, false, false, false, false, 0, FIO_BACKUP_HOST);
|
||||
parray_qsort(xlog_files_list, pgFileCompareName);
|
||||
|
||||
timelineinfos = parray_new();
|
||||
@@ -1772,6 +1772,7 @@ pgBackupWriteControl(FILE *out, pgBackup *backup)
|
||||
|
||||
/*
|
||||
* Save the backup content into BACKUP_CONTROL_FILE.
|
||||
* TODO: honor the strict flag
|
||||
*/
|
||||
void
|
||||
write_backup(pgBackup *backup, bool strict)
|
||||
@@ -1856,6 +1857,10 @@ write_backup_filelist(pgBackup *backup, parray *files, const char *root,
|
||||
char line[BLCKSZ];
|
||||
pgFile *file = (pgFile *) parray_get(files, i);
|
||||
|
||||
/* Ignore disappeared file */
|
||||
if (file->write_size == FILE_NOT_FOUND)
|
||||
continue;
|
||||
|
||||
if (S_ISDIR(file->mode))
|
||||
{
|
||||
backup_size_on_disk += 4096;
|
||||
|
||||
+2
-2
@@ -207,8 +207,8 @@ do_block_validation(char *pgdata, uint32 checksum_version)
|
||||
files_list = parray_new();
|
||||
|
||||
/* list files with the logical path. omit $PGDATA */
|
||||
dir_list_file(files_list, pgdata,
|
||||
true, true, false, 0, FIO_DB_HOST);
|
||||
dir_list_file(files_list, pgdata, true, true,
|
||||
false, false, 0, FIO_DB_HOST);
|
||||
|
||||
/*
|
||||
* Sort pathname ascending.
|
||||
|
||||
+181
-150
@@ -199,44 +199,44 @@ void
|
||||
get_header_errormsg(Page page, char **errormsg)
|
||||
{
|
||||
PageHeader phdr = (PageHeader) page;
|
||||
*errormsg = pgut_malloc(MAXPGPATH);
|
||||
*errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
|
||||
if (PageGetPageSize(phdr) != BLCKSZ)
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"page size %lu is not equal to block size %u",
|
||||
PageGetPageSize(phdr), BLCKSZ);
|
||||
|
||||
else if (phdr->pd_lower < SizeOfPageHeaderData)
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"pd_lower %i is less than page header size %lu",
|
||||
phdr->pd_lower, SizeOfPageHeaderData);
|
||||
|
||||
else if (phdr->pd_lower > phdr->pd_upper)
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"pd_lower %u is greater than pd_upper %u",
|
||||
phdr->pd_lower, phdr->pd_upper);
|
||||
|
||||
else if (phdr->pd_upper > phdr->pd_special)
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"pd_upper %u is greater than pd_special %u",
|
||||
phdr->pd_upper, phdr->pd_special);
|
||||
|
||||
else if (phdr->pd_special > BLCKSZ)
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"pd_special %u is greater than block size %u",
|
||||
phdr->pd_special, BLCKSZ);
|
||||
|
||||
else if (phdr->pd_special != MAXALIGN(phdr->pd_special))
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"pd_special %i is misaligned, expected %lu",
|
||||
phdr->pd_special, MAXALIGN(phdr->pd_special));
|
||||
|
||||
else if (phdr->pd_flags & ~PD_VALID_FLAG_BITS)
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid, "
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid, "
|
||||
"pd_flags mask contain illegal bits");
|
||||
|
||||
else
|
||||
snprintf(*errormsg, MAXPGPATH, "page header invalid");
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN, "page header invalid");
|
||||
}
|
||||
|
||||
/* We know that checksumms are mismatched, store specific
|
||||
@@ -246,9 +246,9 @@ void
|
||||
get_checksum_errormsg(Page page, char **errormsg, BlockNumber absolute_blkno)
|
||||
{
|
||||
PageHeader phdr = (PageHeader) page;
|
||||
*errormsg = pgut_malloc(MAXPGPATH);
|
||||
*errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
|
||||
snprintf(*errormsg, MAXPGPATH,
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN,
|
||||
"page verification failed, "
|
||||
"calculated checksum %u but expected %u",
|
||||
phdr->pd_checksum,
|
||||
@@ -554,8 +554,8 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
CompressAlg calg, int clevel, uint32 checksum_version,
|
||||
int ptrack_version_num, const char *ptrack_schema, bool missing_ok)
|
||||
{
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
FILE *in = NULL;
|
||||
FILE *out = NULL;
|
||||
BlockNumber blknum = 0;
|
||||
BlockNumber nblocks = 0; /* number of blocks in source file */
|
||||
BlockNumber n_blocks_skipped = 0;
|
||||
@@ -606,33 +606,6 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
file->uncompressed_size = 0;
|
||||
INIT_FILE_CRC32(true, file->crc);
|
||||
|
||||
/* open source file for read */
|
||||
in = fio_fopen(from_fullpath, PG_BINARY_R, FIO_DB_HOST);
|
||||
if (in == NULL)
|
||||
{
|
||||
FIN_FILE_CRC32(true, file->crc);
|
||||
|
||||
/*
|
||||
* If file is not found, this is not en error.
|
||||
* It could have been deleted by concurrent postgres transaction.
|
||||
*/
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (missing_ok)
|
||||
{
|
||||
elog(LOG, "File \"%s\" is not found", from_fullpath);
|
||||
file->write_size = FILE_NOT_FOUND;
|
||||
return;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "File \"%s\" is not found", from_fullpath);
|
||||
}
|
||||
|
||||
/* In all other cases throw an error */
|
||||
elog(ERROR, "Cannot open file \"%s\": %s",
|
||||
from_fullpath, strerror(errno));
|
||||
}
|
||||
|
||||
/* open backup file for write */
|
||||
out = fopen(to_fullpath, PG_BINARY_W);
|
||||
if (out == NULL)
|
||||
@@ -661,33 +634,17 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
else
|
||||
use_pagemap = true;
|
||||
|
||||
if (!fio_is_remote_file(in))
|
||||
{
|
||||
/* enable stdio buffering for local input file,
|
||||
* unless the pagemap is involved, which
|
||||
* imply a lot of random access.
|
||||
*/
|
||||
if (use_pagemap)
|
||||
setvbuf(in, NULL, _IONBF, BUFSIZ);
|
||||
else
|
||||
{
|
||||
in_buf = pgut_malloc(STDIO_BUFSIZE);
|
||||
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* enable stdio buffering for output file */
|
||||
out_buf = pgut_malloc(STDIO_BUFSIZE);
|
||||
setvbuf(out, out_buf, _IOFBF, STDIO_BUFSIZE);
|
||||
|
||||
/* Remote mode */
|
||||
if (fio_is_remote_file(in))
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
{
|
||||
char *errmsg = NULL;
|
||||
BlockNumber err_blknum = 0;
|
||||
|
||||
/* TODO: retrying via ptrack should be implemented on the agent */
|
||||
int rc = fio_send_pages(in, out, file,
|
||||
int rc = fio_send_pages(out, from_fullpath, file,
|
||||
/* send prev backup START_LSN */
|
||||
backup_mode == BACKUP_MODE_DIFF_DELTA &&
|
||||
file->exists_in_prev ? prev_backup_start_lsn : InvalidXLogRecPtr,
|
||||
@@ -698,9 +655,16 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
&err_blknum, &errmsg);
|
||||
|
||||
/* check for errors */
|
||||
if (rc == REMOTE_ERROR)
|
||||
elog(ERROR, "Cannot read block %u of \"%s\": %s",
|
||||
err_blknum, from_fullpath, strerror(errno));
|
||||
if (rc == FILE_MISSING)
|
||||
{
|
||||
elog(LOG, "File \"%s\" is not found", from_fullpath);
|
||||
file->write_size = FILE_NOT_FOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
else if (rc == WRITE_FAILED)
|
||||
elog(ERROR, "Cannot write block %u of \"%s\": %s",
|
||||
err_blknum, to_fullpath, strerror(errno));
|
||||
|
||||
else if (rc == PAGE_CORRUPTION)
|
||||
{
|
||||
@@ -711,10 +675,21 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
elog(ERROR, "Corruption detected in file \"%s\", block %u",
|
||||
from_fullpath, err_blknum);
|
||||
}
|
||||
|
||||
else if (rc == WRITE_FAILED)
|
||||
elog(ERROR, "Cannot write block %u of \"%s\": %s",
|
||||
err_blknum, to_fullpath, strerror(errno));
|
||||
/* OPEN_FAILED and READ_FAILED */
|
||||
else if (rc == OPEN_FAILED)
|
||||
{
|
||||
if (errmsg)
|
||||
elog(ERROR, "%s", errmsg);
|
||||
else
|
||||
elog(ERROR, "Failed to open for reading remote file \"%s\"", from_fullpath);
|
||||
}
|
||||
else if (rc == READ_FAILED)
|
||||
{
|
||||
if (errmsg)
|
||||
elog(ERROR, "%s", errmsg);
|
||||
else
|
||||
elog(ERROR, "Failed to read from remote file \"%s\"", from_fullpath);
|
||||
}
|
||||
|
||||
file->read_size = rc * BLCKSZ;
|
||||
pg_free(errmsg);
|
||||
@@ -723,10 +698,47 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
/* Local mode */
|
||||
else
|
||||
{
|
||||
/* open source file for read */
|
||||
in = fopen(from_fullpath, PG_BINARY_R);
|
||||
if (in == NULL)
|
||||
{
|
||||
/*
|
||||
* If file is not found, this is not en error.
|
||||
* It could have been deleted by concurrent postgres transaction.
|
||||
*/
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (missing_ok)
|
||||
{
|
||||
elog(LOG, "File \"%s\" is not found", from_fullpath);
|
||||
file->write_size = FILE_NOT_FOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "File \"%s\" is not found", from_fullpath);
|
||||
}
|
||||
|
||||
/* In all other cases throw an error */
|
||||
elog(ERROR, "Cannot open file \"%s\": %s",
|
||||
from_fullpath, strerror(errno));
|
||||
}
|
||||
|
||||
/* Enable stdio buffering for local input file,
|
||||
* unless the pagemap is involved, which
|
||||
* imply a lot of random access.
|
||||
*/
|
||||
|
||||
if (use_pagemap)
|
||||
{
|
||||
iter = datapagemap_iterate(&file->pagemap);
|
||||
datapagemap_next(iter, &blknum); /* set first block */
|
||||
|
||||
setvbuf(in, NULL, _IONBF, BUFSIZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
in_buf = pgut_malloc(STDIO_BUFSIZE);
|
||||
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
|
||||
}
|
||||
|
||||
while (blknum < nblocks)
|
||||
@@ -775,14 +787,6 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
backup_mode == BACKUP_MODE_DIFF_DELTA)
|
||||
file->n_blocks = file->read_size / BLCKSZ;
|
||||
|
||||
if (fclose(out))
|
||||
elog(ERROR, "Cannot close the backup file \"%s\": %s",
|
||||
to_fullpath, strerror(errno));
|
||||
|
||||
fio_fclose(in);
|
||||
|
||||
FIN_FILE_CRC32(true, file->crc);
|
||||
|
||||
/* Determine that file didn`t changed in case of incremental backup */
|
||||
if (backup_mode != BACKUP_MODE_FULL &&
|
||||
file->exists_in_prev &&
|
||||
@@ -792,15 +796,19 @@ backup_data_file(ConnectionArgs* conn_arg, pgFile *file,
|
||||
file->write_size = BYTES_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* No point in storing empty files.
|
||||
*/
|
||||
if (file->write_size <= 0)
|
||||
{
|
||||
if (unlink(to_fullpath) == -1)
|
||||
elog(ERROR, "Cannot remove file \"%s\": %s", to_fullpath,
|
||||
strerror(errno));
|
||||
}
|
||||
cleanup:
|
||||
/* finish CRC calculation */
|
||||
FIN_FILE_CRC32(true, file->crc);
|
||||
|
||||
/* close local input file */
|
||||
if (in && fclose(in))
|
||||
elog(ERROR, "Cannot close the source file \"%s\": %s",
|
||||
to_fullpath, strerror(errno));
|
||||
|
||||
/* close local output file */
|
||||
if (out && fclose(out))
|
||||
elog(ERROR, "Cannot close the backup file \"%s\": %s",
|
||||
to_fullpath, strerror(errno));
|
||||
|
||||
pg_free(in_buf);
|
||||
pg_free(out_buf);
|
||||
@@ -1265,43 +1273,18 @@ backup_non_data_file_internal(const char *from_fullpath,
|
||||
const char *to_fullpath, pgFile *file,
|
||||
bool missing_ok)
|
||||
{
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
FILE *in = NULL;
|
||||
FILE *out = NULL;
|
||||
ssize_t read_len = 0;
|
||||
char *buf;
|
||||
pg_crc32 crc;
|
||||
char *buf = NULL;
|
||||
|
||||
INIT_FILE_CRC32(true, crc);
|
||||
INIT_FILE_CRC32(true, file->crc);
|
||||
|
||||
/* reset size summary */
|
||||
file->read_size = 0;
|
||||
file->write_size = 0;
|
||||
file->uncompressed_size = 0;
|
||||
|
||||
/* open source file for read */
|
||||
in = fio_fopen(from_fullpath, PG_BINARY_R, from_location);
|
||||
if (in == NULL)
|
||||
{
|
||||
FIN_FILE_CRC32(true, crc);
|
||||
file->crc = crc;
|
||||
|
||||
/* maybe deleted, it's not error in case of backup */
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (missing_ok)
|
||||
{
|
||||
elog(LOG, "File \"%s\" is not found", from_fullpath);
|
||||
file->write_size = FILE_NOT_FOUND;
|
||||
return;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "File \"%s\" is not found", from_fullpath);
|
||||
}
|
||||
|
||||
elog(ERROR, "Cannot open source file \"%s\": %s", from_fullpath,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
/* open backup file for write */
|
||||
out = fopen(to_fullpath, PG_BINARY_W);
|
||||
if (out == NULL)
|
||||
@@ -1313,60 +1296,108 @@ backup_non_data_file_internal(const char *from_fullpath,
|
||||
elog(ERROR, "Cannot change mode of \"%s\": %s", to_fullpath,
|
||||
strerror(errno));
|
||||
|
||||
/* disable stdio buffering for local input/output files */
|
||||
if (!fio_is_remote_file(in))
|
||||
setvbuf(in, NULL, _IONBF, BUFSIZ);
|
||||
setvbuf(out, NULL, _IONBF, BUFSIZ);
|
||||
|
||||
/* allocate 64kB buffer */
|
||||
buf = pgut_malloc(STDIO_BUFSIZE);
|
||||
|
||||
/* copy content and calc CRC */
|
||||
for (;;)
|
||||
/* backup remote file */
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
{
|
||||
read_len = fio_fread(in, buf, STDIO_BUFSIZE);
|
||||
char *errmsg = NULL;
|
||||
int rc = fio_send_file(from_fullpath, to_fullpath, out, file, &errmsg);
|
||||
|
||||
if (read_len < 0)
|
||||
elog(ERROR, "Cannot read from source file \"%s\": %s",
|
||||
from_fullpath, strerror(errno));
|
||||
/* handle errors */
|
||||
if (rc == FILE_MISSING)
|
||||
{
|
||||
/* maybe deleted, it's not error in case of backup */
|
||||
if (missing_ok)
|
||||
{
|
||||
elog(LOG, "File \"%s\" is not found", from_fullpath);
|
||||
file->write_size = FILE_NOT_FOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "File \"%s\" is not found", from_fullpath);
|
||||
}
|
||||
else if (rc == WRITE_FAILED)
|
||||
elog(ERROR, "Cannot write to \"%s\": %s", to_fullpath, strerror(errno));
|
||||
else if (rc != SEND_OK)
|
||||
{
|
||||
if (errmsg)
|
||||
elog(ERROR, "%s", errmsg);
|
||||
else
|
||||
elog(ERROR, "Cannot access remote file \"%s\"", from_fullpath);
|
||||
}
|
||||
|
||||
if (read_len == 0)
|
||||
break;
|
||||
pg_free(errmsg);
|
||||
}
|
||||
/* backup local file */
|
||||
else
|
||||
{
|
||||
/* open source file for read */
|
||||
in = fopen(from_fullpath, PG_BINARY_R);
|
||||
if (in == NULL)
|
||||
{
|
||||
/* maybe deleted, it's not error in case of backup */
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (missing_ok)
|
||||
{
|
||||
elog(LOG, "File \"%s\" is not found", from_fullpath);
|
||||
file->write_size = FILE_NOT_FOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "File \"%s\" is not found", from_fullpath);
|
||||
}
|
||||
|
||||
if (fwrite(buf, 1, read_len, out) != read_len)
|
||||
elog(ERROR, "Cannot write to \"%s\": %s", to_fullpath,
|
||||
elog(ERROR, "Cannot open file \"%s\": %s", from_fullpath,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
/* update CRC */
|
||||
COMP_FILE_CRC32(true, crc, buf, read_len);
|
||||
/* disable stdio buffering for local input/output files to avoid triple buffering */
|
||||
setvbuf(in, NULL, _IONBF, BUFSIZ);
|
||||
setvbuf(out, NULL, _IONBF, BUFSIZ);
|
||||
|
||||
file->read_size += read_len;
|
||||
/* allocate 64kB buffer */
|
||||
buf = pgut_malloc(CHUNK_SIZE);
|
||||
|
||||
// if (read_len < STDIO_BUFSIZE)
|
||||
// {
|
||||
// if (!fio_is_remote_file(in))
|
||||
// {
|
||||
// if (ferror(in))
|
||||
// elog(ERROR, "Cannot read from source file \"%s\": %s",
|
||||
// from_fullpath, strerror(errno));
|
||||
//
|
||||
// if (feof(in))
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
/* copy content and calc CRC */
|
||||
for (;;)
|
||||
{
|
||||
read_len = fread(buf, 1, CHUNK_SIZE, in);
|
||||
|
||||
if (ferror(in))
|
||||
elog(ERROR, "Cannot read from file \"%s\": %s",
|
||||
from_fullpath, strerror(errno));
|
||||
|
||||
if (read_len > 0)
|
||||
{
|
||||
if (fwrite(buf, 1, read_len, out) != read_len)
|
||||
elog(ERROR, "Cannot write to file \"%s\": %s", to_fullpath,
|
||||
strerror(errno));
|
||||
|
||||
/* update CRC */
|
||||
COMP_FILE_CRC32(true, file->crc, buf, read_len);
|
||||
file->read_size += read_len;
|
||||
}
|
||||
|
||||
if (feof(in))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
file->write_size = (int64) file->read_size;
|
||||
|
||||
if (file->write_size > 0)
|
||||
file->uncompressed_size = file->write_size;
|
||||
/* finish CRC calculation and store into pgFile */
|
||||
FIN_FILE_CRC32(true, crc);
|
||||
file->crc = crc;
|
||||
|
||||
if (fclose(out))
|
||||
elog(ERROR, "Cannot write \"%s\": %s", to_fullpath, strerror(errno));
|
||||
fio_fclose(in);
|
||||
cleanup:
|
||||
/* finish CRC calculation and store into pgFile */
|
||||
FIN_FILE_CRC32(true, file->crc);
|
||||
|
||||
if (in && fclose(in))
|
||||
elog(ERROR, "Cannot close the file \"%s\": %s", from_fullpath, strerror(errno));
|
||||
|
||||
if (out && fclose(out))
|
||||
elog(ERROR, "Cannot close the file \"%s\": %s", to_fullpath, strerror(errno));
|
||||
|
||||
pg_free(buf);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -750,7 +750,7 @@ delete_backup_files(pgBackup *backup)
|
||||
|
||||
/* list files to be deleted */
|
||||
files = parray_new();
|
||||
dir_list_file(files, backup->root_dir, false, true, true, 0, FIO_BACKUP_HOST);
|
||||
dir_list_file(files, backup->root_dir, false, false, true, false, 0, FIO_LOCAL_HOST);
|
||||
|
||||
/* delete leaf node first */
|
||||
parray_qsort(files, pgFileCompareRelPathWithExternalDesc);
|
||||
|
||||
@@ -513,7 +513,7 @@ db_map_entry_free(void *entry)
|
||||
*/
|
||||
void
|
||||
dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||
bool add_root, int external_dir_num, fio_location location)
|
||||
bool add_root, bool backup_logs, int external_dir_num, fio_location location)
|
||||
{
|
||||
pgFile *file;
|
||||
|
||||
@@ -527,6 +527,17 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup exclusion list for file search */
|
||||
if (!backup_logs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; pgdata_exclude_dir[i]; i++); /* find first empty slot */
|
||||
|
||||
/* Set 'pg_log' in first empty slot */
|
||||
pgdata_exclude_dir[i] = PG_LOG_DIR;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(file->mode))
|
||||
{
|
||||
if (external_dir_num > 0)
|
||||
|
||||
+1
-1
@@ -1055,7 +1055,7 @@ remove_dir_with_files(const char *path)
|
||||
int i;
|
||||
char full_path[MAXPGPATH];
|
||||
|
||||
dir_list_file(files, path, true, true, true, 0, FIO_LOCAL_HOST);
|
||||
dir_list_file(files, path, false, false, true, false, 0, FIO_LOCAL_HOST);
|
||||
parray_qsort(files, pgFileCompareRelPathWithExternalDesc);
|
||||
for (i = 0; i < parray_num(files); i++)
|
||||
{
|
||||
|
||||
+1
-12
@@ -668,17 +668,6 @@ main(int argc, char *argv[])
|
||||
if (instance_config.conn_opt.pguser != NULL)
|
||||
dbuser = pstrdup(instance_config.conn_opt.pguser);
|
||||
|
||||
/* setup exclusion list for file search */
|
||||
if (!backup_logs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; pgdata_exclude_dir[i]; i++); /* find first empty slot */
|
||||
|
||||
/* Set 'pg_log' in first empty slot */
|
||||
pgdata_exclude_dir[i] = PG_LOG_DIR;
|
||||
}
|
||||
|
||||
if (backup_subcmd == VALIDATE_CMD || backup_subcmd == RESTORE_CMD)
|
||||
{
|
||||
/*
|
||||
@@ -802,7 +791,7 @@ main(int argc, char *argv[])
|
||||
elog(ERROR, "required parameter not specified: BACKUP_MODE "
|
||||
"(-b, --backup-mode)");
|
||||
|
||||
return do_backup(start_time, no_validate, set_backup_params, no_sync);
|
||||
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs);
|
||||
}
|
||||
case RESTORE_CMD:
|
||||
return do_restore_or_validate(current.backup_id,
|
||||
|
||||
+17
-9
@@ -87,6 +87,10 @@ extern const char *PROGRAM_EMAIL;
|
||||
/* stdio buffer size */
|
||||
#define STDIO_BUFSIZE 65536
|
||||
|
||||
#define ERRMSG_MAX_LEN 2048
|
||||
#define CHUNK_SIZE (128 * 1024)
|
||||
#define OUT_BUF_SIZE (512 * 1024)
|
||||
|
||||
/* retry attempts */
|
||||
#define PAGE_READ_ATTEMPTS 100
|
||||
|
||||
@@ -168,9 +172,9 @@ typedef struct pgFile
|
||||
*/
|
||||
/* we need int64 here to store '-1' value */
|
||||
pg_crc32 crc; /* CRC value of the file, regular file only */
|
||||
char *rel_path; /* relative path of the file */
|
||||
char *linked; /* path of the linked file */
|
||||
bool is_datafile; /* true if the file is PostgreSQL data file */
|
||||
char *rel_path; /* relative path of the file */
|
||||
Oid tblspcOid; /* tblspcOid extracted from path, if applicable */
|
||||
Oid dbOid; /* dbOid extracted from path, if applicable */
|
||||
Oid relOid; /* relOid extracted from path, if applicable */
|
||||
@@ -178,7 +182,7 @@ typedef struct pgFile
|
||||
int segno; /* Segment number for ptrack */
|
||||
BlockNumber n_blocks; /* size of the data file in blocks */
|
||||
bool is_cfs; /* Flag to distinguish files compressed by CFS*/
|
||||
bool is_database;
|
||||
bool is_database; /* Flag used strictly by ptrack 1.x backup */
|
||||
int external_dir_num; /* Number of external directory. 0 if not external */
|
||||
bool exists_in_prev; /* Mark files, both data and regular, that exists in previous backup */
|
||||
CompressAlg compress_alg; /* compression algorithm applied to the file */
|
||||
@@ -690,8 +694,8 @@ extern char** commands_args;
|
||||
extern const char *pgdata_exclude_dir[];
|
||||
|
||||
/* in backup.c */
|
||||
extern int do_backup(time_t start_time, bool no_validate,
|
||||
pgSetBackupParams *set_backup_params, bool no_sync);
|
||||
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||
bool no_validate, bool no_sync, bool backup_logs);
|
||||
extern void do_checkdb(bool need_amcheck, ConnectionOptions conn_opt,
|
||||
char *pgdata);
|
||||
extern BackupMode parse_backup_mode(const char *value);
|
||||
@@ -855,7 +859,7 @@ extern const char* deparse_compress_alg(int alg);
|
||||
|
||||
/* in dir.c */
|
||||
extern void dir_list_file(parray *files, const char *root, bool exclude,
|
||||
bool follow_symlink, bool add_root,
|
||||
bool follow_symlink, bool add_root, bool backup_logs,
|
||||
int external_dir_num, fio_location location);
|
||||
|
||||
extern void create_data_directories(parray *dest_files,
|
||||
@@ -1023,13 +1027,17 @@ extern parray * pg_ptrack_get_pagemapset(PGconn *backup_conn, const char *ptrack
|
||||
int ptrack_version_num, XLogRecPtr lsn);
|
||||
|
||||
/* FIO */
|
||||
extern int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
extern int fio_send_pages(FILE* out, const char *from_fullpath, pgFile *file, XLogRecPtr horizonLsn,
|
||||
int calg, int clevel, uint32 checksum_version,
|
||||
datapagemap_t *pagemap, BlockNumber* err_blknum, char **errormsg);
|
||||
/* return codes for fio_send_pages */
|
||||
#define OUT_BUF_SIZE (512 * 1024)
|
||||
extern int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* out, int thread_num);
|
||||
extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out, int thread_num);
|
||||
extern int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
pgFile *file, char **errormsg);
|
||||
extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
pgFile *file, char **errormsg);
|
||||
|
||||
extern void fio_list_dir(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||
bool add_root, bool backup_logs, int external_dir_num);
|
||||
|
||||
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
|
||||
|
||||
|
||||
+364
-91
@@ -14,7 +14,6 @@
|
||||
|
||||
#define PRINTF_BUF_SIZE 1024
|
||||
#define FILE_PERMISSIONS 0600
|
||||
#define CHUNK_SIZE 1024 * 128
|
||||
|
||||
static __thread unsigned long fio_fdset = 0;
|
||||
static __thread void* fio_stdin_buffer;
|
||||
@@ -33,9 +32,38 @@ typedef struct
|
||||
int calg;
|
||||
int clevel;
|
||||
int bitmapsize;
|
||||
int path_len;
|
||||
} fio_send_request;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char path[MAXPGPATH];
|
||||
bool exclude;
|
||||
bool follow_symlink;
|
||||
bool add_root;
|
||||
bool backup_logs;
|
||||
bool exclusive_backup;
|
||||
int external_dir_num;
|
||||
} fio_list_dir_request;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
mode_t mode;
|
||||
size_t size;
|
||||
time_t mtime;
|
||||
bool is_datafile;
|
||||
bool is_database;
|
||||
Oid tblspcOid;
|
||||
Oid dbOid;
|
||||
Oid relOid;
|
||||
ForkName forkName;
|
||||
int segno;
|
||||
int external_dir_num;
|
||||
int linked_len;
|
||||
} fio_pgFile;
|
||||
|
||||
|
||||
/* Convert FIO pseudo handle to index in file descriptor array */
|
||||
#define fio_fileno(f) (((size_t)f - 1) | FIO_PIPE_MARKER)
|
||||
|
||||
@@ -1316,9 +1344,11 @@ static void fio_load_file(int out, char const* path)
|
||||
* Return number of actually(!) readed blocks, attempts or
|
||||
* half-readed block are not counted.
|
||||
* Return values in case of error:
|
||||
* REMOTE_ERROR
|
||||
* PAGE_CORRUPTION
|
||||
* WRITE_FAILED
|
||||
* FILE_MISSING
|
||||
* OPEN_FAILED
|
||||
* READ_ERROR
|
||||
* PAGE_CORRUPTION
|
||||
* WRITE_FAILED
|
||||
*
|
||||
* If none of the above, this function return number of blocks
|
||||
* readed by remote agent.
|
||||
@@ -1326,7 +1356,7 @@ static void fio_load_file(int out, char const* path)
|
||||
* In case of DELTA mode horizonLsn must be a valid lsn,
|
||||
* otherwise it should be set to InvalidXLogRecPtr.
|
||||
*/
|
||||
int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
int fio_send_pages(FILE* out, const char *from_fullpath, pgFile *file, XLogRecPtr horizonLsn,
|
||||
int calg, int clevel, uint32 checksum_version,
|
||||
datapagemap_t *pagemap, BlockNumber* err_blknum,
|
||||
char **errormsg)
|
||||
@@ -1338,22 +1368,19 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
BlockNumber n_blocks_read = 0;
|
||||
BlockNumber blknum = 0;
|
||||
|
||||
Assert(fio_is_remote_file(in));
|
||||
|
||||
/* send message with header
|
||||
|
||||
8bytes 20bytes var
|
||||
------------------------------------------------------
|
||||
| fio_header | fio_send_request | BITMAP(if any) |
|
||||
------------------------------------------------------
|
||||
8bytes 24bytes var var
|
||||
--------------------------------------------------------------
|
||||
| fio_header | fio_send_request | FILE PATH | BITMAP(if any) |
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
req.hdr.handle = fio_fileno(in) & ~FIO_PIPE_MARKER;
|
||||
req.hdr.cop = FIO_SEND_PAGES;
|
||||
|
||||
if (pagemap)
|
||||
{
|
||||
req.hdr.cop = FIO_SEND_PAGES_PAGEMAP;
|
||||
req.hdr.size = sizeof(fio_send_request) + pagemap->bitmapsize;
|
||||
req.hdr.size = sizeof(fio_send_request) + pagemap->bitmapsize + strlen(from_fullpath) + 1;
|
||||
req.arg.bitmapsize = pagemap->bitmapsize;
|
||||
|
||||
/* TODO: add optimization for the case of pagemap
|
||||
@@ -1363,8 +1390,8 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
}
|
||||
else
|
||||
{
|
||||
req.hdr.cop = FIO_SEND_PAGES;
|
||||
req.hdr.size = sizeof(fio_send_request);
|
||||
req.hdr.size = sizeof(fio_send_request) + strlen(from_fullpath) + 1;
|
||||
req.arg.bitmapsize = 0;
|
||||
}
|
||||
|
||||
req.arg.nblocks = file->size/BLCKSZ;
|
||||
@@ -1373,6 +1400,7 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
req.arg.checksumVersion = checksum_version;
|
||||
req.arg.calg = calg;
|
||||
req.arg.clevel = clevel;
|
||||
req.arg.path_len = strlen(from_fullpath) + 1;
|
||||
|
||||
file->compress_alg = calg; /* TODO: wtf? why here? */
|
||||
|
||||
@@ -1385,10 +1413,14 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
// pg_free(iter);
|
||||
//<-----
|
||||
|
||||
/* send header */
|
||||
IO_CHECK(fio_write_all(fio_stdout, &req, sizeof(req)), sizeof(req));
|
||||
|
||||
/* send file path */
|
||||
IO_CHECK(fio_write_all(fio_stdout, from_fullpath, req.arg.path_len), req.arg.path_len);
|
||||
|
||||
/* send pagemap if any */
|
||||
if (pagemap)
|
||||
/* now send pagemap itself */
|
||||
IO_CHECK(fio_write_all(fio_stdout, pagemap->bitmap, pagemap->bitmapsize), pagemap->bitmapsize);
|
||||
|
||||
while (true)
|
||||
@@ -1402,9 +1434,15 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
|
||||
if (hdr.cop == FIO_ERROR)
|
||||
{
|
||||
errno = hdr.arg;
|
||||
*err_blknum = hdr.size;
|
||||
return REMOTE_ERROR;
|
||||
/* FILE_MISSING, OPEN_FAILED and READ_FAILED */
|
||||
if (hdr.size > 0)
|
||||
{
|
||||
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
|
||||
*errormsg = pgut_malloc(hdr.size);
|
||||
snprintf(*errormsg, hdr.size, "%s", buf);
|
||||
}
|
||||
|
||||
return hdr.arg;
|
||||
}
|
||||
else if (hdr.cop == FIO_SEND_FILE_CORRUPTION)
|
||||
{
|
||||
@@ -1414,7 +1452,7 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
{
|
||||
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
|
||||
*errormsg = pgut_malloc(hdr.size);
|
||||
strncpy(*errormsg, buf, hdr.size);
|
||||
snprintf(*errormsg, hdr.size, "%s", buf);
|
||||
}
|
||||
return PAGE_CORRUPTION;
|
||||
}
|
||||
@@ -1449,66 +1487,134 @@ int fio_send_pages(FILE* in, FILE* out, pgFile *file, XLogRecPtr horizonLsn,
|
||||
return n_blocks_read;
|
||||
}
|
||||
|
||||
/* TODO: read file using large buffer */
|
||||
static void fio_send_pages_impl(int fd, int out, char* buf, bool with_pagemap)
|
||||
{
|
||||
BlockNumber blknum = 0;
|
||||
BlockNumber n_blocks_read = 0;
|
||||
XLogRecPtr page_lsn = 0;
|
||||
char read_buffer[BLCKSZ+1];
|
||||
fio_header hdr;
|
||||
fio_send_request *req = (fio_send_request*) buf;
|
||||
/* TODO: read file using large buffer
|
||||
* Return codes:
|
||||
* FIO_ERROR:
|
||||
* FILE_MISSING (-1)
|
||||
* OPEN_FAILED (-2)
|
||||
* READ_FAILED (-3)
|
||||
|
||||
* FIO_SEND_FILE_CORRUPTION
|
||||
* FIO_SEND_FILE_EOF
|
||||
*/
|
||||
static void fio_send_pages_impl(int out, char* buf)
|
||||
{
|
||||
FILE *in = NULL;
|
||||
BlockNumber blknum = 0;
|
||||
BlockNumber n_blocks_read = 0;
|
||||
XLogRecPtr page_lsn = 0;
|
||||
char read_buffer[BLCKSZ+1];
|
||||
char in_buf[STDIO_BUFSIZE];
|
||||
fio_header hdr;
|
||||
fio_send_request *req = (fio_send_request*) buf;
|
||||
char *from_fullpath = (char*) buf + sizeof(fio_send_request);
|
||||
int current_pos = 0;
|
||||
bool with_pagemap = req->bitmapsize > 0 ? true : false;
|
||||
/* error reporting */
|
||||
char *errormsg = NULL;
|
||||
/* parse buffer */
|
||||
datapagemap_t *map = NULL;
|
||||
datapagemap_iterator_t *iter = NULL;
|
||||
|
||||
/* open source file */
|
||||
in = fopen(from_fullpath, PG_BINARY_R);
|
||||
if (!in)
|
||||
{
|
||||
hdr.cop = FIO_ERROR;
|
||||
|
||||
/* do not send exact wording of ENOENT error message
|
||||
* because it is a very common error in our case, so
|
||||
* error code is enough.
|
||||
*/
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
hdr.arg = FILE_MISSING;
|
||||
hdr.size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hdr.arg = OPEN_FAILED;
|
||||
errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
/* Construct the error message */
|
||||
snprintf(errormsg, ERRMSG_MAX_LEN, "Cannot open source file '%s': %s",
|
||||
from_fullpath, strerror(errno));
|
||||
hdr.size = strlen(errormsg) + 1;
|
||||
}
|
||||
|
||||
/* send header and message */
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
if (errormsg)
|
||||
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (with_pagemap)
|
||||
{
|
||||
map = pgut_malloc(sizeof(datapagemap_t));
|
||||
map->bitmapsize = req->bitmapsize;
|
||||
map->bitmap = (char*) buf + sizeof(fio_send_request);
|
||||
map->bitmap = (char*) buf + sizeof(fio_send_request) + req->path_len;
|
||||
|
||||
/* get first block */
|
||||
iter = datapagemap_iterate(map);
|
||||
datapagemap_next(iter, &blknum);
|
||||
}
|
||||
|
||||
hdr.cop = FIO_PAGE;
|
||||
setvbuf(in, NULL, _IONBF, BUFSIZ);
|
||||
}
|
||||
else
|
||||
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
|
||||
|
||||
/* TODO: what is this barrier for? */
|
||||
read_buffer[BLCKSZ] = 1; /* barrier */
|
||||
|
||||
while (blknum < req->nblocks)
|
||||
{
|
||||
int rc = 0;
|
||||
int retry_attempts = PAGE_READ_ATTEMPTS;
|
||||
int rc = 0;
|
||||
size_t read_len = 0;
|
||||
int retry_attempts = PAGE_READ_ATTEMPTS;
|
||||
|
||||
/* TODO: handle signals on the agent */
|
||||
if (interrupted)
|
||||
elog(ERROR, "Interrupted during remote page reading");
|
||||
|
||||
/* read page, check header and validate checksumms */
|
||||
/* TODO: libpq connection on the agent, so we can do ptrack
|
||||
* magic right here.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
ssize_t read_len = pread(fd, read_buffer, BLCKSZ, blknum*BLCKSZ);
|
||||
/* Optimize stdio buffer usage, fseek only when current position
|
||||
* does not match the position of requested block.
|
||||
*/
|
||||
if (current_pos != blknum*BLCKSZ)
|
||||
{
|
||||
current_pos = blknum*BLCKSZ;
|
||||
if (fseek(in, current_pos, SEEK_SET) != 0)
|
||||
elog(ERROR, "fseek to position %u is failed on remote file '%s': %s",
|
||||
current_pos, from_fullpath, strerror(errno));
|
||||
}
|
||||
|
||||
read_len = fread(read_buffer, 1, BLCKSZ, in);
|
||||
page_lsn = InvalidXLogRecPtr;
|
||||
|
||||
/* report eof */
|
||||
if (read_len == 0)
|
||||
goto eof;
|
||||
current_pos += BLCKSZ;
|
||||
|
||||
/* report error */
|
||||
else if (read_len < 0)
|
||||
if (ferror(in))
|
||||
{
|
||||
/* TODO: better to report exact error message, not errno */
|
||||
hdr.cop = FIO_ERROR;
|
||||
hdr.arg = errno;
|
||||
hdr.size = blknum;
|
||||
hdr.arg = READ_FAILED;
|
||||
|
||||
errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
/* Construct the error message */
|
||||
snprintf(errormsg, ERRMSG_MAX_LEN, "Cannot read block %u of '%s': %s",
|
||||
blknum, from_fullpath, strerror(errno));
|
||||
hdr.size = strlen(errormsg) + 1;
|
||||
|
||||
/* send header and message */
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
|
||||
goto cleanup;
|
||||
}
|
||||
else if (read_len == BLCKSZ)
|
||||
|
||||
if (read_len == BLCKSZ)
|
||||
{
|
||||
rc = validate_one_page(read_buffer, req->segmentno + blknum,
|
||||
InvalidXLogRecPtr, &page_lsn, req->checksumVersion);
|
||||
@@ -1519,6 +1625,9 @@ static void fio_send_pages_impl(int fd, int out, char* buf, bool with_pagemap)
|
||||
else if (rc == PAGE_IS_VALID)
|
||||
break;
|
||||
}
|
||||
|
||||
if (feof(in))
|
||||
goto eof;
|
||||
// else /* readed less than BLKSZ bytes, retry */
|
||||
|
||||
/* File is either has insane header or invalid checksum,
|
||||
@@ -1526,7 +1635,6 @@ static void fio_send_pages_impl(int fd, int out, char* buf, bool with_pagemap)
|
||||
*/
|
||||
if (--retry_attempts == 0)
|
||||
{
|
||||
char *errormsg = NULL;
|
||||
hdr.cop = FIO_SEND_FILE_CORRUPTION;
|
||||
hdr.arg = blknum;
|
||||
|
||||
@@ -1547,7 +1655,6 @@ static void fio_send_pages_impl(int fd, int out, char* buf, bool with_pagemap)
|
||||
if (errormsg)
|
||||
IO_CHECK(fio_write_all(out, errormsg, hdr.size), hdr.size);
|
||||
|
||||
pg_free(errormsg);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
@@ -1567,6 +1674,7 @@ static void fio_send_pages_impl(int fd, int out, char* buf, bool with_pagemap)
|
||||
BackupPageHeader* bph = (BackupPageHeader*)write_buffer;
|
||||
|
||||
/* compress page */
|
||||
hdr.cop = FIO_PAGE;
|
||||
hdr.arg = bph->block = blknum;
|
||||
hdr.size = sizeof(BackupPageHeader);
|
||||
|
||||
@@ -1608,6 +1716,9 @@ eof:
|
||||
cleanup:
|
||||
pg_free(map);
|
||||
pg_free(iter);
|
||||
pg_free(errormsg);
|
||||
if (in)
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1621,7 +1732,8 @@ cleanup:
|
||||
* ZLIB_ERROR (-5)
|
||||
* REMOTE_ERROR (-6)
|
||||
*/
|
||||
int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* out, int thread_num)
|
||||
int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
pgFile *file, char **errormsg)
|
||||
{
|
||||
fio_header hdr;
|
||||
int exit_code = SEND_OK;
|
||||
@@ -1634,8 +1746,8 @@ int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* o
|
||||
hdr.cop = FIO_SEND_FILE;
|
||||
hdr.size = path_len;
|
||||
|
||||
elog(VERBOSE, "Thread [%d]: Attempting to open remote compressed WAL file '%s'",
|
||||
thread_num, from_fullpath);
|
||||
// elog(VERBOSE, "Thread [%d]: Attempting to open remote compressed WAL file '%s'",
|
||||
// thread_num, from_fullpath);
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, from_fullpath, path_len), path_len);
|
||||
@@ -1655,7 +1767,8 @@ int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* o
|
||||
if (hdr.size > 0)
|
||||
{
|
||||
IO_CHECK(fio_read_all(fio_stdin, in_buf, hdr.size), hdr.size);
|
||||
elog(WARNING, "Thread [%d]: %s", thread_num, in_buf);
|
||||
*errormsg = pgut_malloc(hdr.size);
|
||||
snprintf(*errormsg, hdr.size, "%s", in_buf);
|
||||
}
|
||||
exit_code = hdr.arg;
|
||||
goto cleanup;
|
||||
@@ -1681,8 +1794,10 @@ int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* o
|
||||
|
||||
if (rc != Z_OK)
|
||||
{
|
||||
elog(WARNING, "Thread [%d]: Failed to initialize decompression stream for file '%s': %i: %s",
|
||||
thread_num, from_fullpath, rc, strm->msg);
|
||||
*errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN,
|
||||
"Failed to initialize decompression stream for file '%s': %i: %s",
|
||||
from_fullpath, rc, strm->msg);
|
||||
exit_code = ZLIB_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1714,8 +1829,10 @@ int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* o
|
||||
else if (rc != Z_OK)
|
||||
{
|
||||
/* got an error */
|
||||
elog(WARNING, "Thread [%d]: Decompression failed for file '%s': %i: %s",
|
||||
thread_num, from_fullpath, rc, strm->msg);
|
||||
*errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
snprintf(*errormsg, ERRMSG_MAX_LEN,
|
||||
"Decompression failed for file '%s': %i: %s",
|
||||
from_fullpath, rc, strm->msg);
|
||||
exit_code = ZLIB_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1725,8 +1842,6 @@ int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* o
|
||||
/* Output buffer is full, write it out */
|
||||
if (fwrite(out_buf, 1, OUT_BUF_SIZE, out) != OUT_BUF_SIZE)
|
||||
{
|
||||
elog(WARNING, "Thread [%d]: Cannot write to file '%s': %s",
|
||||
thread_num, to_fullpath, strerror(errno));
|
||||
exit_code = WRITE_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1743,20 +1858,13 @@ int fio_send_file_gz(const char *from_fullpath, const char *to_fullpath, FILE* o
|
||||
|
||||
if (fwrite(out_buf, 1, len, out) != len)
|
||||
{
|
||||
elog(WARNING, "Thread [%d]: Cannot write to file: %s",
|
||||
thread_num, strerror(errno));
|
||||
exit_code = WRITE_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(WARNING, "Thread [%d]: Remote agent returned message of unexpected type: %i",
|
||||
thread_num, hdr.cop);
|
||||
exit_code = REMOTE_ERROR;
|
||||
break;
|
||||
}
|
||||
elog(ERROR, "Remote agent returned message of unexpected type: %i", hdr.cop);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
@@ -1779,10 +1887,14 @@ cleanup:
|
||||
* SEND_OK (0)
|
||||
* FILE_MISSING (-1)
|
||||
* OPEN_FAILED (-2)
|
||||
* READ_FAIL (-3)
|
||||
* WRITE_FAIL (-4)
|
||||
* READ_FAILED (-3)
|
||||
* WRITE_FAILED (-4)
|
||||
*
|
||||
* OPEN_FAILED and READ_FAIL should also set errormsg.
|
||||
* If pgFile is not NULL then we must calculate crc and read_size for it.
|
||||
*/
|
||||
int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out, int thread_num)
|
||||
int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
pgFile *file, char **errormsg)
|
||||
{
|
||||
fio_header hdr;
|
||||
int exit_code = SEND_OK;
|
||||
@@ -1792,8 +1904,8 @@ int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
hdr.cop = FIO_SEND_FILE;
|
||||
hdr.size = path_len;
|
||||
|
||||
elog(VERBOSE, "Thread [%d]: Attempting to open remote WAL file '%s'",
|
||||
thread_num, from_fullpath);
|
||||
// elog(VERBOSE, "Thread [%d]: Attempting to open remote WAL file '%s'",
|
||||
// thread_num, from_fullpath);
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, from_fullpath, path_len), path_len);
|
||||
@@ -1813,7 +1925,8 @@ int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
if (hdr.size > 0)
|
||||
{
|
||||
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
|
||||
elog(WARNING, "Thread [%d]: %s", thread_num, buf);
|
||||
*errormsg = pgut_malloc(hdr.size);
|
||||
snprintf(*errormsg, hdr.size, "%s", buf);
|
||||
}
|
||||
exit_code = hdr.arg;
|
||||
break;
|
||||
@@ -1826,19 +1939,20 @@ int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
/* We have received a chunk of data data, lets write it out */
|
||||
if (fwrite(buf, 1, hdr.size, out) != hdr.size)
|
||||
{
|
||||
elog(WARNING, "Thread [%d]: Cannot write to file '%s': %s",
|
||||
thread_num, to_fullpath, strerror(errno));
|
||||
exit_code = WRITE_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (file)
|
||||
{
|
||||
file->read_size += hdr.size;
|
||||
COMP_FILE_CRC32(true, file->crc, buf, hdr.size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: fio_disconnect may get assert fail when running after this */
|
||||
elog(WARNING, "Thread [%d]: Remote agent returned message of unexpected type: %i",
|
||||
thread_num, hdr.cop);
|
||||
exit_code = REMOTE_ERROR;
|
||||
break;
|
||||
elog(ERROR, "Remote agent returned message of unexpected type: %i", hdr.cop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1851,9 +1965,13 @@ int fio_send_file(const char *from_fullpath, const char *to_fullpath, FILE* out,
|
||||
|
||||
/* Send file content
|
||||
* On error we return FIO_ERROR message with following codes
|
||||
* FILE_MISSING (-1)
|
||||
* OPEN_FAILED (-2)
|
||||
* READ_FAILED (-3)
|
||||
* FIO_ERROR:
|
||||
* FILE_MISSING (-1)
|
||||
* OPEN_FAILED (-2)
|
||||
* READ_FAILED (-3)
|
||||
*
|
||||
* FIO_PAGE
|
||||
* FIO_SEND_FILE_EOF
|
||||
*
|
||||
*/
|
||||
static void fio_send_file_impl(int out, char const* path)
|
||||
@@ -1883,9 +2001,9 @@ static void fio_send_file_impl(int out, char const* path)
|
||||
else
|
||||
{
|
||||
hdr.arg = OPEN_FAILED;
|
||||
errormsg = pgut_malloc(MAXPGPATH);
|
||||
errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
/* Construct the error message */
|
||||
snprintf(errormsg, MAXPGPATH, "Cannot open source file '%s': %s", path, strerror(errno));
|
||||
snprintf(errormsg, ERRMSG_MAX_LEN, "Cannot open file '%s': %s", path, strerror(errno));
|
||||
hdr.size = strlen(errormsg) + 1;
|
||||
}
|
||||
|
||||
@@ -1909,10 +2027,10 @@ static void fio_send_file_impl(int out, char const* path)
|
||||
if (ferror(fp))
|
||||
{
|
||||
hdr.cop = FIO_ERROR;
|
||||
errormsg = pgut_malloc(MAXPGPATH);
|
||||
errormsg = pgut_malloc(ERRMSG_MAX_LEN);
|
||||
hdr.arg = READ_FAILED;
|
||||
/* Construct the error message */
|
||||
snprintf(errormsg, MAXPGPATH, "Cannot read source file '%s': %s", path, strerror(errno));
|
||||
snprintf(errormsg, ERRMSG_MAX_LEN, "Cannot read from file '%s': %s", path, strerror(errno));
|
||||
hdr.size = strlen(errormsg) + 1;
|
||||
/* send header and message */
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
@@ -1946,6 +2064,162 @@ cleanup:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compile the array of files located on remote machine in directory root */
|
||||
void fio_list_dir(parray *files, const char *root, bool exclude,
|
||||
bool follow_symlink, bool add_root, bool backup_logs, int external_dir_num)
|
||||
{
|
||||
fio_header hdr;
|
||||
fio_list_dir_request req;
|
||||
char *buf = pgut_malloc(CHUNK_SIZE);
|
||||
|
||||
/* Send to the agent message with parameters for directory listing */
|
||||
snprintf(req.path, MAXPGPATH, "%s", root);
|
||||
req.exclude = exclude;
|
||||
req.follow_symlink = follow_symlink;
|
||||
req.add_root = add_root;
|
||||
req.backup_logs = backup_logs;
|
||||
req.exclusive_backup = exclusive_backup;
|
||||
req.external_dir_num = external_dir_num;
|
||||
|
||||
hdr.cop = FIO_LIST_DIR;
|
||||
hdr.size = sizeof(req);
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, &req, hdr.size), hdr.size);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* receive data */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
|
||||
if (hdr.cop == FIO_SEND_FILE_EOF)
|
||||
{
|
||||
/* the work is done */
|
||||
break;
|
||||
}
|
||||
else if (hdr.cop == FIO_SEND_FILE)
|
||||
{
|
||||
pgFile *file = NULL;
|
||||
fio_pgFile fio_file;
|
||||
|
||||
/* receive rel_path */
|
||||
IO_CHECK(fio_read_all(fio_stdin, buf, hdr.size), hdr.size);
|
||||
file = pgFileInit(buf);
|
||||
|
||||
/* receive metainformation */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &fio_file, sizeof(fio_file)), sizeof(fio_file));
|
||||
|
||||
file->mode = fio_file.mode;
|
||||
file->size = fio_file.size;
|
||||
file->mtime = fio_file.mtime;
|
||||
file->is_datafile = fio_file.is_datafile;
|
||||
file->is_database = fio_file.is_database;
|
||||
file->tblspcOid = fio_file.tblspcOid;
|
||||
file->dbOid = fio_file.dbOid;
|
||||
file->relOid = fio_file.relOid;
|
||||
file->forkName = fio_file.forkName;
|
||||
file->segno = fio_file.segno;
|
||||
file->external_dir_num = fio_file.external_dir_num;
|
||||
|
||||
if (fio_file.linked_len > 0)
|
||||
{
|
||||
IO_CHECK(fio_read_all(fio_stdin, buf, fio_file.linked_len), fio_file.linked_len);
|
||||
|
||||
file->linked = pgut_malloc(fio_file.linked_len);
|
||||
snprintf(file->linked, fio_file.linked_len, "%s", buf);
|
||||
}
|
||||
|
||||
// elog(INFO, "Received file: %s, mode: %u, size: %lu, mtime: %lu",
|
||||
// file->rel_path, file->mode, file->size, file->mtime);
|
||||
|
||||
parray_append(files, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: fio_disconnect may get assert fail when running after this */
|
||||
elog(ERROR, "Remote agent returned message of unexpected type: %i", hdr.cop);
|
||||
}
|
||||
}
|
||||
|
||||
pg_free(buf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* To get the arrays of files we use the same function dir_list_file(),
|
||||
* that is used for local backup.
|
||||
* After that we iterate over arrays and for every file send at least
|
||||
* two messages to main process:
|
||||
* 1. rel_path
|
||||
* 2. metainformation (size, mtime, etc)
|
||||
* 3. link path (optional)
|
||||
*
|
||||
* TODO: replace FIO_SEND_FILE and FIO_SEND_FILE_EOF with dedicated messages
|
||||
*/
|
||||
static void fio_list_dir_impl(int out, char* buf)
|
||||
{
|
||||
int i;
|
||||
fio_header hdr;
|
||||
fio_list_dir_request *req = (fio_list_dir_request*) buf;
|
||||
parray *file_files = parray_new();
|
||||
|
||||
/*
|
||||
* Disable logging into console any messages with exception of ERROR messages,
|
||||
* because currently we have no mechanism to notify the main process
|
||||
* about then message been sent.
|
||||
* TODO: correctly send elog messages from agent to main process.
|
||||
*/
|
||||
instance_config.logger.log_level_console = ERROR;
|
||||
exclusive_backup = req->exclusive_backup;
|
||||
|
||||
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
|
||||
req->add_root, req->backup_logs, req->external_dir_num, FIO_LOCAL_HOST);
|
||||
|
||||
/* send information about files to the main process */
|
||||
for (i = 0; i < parray_num(file_files); i++)
|
||||
{
|
||||
fio_pgFile fio_file;
|
||||
pgFile *file = (pgFile *) parray_get(file_files, i);
|
||||
|
||||
fio_file.mode = file->mode;
|
||||
fio_file.size = file->size;
|
||||
fio_file.mtime = file->mtime;
|
||||
fio_file.is_datafile = file->is_datafile;
|
||||
fio_file.is_database = file->is_database;
|
||||
fio_file.tblspcOid = file->tblspcOid;
|
||||
fio_file.dbOid = file->dbOid;
|
||||
fio_file.relOid = file->relOid;
|
||||
fio_file.forkName = file->forkName;
|
||||
fio_file.segno = file->segno;
|
||||
fio_file.external_dir_num = file->external_dir_num;
|
||||
|
||||
if (file->linked)
|
||||
fio_file.linked_len = strlen(file->linked) + 1;
|
||||
else
|
||||
fio_file.linked_len = 0;
|
||||
|
||||
hdr.cop = FIO_SEND_FILE;
|
||||
hdr.size = strlen(file->rel_path) + 1;
|
||||
|
||||
/* send rel_path first */
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(out, file->rel_path, hdr.size), hdr.size);
|
||||
|
||||
/* now send file metainformation */
|
||||
IO_CHECK(fio_write_all(out, &fio_file, sizeof(fio_file)), sizeof(fio_file));
|
||||
|
||||
/* If file is a symlink, then send link path */
|
||||
if (file->linked)
|
||||
IO_CHECK(fio_write_all(out, file->linked, fio_file.linked_len), fio_file.linked_len);
|
||||
|
||||
pgFileFree(file);
|
||||
}
|
||||
|
||||
parray_free(file_files);
|
||||
hdr.cop = FIO_SEND_FILE_EOF;
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
}
|
||||
|
||||
/* Execute commands at remote host */
|
||||
void fio_communicate(int in, int out)
|
||||
{
|
||||
@@ -2083,13 +2357,12 @@ void fio_communicate(int in, int out)
|
||||
case FIO_TRUNCATE: /* Truncate file */
|
||||
SYS_CHECK(ftruncate(fd[hdr.handle], hdr.arg));
|
||||
break;
|
||||
case FIO_SEND_PAGES:
|
||||
Assert(hdr.size == sizeof(fio_send_request));
|
||||
fio_send_pages_impl(fd[hdr.handle], out, buf, false);
|
||||
case FIO_LIST_DIR:
|
||||
fio_list_dir_impl(out, buf);
|
||||
break;
|
||||
case FIO_SEND_PAGES_PAGEMAP:
|
||||
case FIO_SEND_PAGES:
|
||||
// buf contain fio_send_request header and bitmap.
|
||||
fio_send_pages_impl(fd[hdr.handle], out, buf, true);
|
||||
fio_send_pages_impl(out, buf);
|
||||
break;
|
||||
case FIO_SEND_FILE:
|
||||
fio_send_file_impl(out, buf);
|
||||
|
||||
+2
-2
@@ -37,7 +37,6 @@ typedef enum
|
||||
FIO_GET_CRC32,
|
||||
/* used in fio_send_pages */
|
||||
FIO_SEND_PAGES,
|
||||
FIO_SEND_PAGES_PAGEMAP,
|
||||
FIO_ERROR,
|
||||
FIO_SEND_FILE,
|
||||
// FIO_CHUNK,
|
||||
@@ -47,7 +46,8 @@ typedef enum
|
||||
FIO_DISCONNECT,
|
||||
FIO_DISCONNECTED,
|
||||
/* message for compatibility check */
|
||||
FIO_AGENT_VERSION
|
||||
FIO_AGENT_VERSION,
|
||||
FIO_LIST_DIR
|
||||
} fio_operations;
|
||||
|
||||
typedef enum
|
||||
|
||||
+24
-24
@@ -453,8 +453,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page verification failed, calculated checksum'.format(
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page verification failed, calculated checksum".format(
|
||||
heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
@@ -474,8 +474,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page verification failed, calculated checksum'.format(
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page verification failed, calculated checksum".format(
|
||||
heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
@@ -495,8 +495,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page verification failed, calculated checksum'.format(
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page verification failed, calculated checksum".format(
|
||||
heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
@@ -598,8 +598,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page header invalid, pd_lower'.format(heap_fullpath),
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page header invalid, pd_lower".format(heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -618,8 +618,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page header invalid, pd_lower'.format(heap_fullpath),
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page header invalid, pd_lower".format(heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -638,8 +638,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page header invalid, pd_lower'.format(heap_fullpath),
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page header invalid, pd_lower".format(heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -741,8 +741,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page header invalid, pd_lower'.format(heap_fullpath),
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page header invalid, pd_lower".format(heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -761,8 +761,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page header invalid, pd_lower'.format(heap_fullpath),
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page header invalid, pd_lower".format(heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -781,8 +781,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Corruption detected in file "{0}", block 1: '
|
||||
'page header invalid, pd_lower'.format(heap_fullpath),
|
||||
"ERROR: Corruption detected in file '{0}', block 1: "
|
||||
"page header invalid, pd_lower".format(heap_fullpath),
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -1385,8 +1385,8 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
with open(os.path.join(backup_dir, 'log', 'pg_probackup.log')) as f:
|
||||
log_content = f.read()
|
||||
self.assertTrue(
|
||||
'LOG: File "{0}" is not found'.format(absolute_path) in log_content,
|
||||
'File "{0}" should be deleted but it`s not'.format(absolute_path))
|
||||
"LOG: File '{0}' is not found".format(absolute_path) in log_content,
|
||||
"File '{0}' should be deleted but it`s not".format(absolute_path))
|
||||
|
||||
node.cleanup()
|
||||
self.restore_node(backup_dir, 'node', node, options=["-j", "4"])
|
||||
@@ -1922,7 +1922,7 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
repr(self.output), self.cmd))
|
||||
except ProbackupException as e:
|
||||
self.assertIn(
|
||||
'ERROR: Cannot open file',
|
||||
'ERROR: Cannot open source file',
|
||||
e.message,
|
||||
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
|
||||
repr(e.message), self.cmd))
|
||||
@@ -2366,7 +2366,7 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
# Clean after yourself
|
||||
self.del_test_dir(module_name, fname)
|
||||
|
||||
# @unittest.skip("skip")
|
||||
@unittest.skip("skip")
|
||||
def test_backup_with_less_privileges_role(self):
|
||||
"""
|
||||
check permissions correctness from documentation:
|
||||
@@ -2880,13 +2880,13 @@ class BackupTest(ProbackupTest, unittest.TestCase):
|
||||
node.slow_start()
|
||||
|
||||
with node.connect("postgres") as conn:
|
||||
for i in range(1200000):
|
||||
for i in range(1000000):
|
||||
conn.execute(
|
||||
"CREATE TABLE t_{0} as select 1".format(i))
|
||||
conn.commit()
|
||||
|
||||
full_id = self.backup_node(
|
||||
backup_dir, 'node', node, options=['--stream'])
|
||||
backup_dir, 'node', node, options=['--stream', '-j2'])
|
||||
|
||||
pgdata = self.pgdata_content(node.data_dir)
|
||||
|
||||
|
||||
+3
-3
@@ -413,7 +413,7 @@ class MergeTest(ProbackupTest, unittest.TestCase):
|
||||
node.slow_start()
|
||||
|
||||
# Fill with data
|
||||
node.pgbench_init(scale=5)
|
||||
node.pgbench_init(scale=20)
|
||||
|
||||
# Do uncompressed FULL backup
|
||||
self.backup_node(backup_dir, "node", node)
|
||||
@@ -423,7 +423,7 @@ class MergeTest(ProbackupTest, unittest.TestCase):
|
||||
self.assertEqual(show_backup["backup-mode"], "FULL")
|
||||
|
||||
# Change data
|
||||
pgbench = node.pgbench(options=['-T', '20', '-c', '2', '--no-vacuum'])
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '1', '--no-vacuum'])
|
||||
pgbench.wait()
|
||||
|
||||
# Do compressed DELTA backup
|
||||
@@ -432,7 +432,7 @@ class MergeTest(ProbackupTest, unittest.TestCase):
|
||||
options=['--compress-algorithm=zlib', '--stream'])
|
||||
|
||||
# Change data
|
||||
pgbench = node.pgbench(options=['-T', '20', '-c', '2', '--no-vacuum'])
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '1', '--no-vacuum'])
|
||||
pgbench.wait()
|
||||
|
||||
# Do uncompressed PAGE backup
|
||||
|
||||
Reference in New Issue
Block a user