Skip to content

Commit ef5856f

Browse files
committed
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.
1 parent 1161d89 commit ef5856f

File tree

7 files changed

+306
-20
lines changed

7 files changed

+306
-20
lines changed

doc/src/sgml/protocol.sgml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,7 @@ The commands accepted in walsender mode are:
17721772
</varlistentry>
17731773

17741774
<varlistentry>
1775-
<term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] [<literal>FAST</literal>] [<literal>WAL</literal>] [<literal>NOWAIT</literal>]</term>
1775+
<term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] [<literal>FAST</literal>] [<literal>WAL</literal>] [<literal>NOWAIT</literal>] [<literal>MAX_RATE</literal> <replaceable>rate</replaceable>]</term>
17761776
<listitem>
17771777
<para>
17781778
Instructs the server to start streaming a base backup.
@@ -1840,7 +1840,21 @@ The commands accepted in walsender mode are:
18401840
the waiting and the warning, leaving the client responsible for
18411841
ensuring the required log is available.
18421842
</para>
1843-
</listitem>
1843+
</listitem>
1844+
</varlistentry>
1845+
1846+
<varlistentry>
1847+
<term><literal>MAX_RATE</literal> <replaceable>rate</></term>
1848+
<listitem>
1849+
<para>
1850+
Limit (throttle) the maximum amount of data transferred from server
1851+
to client per unit of time. The expected unit is kilobytes per second.
1852+
If this option is specified, the value must either be equal to zero
1853+
or it must fall within the range from 32 kB through 1 GB (inclusive).
1854+
If zero is passed or the option is not specified, no restriction is
1855+
imposed on the transfer.
1856+
</para>
1857+
</listitem>
18441858
</varlistentry>
18451859
</variablelist>
18461860
</para>

doc/src/sgml/ref/pg_basebackup.sgml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,27 @@ PostgreSQL documentation
188188
</listitem>
189189
</varlistentry>
190190

191+
<varlistentry>
192+
<term><option>-r <replaceable class="parameter">rate</replaceable></option></term>
193+
<term><option>--max-rate=<replaceable class="parameter">rate</replaceable></option></term>
194+
<listitem>
195+
<para>
196+
The maximum transfer rate of data transferred from the server. Values are
197+
in kilobytes per second. Use a suffix of <literal>M</> to indicate megabytes
198+
per second. A suffix of <literal>k</> is also accepted, and has no effect.
199+
Valid values are between 32 kilobytes per second and 1024 megabytes per second.
200+
</para>
201+
<para>
202+
The purpose is to limit the impact of <application>pg_basebackup</application>
203+
on the running server.
204+
</para>
205+
<para>
206+
This option always affects transfer of the data directory. Transfer of
207+
WAL files is only affected if the collection method is <literal>fetch</literal>.
208+
</para>
209+
</listitem>
210+
</varlistentry>
211+
191212
<varlistentry>
192213
<term><option>-R</option></term>
193214
<term><option>--write-recovery-conf</option></term>

src/backend/replication/basebackup.c

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "libpq/pqformat.h"
2626
#include "miscadmin.h"
2727
#include "nodes/pg_list.h"
28+
#include "pgtar.h"
2829
#include "pgstat.h"
2930
#include "replication/basebackup.h"
3031
#include "replication/walsender.h"
@@ -34,7 +35,8 @@
3435
#include "utils/builtins.h"
3536
#include "utils/elog.h"
3637
#include "utils/ps_status.h"
37-
#include "pgtar.h"
38+
#include "utils/timestamp.h"
39+
3840

3941
typedef struct
4042
{
@@ -43,6 +45,7 @@ typedef struct
4345
bool fastcheckpoint;
4446
bool nowait;
4547
bool includewal;
48+
uint32 maxrate;
4649
} basebackup_options;
4750

4851

@@ -60,6 +63,7 @@ static void perform_base_backup(basebackup_options *opt, DIR *tblspcdir);
6063
static void parse_basebackup_options(List *options, basebackup_options *opt);
6164
static void SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli);
6265
static int compareWalFileNames(const void *a, const void *b);
66+
static void throttle(size_t increment);
6367

