1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6
7
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22 ----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39 #include "test.h"
40
41 /* Test by counting the fsyncs, to see if group commit is working. */
42
43 #include <db.h>
44 #include <toku_pthread.h>
45 #include <toku_time.h>
46 #include <sys/stat.h>
47 #include <unistd.h>
48
49 DB_ENV *env;
50 DB *db;
51 int do_sync=1;
52
53 #define NITER 100
54
start_a_thread(void * i_p)55 static void *start_a_thread (void *i_p) {
56 int *CAST_FROM_VOIDP(which_thread_p, i_p);
57 int i,r;
58 for (i=0; i<NITER; i++) {
59 DB_TXN *tid;
60 char keystr[100];
61 DBT key,data;
62 snprintf(keystr, sizeof(key), "%ld.%d.%d", random(), *which_thread_p, i);
63 r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
64 r=db->put(db, tid,
65 dbt_init(&key, keystr, 1+strlen(keystr)),
66 dbt_init(&data, keystr, 1+strlen(keystr)),
67 0);
68 r=tid->commit(tid, do_sync ? 0 : DB_TXN_NOSYNC); CKERR(r);
69 }
70 return 0;
71 }
72
73 const char *env_path;
74
75 static void
test_groupcommit(int nthreads)76 test_groupcommit (int nthreads) {
77 int r;
78 DB_TXN *tid;
79
80 r=db_env_create(&env, 0); assert(r==0);
81 r=env->open(env, env_path, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
82 r=db_create(&db, env, 0); CKERR(r);
83 r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
84 r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
85 r=tid->commit(tid, 0); assert(r==0);
86
87 int i;
88 toku_pthread_t threads[nthreads];
89 int whichthread[nthreads];
90 for (i = 0; i < nthreads; i++) {
91 whichthread[i] = i;
92 r = toku_pthread_create(toku_uninstrumented,
93 &threads[i],
94 nullptr,
95 start_a_thread,
96 &whichthread[i]);
97 }
98 for (i = 0; i < nthreads; i++) {
99 toku_pthread_join(threads[i], 0);
100 }
101
102 r=db->close(db, 0); assert(r==0);
103 r=env->close(env, 0); assert(r==0);
104
105 //if (verbose) printf(" That's a total of %d commits\n", nthreads*NITER);
106 }
107
108 // helgrind doesn't understand that pthread_join removes a race condition. I'm not impressed... -Bradley
109 // Also, it doesn't happen every time, making helgrind unsuitable for regression tests.
110 // So we must put locks around things that are properly serialized anyway.
111
112 static int fsync_count_maybe_lockprotected=0;
113 static void
inc_fsync_count(void)114 inc_fsync_count (void) {
115 fsync_count_maybe_lockprotected++;
116 }
117
118 static int
get_fsync_count(void)119 get_fsync_count (void) {
120 int result=fsync_count_maybe_lockprotected;
121 return result;
122 }
123
124 static int
do_fsync(int fd)125 do_fsync (int fd) {
126 //fprintf(stderr, "%8.6fs Thread %ld start fsyncing\n", get_tdiff(), pthread_self());
127 inc_fsync_count();
128 int r = fsync(fd);
129 //fprintf(stderr, "%8.6fs Thread %ld done fsyncing\n", get_tdiff(), pthread_self());
130 return r;
131 }
132
133 static const char *progname;
134 static struct timeval prevtime;
135 static int prev_count;
136
137 static void
printtdiff(int N)138 printtdiff (int N) {
139 struct timeval thistime;
140 gettimeofday(&thistime, 0);
141 double diff = toku_tdiff(&thistime, &prevtime);
142 int fcount=get_fsync_count();
143 if (verbose) printf("%s: %10.6fs %4d fsyncs for %4d threads %s %8.1f tps, %8.1f tps/thread\n", progname, diff, fcount-prev_count,
144 N,
145 do_sync ? "with sync " : "with DB_TXN_NOSYNC",
146 NITER*(N/diff), NITER/diff);
147 prevtime=thistime;
148 prev_count=fcount;
149 }
150
151 static void
do_test(int N)152 do_test (int N) {
153 for (do_sync = 0; do_sync<2; do_sync++) {
154 int count_before = get_fsync_count();
155 test_groupcommit(N);
156 printtdiff(N);
157 int count_after = get_fsync_count();
158 if (count_after-count_before >= N*NITER) {
159 if (verbose) printf("It looks like too many fsyncs. Group commit doesn't appear to be occuring. %d - %d >= %d\n", count_after, count_before, N*NITER);
160 exit(1);
161 }
162 }
163 }
164
165 int log_max_n_threads_over_10 = 3;
166
167 static void
my_parse_args(int argc,char * const argv[])168 my_parse_args (int argc, char *const argv[]) {
169 verbose=1; // use -q to turn off the talking.
170 env_path = TOKU_TEST_FILENAME;
171 const char *argv0=argv[0];
172 while (argc>1) {
173 int resultcode=0;
174 if (strcmp(argv[1], "-v")==0) {
175 verbose++;
176 } else if (strcmp(argv[1],"-q")==0) {
177 verbose--;
178 if (verbose<0) verbose=0;
179 } else if (strcmp(argv[1],"-n")==0) {
180 argc--;
181 argv++;
182 if (argc<=1) { resultcode=1; goto do_usage; }
183 errno = 0;
184 char *end;
185 log_max_n_threads_over_10 = strtol(argv[1], &end, 10);
186 if (errno!=0 || *end) {
187 resultcode=1;
188 goto do_usage;
189 }
190 } else if (strcmp(argv[1], "-h")==0) {
191 do_usage:
192 fprintf(stderr, "Usage:\n%s [-v|-q] [-n LOG(MAX_N_THREADS/10)] [-h]\n", argv0);
193 exit(resultcode);
194 } else {
195 resultcode=1;
196 goto do_usage;
197 }
198 argc--;
199 argv++;
200 }
201 }
202
203
204 int
test_main(int argc,char * const argv[])205 test_main (int argc, char *const argv[]) {
206 progname=argv[0];
207 my_parse_args(argc, argv);
208
209 gettimeofday(&prevtime, 0);
210 prev_count=0;
211
212 db_env_set_func_fsync(do_fsync);
213 db_env_set_num_bucket_mutexes(32);
214
215 toku_os_recursive_delete(env_path);
216 { int r=toku_os_mkdir(env_path, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0); }
217
218 test_groupcommit(1); printtdiff(1);
219 test_groupcommit(2); printtdiff(2);
220 for (int i=0; i<log_max_n_threads_over_10; i++) {
221 do_test(10 << i);
222 }
223 return 0;
224 }
225