Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 280e5f1

Browse files
committed
Add progress reporting to pg_checksums
This adds a new option to pg_checksums called -P/--progress, showing every second some information about the computation state of an operation for --check and --enable (--disable only updates the control file and is quick). This requires a pre-scan of the data folder so as the total size of checksummable items can be calculated, and then it gets compared to the amount processed. Similarly to what is done for pg_rewind and pg_basebackup, the information printed in the progress report consists of the current amount of data computed and the total amount of data to compute. This could be extended later on. Author: Michael Banck, Bernd Helmle Reviewed-by: Fabien Coelho, Michael Paquier Discussion: https://postgr.es/m/1535719851.1286.17.camel@credativ.de
1 parent 475861b commit 280e5f1

File tree

2 files changed

+117
-8
lines changed

2 files changed

+117
-8
lines changed

doc/src/sgml/ref/pg_checksums.sgml

+11
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ PostgreSQL documentation
135135
</listitem>
136136
</varlistentry>
137137

138+
<varlistentry>
139+
<term><option>-P</option></term>
140+
<term><option>--progress</option></term>
141+
<listitem>
142+
<para>
143+
Enable progress reporting. Turning this on will deliver a progress
144+
report while checking or enabling checksums.
145+
</para>
146+
</listitem>
147+
</varlistentry>
148+
138149
<varlistentry>
139150
<term><option>-V</option></term>
140151
<term><option>--version</option></term>

src/bin/pg_checksums/pg_checksums.c

+106-8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "postgres_fe.h"
1616

1717
#include <dirent.h>
18+
#include <time.h>
1819
#include <sys/stat.h>
1920
#include <unistd.h>
2021

@@ -38,6 +39,7 @@ static ControlFileData *ControlFile;
3839
static char *only_relfilenode = NULL;
3940
static bool do_sync = true;
4041
static bool verbose = false;
42+
static bool showprogress = false;
4143

4244
typedef enum
4345
{
@@ -60,6 +62,13 @@ static PgChecksumMode mode = PG_MODE_CHECK;
6062

6163
static const char *progname;
6264

65+
/*
66+
* Progress status information.
67+
*/
68+
int64 total_size = 0;
69+
int64 current_size = 0;
70+
static pg_time_t last_progress_report = 0;
71+
6372
static void
6473
usage(void)
6574
{
@@ -72,6 +81,7 @@ usage(void)
7281
printf(_(" -d, --disable disable data checksums\n"));
7382
printf(_(" -e, --enable enable data checksums\n"));
7483
printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
84+
printf(_(" -P, --progress show progress information\n"));
7585
printf(_(" -v, --verbose output verbose messages\n"));
7686
printf(_(" -r RELFILENODE check only relation with specified relfilenode\n"));
7787
printf(_(" -V, --version output version information, then exit\n"));
@@ -98,6 +108,52 @@ static const char *const skip[] = {
98108
NULL,
99109
};
100110

111+
/*
112+
* Report current progress status. Parts borrowed from
113+
* src/bin/pg_basebackup.c.
114+
*/
115+
static void
116+
progress_report(bool force)
117+
{
118+
int percent;
119+
char total_size_str[32];
120+
char current_size_str[32];
121+
pg_time_t now;
122+
123+
Assert(showprogress);
124+
125+
now = time(NULL);
126+
if (now == last_progress_report && !force)
127+
return; /* Max once per second */
128+
129+
/* Save current time */
130+
last_progress_report = now;
131+
132+
/* Adjust total size if current_size is larger */
133+
if (current_size > total_size)
134+
total_size = current_size;
135+
136+
/* Calculate current percentage of size done */
137+
percent = total_size ? (int) ((current_size) * 100 / total_size) : 0;
138+
139+
snprintf(total_size_str, sizeof(total_size_str), INT64_FORMAT,
140+
total_size / (1024 * 1024));
141+
snprintf(current_size_str, sizeof(current_size_str), INT64_FORMAT,
142+
current_size / (1024 * 1024));
143+
144+
/*
145+
* Separate step to keep platform-dependent format code out of
146+
* translatable strings. And we only test for INT64_FORMAT availability
147+
* in snprintf, not fprintf.
148+
*/
149+
fprintf(stderr, "%*s/%s MB (%d%%) computed",
150+
(int) strlen(current_size_str), current_size_str, total_size_str,
151+
percent);
152+
153+
/* Stay on the same line if reporting to a terminal */
154+
fprintf(stderr, isatty(fileno(stderr)) ? "\r" : "\n");
155+
}
156+
101157
static bool
102158
skipfile(const char *fn)
103159
{
@@ -153,6 +209,7 @@ scan_file(const char *fn, BlockNumber segmentno)
153209
continue;
154210

155211
csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
212+
current_size += r;
156213
if (mode == PG_MODE_CHECK)
157214
{
158215
if (csum != header->pd_checksum)
@@ -183,6 +240,9 @@ scan_file(const char *fn, BlockNumber segmentno)
183240
exit(1);
184241
}
185242
}
243+
244+
if (showprogress)
245+
progress_report(false);
186246
}
187247

188248
if (verbose)
@@ -196,9 +256,17 @@ scan_file(const char *fn, BlockNumber segmentno)
196256
close(f);
197257
}
198258