6468
/* Was the backup currently in-progress initiated in recovery mode? */
6569
static bool backup_started_in_recovery = false;
@@ -72,6 +76,23 @@ static char *statrelpath = NULL;
7276
*/
7377
#define TAR_SEND_SIZE 32768
7478

79+
/*
80+
* How frequently to throttle, as a fraction of the specified rate-second.
81+
*/
82+
#define THROTTLING_FREQUENCY 8
83+
84+
/* The actual number of bytes, transfer of which may cause sleep. */
85+
static uint64 throttling_sample;
86+
87+
/* Amount of data already transfered but not yet throttled. */
88+
static int64 throttling_counter;
89+
90+
/* The minimum time required to transfer throttling_sample bytes. */
91+
static int64 elapsed_min_unit;
92+
93+
/* The last check of the transfer rate. */
94+
static int64 throttled_last;
95+
7596
typedef struct
7697
{
7798
char *oid;
@@ -203,6 +224,29 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
203224
/* Send tablespace header */
204225
SendBackupHeader(tablespaces);
205226

227+
/* Setup and activate network throttling, if client requested it */
228+
if (opt->maxrate > 0)
229+
{
230+
throttling_sample = opt->maxrate * 1024 / THROTTLING_FREQUENCY;
231+
232+
/*
233+
* The minimum amount of time for throttling_sample
234+
* bytes to be transfered.
235+
*/
236+
elapsed_min_unit = USECS_PER_SEC / THROTTLING_FREQUENCY;
237+
238+
/* Enable throttling. */
239+
throttling_counter = 0;
240+
241+
/* The 'real data' starts now (header was ignored). */
242+
throttled_last = GetCurrentIntegerTimestamp();
243+
}
244+
else
245+
{
246+
/* Disable throttling. */
247+
throttling_counter = -1;
248+
}
249+
206250
/* Send off our tablespaces one by one */
207251
foreach(lc, tablespaces)
208252
{
@@ -430,6 +474,8 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
430474
(errmsg("base backup could not send data, aborting backup")));
431475

432476
len += cnt;
477+
throttle(cnt);
478+
433479
if (len == XLogSegSize)
434480
break;
435481
}
@@ -500,6 +546,7 @@ parse_basebackup_options(List *options, basebackup_options *opt)
500546
bool o_fast = false;
501547
bool o_nowait = false;
502548
bool o_wal = false;
549+
bool o_maxrate = false;
503550

504551
MemSet(opt, 0, sizeof(*opt));
505552
foreach(lopt, options)
@@ -551,6 +598,25 @@ parse_basebackup_options(List *options, basebackup_options *opt)
551598
opt->includewal = true;
552599
o_wal = true;
553600
}
601+
else if (strcmp(defel->defname, "max_rate") == 0)
602+
{
603+
long maxrate;
604+
605+
if (o_maxrate)
606+
ereport(ERROR,
607+
(errcode(ERRCODE_SYNTAX_ERROR),
608+
errmsg("duplicate option \"%s\"", defel->defname)));
609+
610+
maxrate = intVal(defel->arg);
611+
if (maxrate < MAX_RATE_LOWER || maxrate > MAX_RATE_UPPER)
612+
ereport(ERROR,
613+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
614+
errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
615+
(int) maxrate, "MAX_RATE", MAX_RATE_LOWER, MAX_RATE_UPPER)));
616+
617+
opt->maxrate = (uint32) maxrate;
618+
o_maxrate = true;
619+
}
554620
else
555621
elog(ERROR, "option \"%s\" not recognized",
556622
defel->defname);
@@ -1112,6 +1178,7 @@ sendFile(char *readfilename, char *tarfilename, struct stat * statbuf,
11121178
(errmsg("base backup could not send data, aborting backup")));
11131179

11141180
len += cnt;
1181+
throttle(cnt);
11151182

11161183
if (len >= statbuf->st_size)
11171184
{
@@ -1133,10 +1200,14 @@ sendFile(char *readfilename, char *tarfilename, struct stat * statbuf,
11331200
cnt = Min(sizeof(buf), statbuf->st_size - len);
11341201
pq_putmessage('d', buf, cnt);
11351202
len += cnt;
1203+
throttle(cnt);
11361204
}
11371205
}
11381206

