1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

Allow BASE_BACKUP to be throttled

A new MAX_RATE option allows imposing a limit to the network transfer
rate from the server side.  This is useful to limit the stress that
taking a base backup has on the server.

pg_basebackup is now able to specify a value to the server, too.

Author: Antonin Houska

Patch reviewed by Stefan Radomski, Andres Freund, Zoltán Böszörményi,
Fujii Masao, and Álvaro Herrera.
This commit is contained in:
Alvaro Herrera
2014-02-27 18:55:57 -03:00
parent 1161d895d8
commit ef5856fd9b
7 changed files with 306 additions and 20 deletions

View File

@@ -12,10 +12,6 @@
*/
#include "postgres_fe.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "pgtar.h"
#include "pgtime.h"
#include <unistd.h>
#include <dirent.h>
@@ -30,8 +26,12 @@
#endif
#include "getopt_long.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "pgtar.h"
#include "pgtime.h"
#include "receivelog.h"
#include "replication/basebackup.h"
#include "streamutil.h"
@@ -65,6 +65,8 @@ static bool fastcheckpoint = false;
static bool writerecoveryconf = false;
static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
static pg_time_t last_progress_report = 0;
static int32 maxrate = 0; /* no limit by default */
/* Progress counters */
static uint64 totalsize;
@@ -226,6 +228,7 @@ usage(void)
printf(_("\nOptions controlling the output:\n"));
printf(_(" -D, --pgdata=DIRECTORY receive base backup into directory\n"));
printf(_(" -F, --format=p|t output format (plain (default), tar)\n"));
printf(_(" -r, --max-rate=RATE maximum transfer rate to transfer data directory\n"));
printf(_(" -R, --write-recovery-conf\n"
" write recovery.conf after backup\n"));
printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
@@ -606,6 +609,97 @@ progress_report(int tablespacenum, const char *filename, bool force)
fprintf(stderr, "\r");
}
static int32
parse_max_rate(char *src)
{
double result;
char *after_num;
char *suffix = NULL;
errno = 0;
result = strtod(src, &after_num);
if (src == after_num)
{
fprintf(stderr,
_("%s: transfer rate \"%s\" is not a valid value\n"),
progname, src);
exit(1);
}
if (errno != 0)
{
fprintf(stderr,
_("%s: invalid transfer rate \"%s\": %s\n"),
progname, src, strerror(errno));
exit(1);
}
if (result <= 0)
{
/*
* Reject obviously wrong values here.
*/
fprintf(stderr, _("%s: transfer rate must be greater than zero\n"),
progname);
exit(1);
}
/*
* Evaluate suffix, after skipping over possible whitespace.
* Lack of suffix means kilobytes.
*/
while (*after_num != '\0' && isspace((unsigned char) *after_num))
after_num++;
if (*after_num != '\0')
{
suffix = after_num;
if (*after_num == 'k')
{
/* kilobyte is the expected unit. */
after_num++;
}
else if (*after_num == 'M')
{
after_num++;
result *= 1024.0;
}
}
/* The rest can only consist of white space. */
while (*after_num != '\0' && isspace((unsigned char) *after_num))
after_num++;
if (*after_num != '\0')
{
fprintf(stderr,
_("%s: invalid --max-rate units: \"%s\"\n"),
progname, suffix);
exit(1);
}
/* Valid integer? */
if ((uint64) result != (uint64) ((uint32) result))
{
fprintf(stderr,
_("%s: transfer rate \"%s\" exceeds integer range\n"),
progname, src);
exit(1);
}
/*
* The range is checked on the server side too, but avoid the server
* connection if a nonsensical value was passed.
*/
if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
{
fprintf(stderr,
_("%s: transfer rate \"%s\" is out of range\n"),
progname, src);
exit(1);
}
return (int32) result;
}
/*
* Write a piece of tar data
@@ -1485,8 +1579,9 @@ BaseBackup(void)
char *sysidentifier;
uint32 latesttli;
uint32 starttli;
char current_path[MAXPGPATH];
char *basebkp;
char escaped_label[MAXPGPATH];
char *maxrate_clause = NULL;
int i;
char xlogstart[64];
char xlogend[64];
@@ -1559,15 +1654,20 @@ BaseBackup(void)
* Start the actual backup
*/
PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
snprintf(current_path, sizeof(current_path),
"BASE_BACKUP LABEL '%s' %s %s %s %s",
escaped_label,
showprogress ? "PROGRESS" : "",
includewal && !streamwal ? "WAL" : "",
fastcheckpoint ? "FAST" : "",
includewal ? "NOWAIT" : "");
if (PQsendQuery(conn, current_path) == 0)
if (maxrate > 0)
maxrate_clause = psprintf("MAX_RATE %u", maxrate);
basebkp =
psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s",
escaped_label,
showprogress ? "PROGRESS" : "",
includewal && !streamwal ? "WAL" : "",
fastcheckpoint ? "FAST" : "",
includewal ? "NOWAIT" : "",
maxrate_clause ? maxrate_clause : "");
if (PQsendQuery(conn, basebkp) == 0)
{
fprintf(stderr, _("%s: could not send replication command \"%s\": %s"),
progname, "BASE_BACKUP", PQerrorMessage(conn));
@@ -1847,6 +1947,7 @@ main(int argc, char **argv)
{"pgdata", required_argument, NULL, 'D'},
{"format", required_argument, NULL, 'F'},
{"checkpoint", required_argument, NULL, 'c'},
{"max-rate", required_argument, NULL, 'r'},
{"write-recovery-conf", no_argument, NULL, 'R'},
{"tablespace-mapping", required_argument, NULL, 'T'},
{"xlog", no_argument, NULL, 'x'},
@@ -1888,7 +1989,7 @@ main(int argc, char **argv)
}
}
while ((c = getopt_long(argc, argv, "D:F:RT:xX:l:zZ:d:c:h:p:U:s:wWvP",
while ((c = getopt_long(argc, argv, "D:F:r:RT:xX:l:zZ:d:c:h:p:U:s:wWvP",
long_options, &option_index)) != -1)
{
switch (c)
@@ -1909,6 +2010,9 @@ main(int argc, char **argv)
exit(1);
}
break;
case 'r':
maxrate = parse_max_rate(optarg);
break;
case 'R':
writerecoveryconf = true;
break;