PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pg_test_fsync.c
Go to the documentation of this file.
1 /*
2  * pg_test_fsync.c
3  * tests all supported fsync() methods
4  */
5 
6 #include "postgres_fe.h"
7 
8 #include <sys/stat.h>
9 #include <sys/time.h>
10 #include <fcntl.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include <signal.h>
14 
15 #include "getopt_long.h"
16 #include "access/xlogdefs.h"
17 
18 
19 /*
20  * put the temp files in the local directory
21  * unless the user specifies otherwise
22  */
23 #define FSYNC_FILENAME "./pg_test_fsync.out"
24 
25 #define XLOG_BLCKSZ_K (XLOG_BLCKSZ / 1024)
26 
27 #define LABEL_FORMAT " %-30s"
28 #define NA_FORMAT "%21s\n"
29 /* translator: maintain alignment with NA_FORMAT */
30 #define OPS_FORMAT gettext_noop("%13.3f ops/sec %6.0f usecs/op\n")
31 #define USECS_SEC 1000000
32 
33 /* These are macros to avoid timing the function call overhead. */
34 #ifndef WIN32
35 #define START_TIMER \
36 do { \
37  alarm_triggered = false; \
38  alarm(secs_per_test); \
39  gettimeofday(&start_t, NULL); \
40 } while (0)
41 #else
42 /* WIN32 doesn't support alarm, so we create a thread and sleep there */
43 #define START_TIMER \
44 do { \
45  alarm_triggered = false; \
46  if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == \
47  INVALID_HANDLE_VALUE) \
48  { \
49  fprintf(stderr, _("Could not create thread for alarm\n")); \
50  exit(1); \
51  } \
52  gettimeofday(&start_t, NULL); \
53 } while (0)
54 #endif
55 
56 #define STOP_TIMER \
57 do { \
58  gettimeofday(&stop_t, NULL); \
59  print_elapse(start_t, stop_t, ops); \
60 } while (0)
61 
62 
63 static const char *progname;
64 
65 static int secs_per_test = 5;
66 static int needs_unlink = 0;
67 static char full_buf[XLOG_SEG_SIZE],
68  *buf,
70 static struct timeval start_t,
71  stop_t;
72 static bool alarm_triggered = false;
73 
74 
75 static void handle_args(int argc, char *argv[]);
76 static void prepare_buf(void);
77 static void test_open(void);
78 static void test_non_sync(void);
79 static void test_sync(int writes_per_op);
80 static void test_open_syncs(void);
81 static void test_open_sync(const char *msg, int writes_size);
82 static void test_file_descriptor_sync(void);
83 
84 #ifndef WIN32
85 static void process_alarm(int sig);
86 #else
87 static DWORD WINAPI process_alarm(LPVOID param);
88 #endif
89 static void signal_cleanup(int sig);
90 
91 #ifdef HAVE_FSYNC_WRITETHROUGH
92 static int pg_fsync_writethrough(int fd);
93 #endif
94 static void print_elapse(struct timeval start_t, struct timeval stop_t, int ops);
95 static void die(const char *str);
96 
97 
98 int
99 main(int argc, char *argv[])
100 {
101  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_test_fsync"));
102  progname = get_progname(argv[0]);
103 
104  handle_args(argc, argv);
105 
106  /* Prevent leaving behind the test file */
107  pqsignal(SIGINT, signal_cleanup);
108  pqsignal(SIGTERM, signal_cleanup);
109 #ifndef WIN32
111 #endif
112 #ifdef SIGHUP
113  /* Not defined on win32 */
115 #endif
116 
117  prepare_buf();
118 
119  test_open();
120 
121  /* Test using 1 XLOG_BLCKSZ write */
122  test_sync(1);
123 
124  /* Test using 2 XLOG_BLCKSZ writes */
125  test_sync(2);
126 
127  test_open_syncs();
128 
130 
131  test_non_sync();
132 
133  unlink(filename);
134 
135  return 0;
136 }
137 
138 static void
139 handle_args(int argc, char *argv[])
140 {
141  static struct option long_options[] = {
142  {"filename", required_argument, NULL, 'f'},
143  {"secs-per-test", required_argument, NULL, 's'},
144  {NULL, 0, NULL, 0}
145  };
146 
147  int option; /* Command line option */
148  int optindex = 0; /* used by getopt_long */
149 
150  if (argc > 1)
151  {
152  if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
153  {
154  printf(_("Usage: %s [-f FILENAME] [-s SECS-PER-TEST]\n"), progname);
155  exit(0);
156  }
157  if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
158  {
159  puts("pg_test_fsync (PostgreSQL) " PG_VERSION);
160  exit(0);
161  }
162  }
163 
164  while ((option = getopt_long(argc, argv, "f:s:",
165  long_options, &optindex)) != -1)
166  {
167  switch (option)
168  {
169  case 'f':
170  filename = strdup(optarg);
171  break;
172 
173  case 's':
174  secs_per_test = atoi(optarg);
175  break;
176 
177  default:
178  fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
179  progname);
180  exit(1);
181  break;
182  }
183  }
184 
185  if (argc > optind)
186  {
187  fprintf(stderr,
188  _("%s: too many command-line arguments (first is \"%s\")\n"),
189  progname, argv[optind]);
190  fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
191  progname);
192  exit(1);
193  }
194 
195  printf(ngettext("%d second per test\n",
196  "%d seconds per test\n",
197  secs_per_test),
198  secs_per_test);
199 #if PG_O_DIRECT != 0
200  printf(_("O_DIRECT supported on this platform for open_datasync and open_sync.\n"));
201 #else
202  printf(_("Direct I/O is not supported on this platform.\n"));
203 #endif
204 }
205 
206 static void
208 {
209  int ops;
210 
211  /* write random data into buffer */
212  for (ops = 0; ops < XLOG_SEG_SIZE; ops++)
213  full_buf[ops] = random();
214 
215  buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
216 }
217 
218 static void
220 {
221  int tmpfile;
222 
223  /*
224  * test if we can open the target file
225  */
226  if ((tmpfile = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) == -1)
227  die("could not open output file");
228  needs_unlink = 1;
229  if (write(tmpfile, full_buf, XLOG_SEG_SIZE) != XLOG_SEG_SIZE)
230  die("write failed");
231 
232  /* fsync now so that dirty buffers don't skew later tests */
233  if (fsync(tmpfile) != 0)
234  die("fsync failed");
235 
236  close(tmpfile);
237 }
238 
239 static void
240 test_sync(int writes_per_op)
241 {
242  int tmpfile,
243  ops,
244  writes;
245  bool fs_warning = false;
246 
247  if (writes_per_op == 1)
248  printf(_("\nCompare file sync methods using one %dkB write:\n"), XLOG_BLCKSZ_K);
249  else
250  printf(_("\nCompare file sync methods using two %dkB writes:\n"), XLOG_BLCKSZ_K);
251  printf(_("(in wal_sync_method preference order, except fdatasync is Linux's default)\n"));
252 
253  /*
254  * Test open_datasync if available
255  */
256  printf(LABEL_FORMAT, "open_datasync");
257  fflush(stdout);
258 
259 #ifdef OPEN_DATASYNC_FLAG
260  if ((tmpfile = open(filename, O_RDWR | O_DSYNC | PG_O_DIRECT, 0)) == -1)
261  {
262  printf(NA_FORMAT, _("n/a*"));
263  fs_warning = true;
264  }
265  else
266  {
267  START_TIMER;
268  for (ops = 0; alarm_triggered == false; ops++)
269  {
270  for (writes = 0; writes < writes_per_op; writes++)
271  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
272  die("write failed");
273  if (lseek(tmpfile, 0, SEEK_SET) == -1)
274  die("seek failed");
275  }
276  STOP_TIMER;
277  close(tmpfile);
278  }
279 #else
280  printf(NA_FORMAT, _("n/a"));
281 #endif
282 
283 /*
284  * Test fdatasync if available
285  */
286  printf(LABEL_FORMAT, "fdatasync");
287  fflush(stdout);
288 
289 #ifdef HAVE_FDATASYNC
290  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
291  die("could not open output file");
292  START_TIMER;
293  for (ops = 0; alarm_triggered == false; ops++)
294  {
295  for (writes = 0; writes < writes_per_op; writes++)
296  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
297  die("write failed");
298  fdatasync(tmpfile);
299  if (lseek(tmpfile, 0, SEEK_SET) == -1)
300  die("seek failed");
301  }
302  STOP_TIMER;
303  close(tmpfile);
304 #else
305  printf(NA_FORMAT, _("n/a"));
306 #endif
307 
308 /*
309  * Test fsync
310  */
311  printf(LABEL_FORMAT, "fsync");
312  fflush(stdout);
313 
314  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
315  die("could not open output file");
316  START_TIMER;
317  for (ops = 0; alarm_triggered == false; ops++)
318  {
319  for (writes = 0; writes < writes_per_op; writes++)
320  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
321  die("write failed");
322  if (fsync(tmpfile) != 0)
323  die("fsync failed");
324  if (lseek(tmpfile, 0, SEEK_SET) == -1)
325  die("seek failed");
326  }
327  STOP_TIMER;
328  close(tmpfile);
329 
330 /*
331  * If fsync_writethrough is available, test as well
332  */
333  printf(LABEL_FORMAT, "fsync_writethrough");
334  fflush(stdout);
335 
336 #ifdef HAVE_FSYNC_WRITETHROUGH
337  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
338  die("could not open output file");
339  START_TIMER;
340  for (ops = 0; alarm_triggered == false; ops++)
341  {
342  for (writes = 0; writes < writes_per_op; writes++)
343  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
344  die("write failed");
345  if (pg_fsync_writethrough(tmpfile) != 0)
346  die("fsync failed");
347  if (lseek(tmpfile, 0, SEEK_SET) == -1)
348  die("seek failed");
349  }
350  STOP_TIMER;
351  close(tmpfile);
352 #else
353  printf(NA_FORMAT, _("n/a"));
354 #endif
355 
356 /*
357  * Test open_sync if available
358  */
359  printf(LABEL_FORMAT, "open_sync");
360  fflush(stdout);
361 
362 #ifdef OPEN_SYNC_FLAG
363  if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG | PG_O_DIRECT, 0)) == -1)
364  {
365  printf(NA_FORMAT, _("n/a*"));
366  fs_warning = true;
367  }
368  else
369  {
370  START_TIMER;
371  for (ops = 0; alarm_triggered == false; ops++)
372  {
373  for (writes = 0; writes < writes_per_op; writes++)
374  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
375 
376  /*
377  * This can generate write failures if the filesystem has
378  * a large block size, e.g. 4k, and there is no support
379  * for O_DIRECT writes smaller than the file system block
380  * size, e.g. XFS.
381  */
382  die("write failed");
383  if (lseek(tmpfile, 0, SEEK_SET) == -1)
384  die("seek failed");
385  }
386  STOP_TIMER;
387  close(tmpfile);
388  }
389 #else
390  printf(NA_FORMAT, _("n/a"));
391 #endif
392 
393  if (fs_warning)
394  {
395  printf(_("* This file system and its mount options do not support direct\n"
396  " I/O, e.g. ext4 in journaled mode.\n"));
397  }
398 }
399 
400 static void
402 {
403  printf(_("\nCompare open_sync with different write sizes:\n"));
404  printf(_("(This is designed to compare the cost of writing 16kB in different write\n"
405  "open_sync sizes.)\n"));
406 
407  test_open_sync(_(" 1 * 16kB open_sync write"), 16);
408  test_open_sync(_(" 2 * 8kB open_sync writes"), 8);
409  test_open_sync(_(" 4 * 4kB open_sync writes"), 4);
410  test_open_sync(_(" 8 * 2kB open_sync writes"), 2);
411  test_open_sync(_("16 * 1kB open_sync writes"), 1);
412 }
413 
414 /*
415  * Test open_sync with different size files
416  */
417 static void
418 test_open_sync(const char *msg, int writes_size)
419 {
420 #ifdef OPEN_SYNC_FLAG
421  int tmpfile,
422  ops,
423  writes;
424 #endif
425 
426  printf(LABEL_FORMAT, msg);
427  fflush(stdout);
428 
429 #ifdef OPEN_SYNC_FLAG
430  if ((tmpfile = open(filename, O_RDWR | OPEN_SYNC_FLAG | PG_O_DIRECT, 0)) == -1)
431  printf(NA_FORMAT, _("n/a*"));
432  else
433  {
434  START_TIMER;
435  for (ops = 0; alarm_triggered == false; ops++)
436  {
437  for (writes = 0; writes < 16 / writes_size; writes++)
438  if (write(tmpfile, buf, writes_size * 1024) !=
439  writes_size * 1024)
440  die("write failed");
441  if (lseek(tmpfile, 0, SEEK_SET) == -1)
442  die("seek failed");
443  }
444  STOP_TIMER;
445  close(tmpfile);
446  }
447 #else
448  printf(NA_FORMAT, _("n/a"));
449 #endif
450 }
451 
452 static void
454 {
455  int tmpfile,
456  ops;
457 
458  /*
459  * Test whether fsync can sync data written on a different descriptor for
460  * the same file. This checks the efficiency of multi-process fsyncs
461  * against the same file. Possibly this should be done with writethrough
462  * on platforms which support it.
463  */
464  printf(_("\nTest if fsync on non-write file descriptor is honored:\n"));
465  printf(_("(If the times are similar, fsync() can sync data written on a different\n"
466  "descriptor.)\n"));
467 
468  /*
469  * first write, fsync and close, which is the normal behavior without
470  * multiple descriptors
471  */
472  printf(LABEL_FORMAT, "write, fsync, close");
473  fflush(stdout);
474 
475  START_TIMER;
476  for (ops = 0; alarm_triggered == false; ops++)
477  {
478  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
479  die("could not open output file");
480  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
481  die("write failed");
482  if (fsync(tmpfile) != 0)
483  die("fsync failed");
484  close(tmpfile);
485 
486  /*
487  * open and close the file again to be consistent with the following
488  * test
489  */
490  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
491  die("could not open output file");
492  close(tmpfile);
493  }
494  STOP_TIMER;
495 
496  /*
497  * Now open, write, close, open again and fsync This simulates processes
498  * fsyncing each other's writes.
499  */
500  printf(LABEL_FORMAT, "write, close, fsync");
501  fflush(stdout);
502 
503  START_TIMER;
504  for (ops = 0; alarm_triggered == false; ops++)
505  {
506  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
507  die("could not open output file");
508  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
509  die("write failed");
510  close(tmpfile);
511  /* reopen file */
512  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
513  die("could not open output file");
514  if (fsync(tmpfile) != 0)
515  die("fsync failed");
516  close(tmpfile);
517  }
518  STOP_TIMER;
519 }
520 
521 static void
523 {
524  int tmpfile,
525  ops;
526 
527  /*
528  * Test a simple write without fsync
529  */
530  printf(_("\nNon-sync'ed %dkB writes:\n"), XLOG_BLCKSZ_K);
531  printf(LABEL_FORMAT, "write");
532  fflush(stdout);
533 
534  START_TIMER;
535  for (ops = 0; alarm_triggered == false; ops++)
536  {
537  if ((tmpfile = open(filename, O_RDWR, 0)) == -1)
538  die("could not open output file");
539  if (write(tmpfile, buf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
540  die("write failed");
541  close(tmpfile);
542  }
543  STOP_TIMER;
544 }
545 
546 static void
547 signal_cleanup(int signum)
548 {
549  /* Delete the file if it exists. Ignore errors */
550  if (needs_unlink)
551  unlink(filename);
552  /* Finish incomplete line on stdout */
553  puts("");
554  exit(signum);
555 }
556 
557 #ifdef HAVE_FSYNC_WRITETHROUGH
558 
559 static int
561 {
562 #ifdef WIN32
563  return _commit(fd);
564 #elif defined(F_FULLFSYNC)
565  return (fcntl(fd, F_FULLFSYNC, 0) == -1) ? -1 : 0;
566 #else
567  errno = ENOSYS;
568  return -1;
569 #endif
570 }
571 #endif
572 
573 /*
574  * print out the writes per second for tests
575  */
576 static void
577 print_elapse(struct timeval start_t, struct timeval stop_t, int ops)
578 {
579  double total_time = (stop_t.tv_sec - start_t.tv_sec) +
580  (stop_t.tv_usec - start_t.tv_usec) * 0.000001;
581  double per_second = ops / total_time;
582  double avg_op_time_us = (total_time / ops) * USECS_SEC;
583 
584  printf(_(OPS_FORMAT), per_second, avg_op_time_us);
585 }
586 
587 #ifndef WIN32
588 static void
590 {
591  alarm_triggered = true;
592 }
593 #else
594 static DWORD WINAPI
595 process_alarm(LPVOID param)
596 {
597  /* WIN32 doesn't support alarm, so we create a thread and sleep here */
598  Sleep(secs_per_test * 1000);
599  alarm_triggered = true;
600  ExitThread(0);
601 }
602 #endif
603 
604 static void
605 die(const char *str)
606 {
607  fprintf(stderr, _("%s: %s\n"), _(str), strerror(errno));
608  exit(1);
609 }
#define PG_O_DIRECT
Definition: xlogdefs.h:65
static void test_sync(int writes_per_op)
static char full_buf[XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:67
static int secs_per_test
Definition: pg_test_fsync.c:65
static char * filename
Definition: pg_test_fsync.c:69
#define write(a, b, c)
Definition: win32.h:14
const char * get_progname(const char *argv0)
Definition: path.c:453
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
long random(void)
Definition: random.c:22
int pg_fsync_writethrough(int fd)
Definition: fd.c:362
#define USECS_SEC
Definition: pg_test_fsync.c:31
static int fd(const char *x, int i)
Definition: preproc-init.c:105
static void test_open(void)
static void die(const char *str)
static bool alarm_triggered
Definition: pg_test_fsync.c:72
static void prepare_buf(void)
#define required_argument
Definition: getopt_long.h:25
int optind
Definition: getopt.c:51
static const char * progname
Definition: pg_test_fsync.c:63
#define O_DSYNC
Definition: win32.h:270
static char * buf
Definition: pg_test_fsync.c:67
#define NA_FORMAT
Definition: pg_test_fsync.c:28
int unlink(const char *filename)
#define XLOG_BLCKSZ_K
Definition: pg_test_fsync.c:25
#define fsync(fd)
Definition: win32.h:62
#define STOP_TIMER
Definition: pg_test_fsync.c:56
#define ngettext(s, p, n)
Definition: c.h:127
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1001
#define LABEL_FORMAT
Definition: pg_test_fsync.c:27
static void print_elapse(struct timeval start_t, struct timeval stop_t, int ops)
static void test_open_syncs(void)
static void test_non_sync(void)
static int sig
Definition: pg_ctl.c:88
#define SIGHUP
Definition: win32.h:188
static struct timeval start_t stop_t
Definition: pg_test_fsync.c:70
#define TYPEALIGN(ALIGNVAL, LEN)
Definition: c.h:569
static void process_alarm(int sig)
pqsigfunc pqsignal(int signum, pqsigfunc handler)
Definition: signal.c:168
static void signal_cleanup(int sig)
int main(int argc, char *argv[])
Definition: pg_test_fsync.c:99
#define START_TIMER
Definition: pg_test_fsync.c:35
#define OPS_FORMAT
Definition: pg_test_fsync.c:30
static int needs_unlink
Definition: pg_test_fsync.c:66
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:550
#define FSYNC_FILENAME
Definition: pg_test_fsync.c:23
char * optarg
Definition: getopt.c:53
const char * strerror(int errnum)
Definition: strerror.c:19
static void test_file_descriptor_sync(void)
#define close(a)
Definition: win32.h:12
#define SIGALRM
Definition: win32.h:194
#define _(x)
Definition: elog.c:84
static void test_open_sync(const char *msg, int writes_size)
static void handle_args(int argc, char *argv[])