199-
static void
200-
scan_directory(const char *basedir, const char *subdir)
259+
/*
260+
* Scan the given directory for items which can be checksummed and
261+
* operate on each one of them. If "sizeonly" is true, the size of
262+
* all the items which have checksums is computed and returned back
263+
* to the caller without operating on the files. This is used to compile
264+
* the total size of the data directory for progress reports.
265+
*/
266+
static int64
267+
scan_directory(const char *basedir, const char *subdir, bool sizeonly)
201268
{
269+
int64 dirsize = 0;
202270
char path[MAXPGPATH];
203271
DIR *dir;
204272
struct dirent *de;
@@ -275,16 +343,24 @@ scan_directory(const char *basedir, const char *subdir)
275343
/* Relfilenode not to be included */
276344
continue;
277345

278-
scan_file(fn, segmentno);
346+
dirsize += st.st_size;
347+
348+
/*
349+
* No need to work on the file when calculating only the size of
350+
* the items in the data folder.
351+
*/
352+
if (!sizeonly)
353+
scan_file(fn, segmentno);
279354
}
280355
#ifndef WIN32
281356
else if (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
282357
#else
283358
else if (S_ISDIR(st.st_mode) || pgwin32_is_junction(fn))
284359
#endif
285-
scan_directory(path, de->d_name);
360+
dirsize += scan_directory(path, de->d_name, sizeonly);
286361
}
287362
closedir(dir);
363+
return dirsize;
288364
}
289365

290366
int
@@ -296,6 +372,7 @@ main(int argc, char *argv[])
296372
{"disable", no_argument, NULL, 'd'},
297373
{"enable", no_argument, NULL, 'e'},
298374
{"no-sync", no_argument, NULL, 'N'},
375+
{"progress", no_argument, NULL, 'P'},
299376
{"verbose", no_argument, NULL, 'v'},
300377
{NULL, 0, NULL, 0}
301378
};
@@ -323,7 +400,7 @@ main(int argc, char *argv[])
323400
}
324401
}
325402

326-
while ((c = getopt_long(argc, argv, "cD:deNr:v", long_options, &option_index)) != -1)
403+
while ((c = getopt_long(argc, argv, "cD:deNPr:v", long_options, &option_index)) != -1)
327404
{
328405
switch (c)
329406
{
@@ -353,6 +430,9 @@ main(int argc, char *argv[])
353430
}
354431
only_relfilenode = pstrdup(optarg);
355432
break;
433+
case 'P':
434+
showprogress = true;
435+
break;
356436
default:
357437
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
358438
exit(1);
@@ -447,9 +527,27 @@ main(int argc, char *argv[])
447527
/* Operate on all files if checking or enabling checksums */
448528
if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
449529
{
450-
scan_directory(DataDir, "global");
451-
scan_directory(DataDir, "base");
452-
scan_directory(DataDir, "pg_tblspc");
530+
/*
531+
* If progress status information is requested, we need to scan the
532+
* directory tree twice: once to know how much total data needs to be
533+
* processed and once to do the real work.
534+
*/
535+
if (showprogress)
536+
{
537+
total_size = scan_directory(DataDir, "global", true);
538+
total_size += scan_directory(DataDir, "base", true);
539+
total_size += scan_directory(DataDir, "pg_tblspc", true);
540+
}
541+
542+
(void) scan_directory(DataDir, "global", false);
543+
(void) scan_directory(DataDir, "base", false);
544+
(void) scan_directory(DataDir, "pg_tblspc", false);
545+
546+
if (showprogress)
547+
{
548+
progress_report(true);
549+
fprintf(stderr, "\n"); /* Need to move to next line */
550+
}
453551

454552
printf(_("Checksum operation completed\n"));
455553
printf(_("Files scanned: %s\n"), psprintf(INT64_FORMAT, files));

0 commit comments

Comments
 (0)