1139-
/* Pad to 512 byte boundary, per tar format requirements */
1207+
/*
1208+
* Pad to 512 byte boundary, per tar format requirements. (This small
1209+
* piece of data is probably not worth throttling.)
1210+
*/
11401211
pad = ((len + 511) & ~511) - len;
11411212
if (pad > 0)
11421213
{
@@ -1162,3 +1233,65 @@ _tarWriteHeader(const char *filename, const char *linktarget,
11621233

11631234
pq_putmessage('d', h, 512);
11641235
}
1236+
1237+
/*
1238+
* Increment the network transfer counter by the given number of bytes,
1239+
* and sleep if necessary to comply with the requested network transfer
1240+
* rate.
1241+
*/
1242+
static void
1243+
throttle(size_t increment)
1244+
{
1245+
int64 elapsed,
1246+
elapsed_min,
1247+
sleep;
1248+
int wait_result;
1249+
1250+
if (throttling_counter < 0)
1251+
return;
1252+
1253+
throttling_counter += increment;
1254+
if (throttling_counter < throttling_sample)
1255+
return;
1256+
1257+
/* Time elapsed since the last measurement (and possible wake up). */
1258+
elapsed = GetCurrentIntegerTimestamp() - throttled_last;
1259+
/* How much should have elapsed at minimum? */
1260+
elapsed_min = elapsed_min_unit * (throttling_counter / throttling_sample);
1261+
sleep = elapsed_min - elapsed;
1262+
/* Only sleep if the transfer is faster than it should be. */
1263+
if (sleep > 0)
1264+
{
1265+
ResetLatch(&MyWalSnd->latch);
1266+
1267+
/*
1268+
* (TAR_SEND_SIZE / throttling_sample * elapsed_min_unit) should be
1269+
* the maximum time to sleep. Thus the cast to long is safe.
1270+
*/
1271+
wait_result = WaitLatch(&MyWalSnd->latch,
1272+
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
1273+
(long) (sleep / 1000));
1274+
}
1275+
else
1276+
{
1277+
/*
1278+
* The actual transfer rate is below the limit. A negative value would
1279+
* distort the adjustment of throttled_last.
1280+
*/
1281+
wait_result = 0;
1282+
sleep = 0;
1283+
}
1284+
1285+
/*
1286+
* Only a whole multiple of throttling_sample was processed. The rest will
1287+
* be done during the next call of this function.
1288+
*/
1289+
throttling_counter %= throttling_sample;
1290+
1291+
/* Once the (possible) sleep has ended, new period starts. */
1292+
if (wait_result & WL_TIMEOUT)
1293+
throttled_last += elapsed + sleep;
1294+
else if (sleep > 0)
1295+
/* Sleep was necessary but might have been interrupted. */
1296+
throttled_last = GetCurrentIntegerTimestamp();
1297+
}

src/backend/replication/repl_gram.y

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Node *replication_parse_result;
6969
%token K_PROGRESS
7070
%token K_FAST
7171
%token K_NOWAIT
72+
%token K_MAX_RATE
7273
%token K_WAL
7374
%token K_TIMELINE
7475
%token K_PHYSICAL
@@ -113,7 +114,7 @@ identify_system:
113114
;
114115

115116
/*
116-
* BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] [NOWAIT]
117+
* BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] [NOWAIT] [MAX_RATE %d]
117118
*/
118119
base_backup:
119120
K_BASE_BACKUP base_backup_opt_list
@@ -157,6 +158,11 @@ base_backup_opt:
157158
$$ = makeDefElem("nowait",
158159
(Node *)makeInteger(TRUE));
159160
}
161+
| K_MAX_RATE UCONST
162+
{
163+
$$ = makeDefElem("max_rate",
164+
(Node *)makeInteger($2));
165+
}
160166
;
161167

162168
/* CREATE_REPLICATION_SLOT SLOT slot PHYSICAL */

src/backend/replication/repl_scanner.l

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ IDENTIFY_SYSTEM { return K_IDENTIFY_SYSTEM; }
8686
LABEL { return K_LABEL; }
8787
NOWAIT { return K_NOWAIT; }
8888
PROGRESS { return K_PROGRESS; }
89+
MAX_RATE { return K_MAX_RATE; }
8990
WAL { return K_WAL; }
9091
TIMELINE { return K_TIMELINE; }
9192
START_REPLICATION { return K_START_REPLICATION; }

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy