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