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 /* Transaction consistency:
40  *  fork a process:
41  *   Open two tables, T1 and T2
42  *   begin transaction
43  *   store A in T1
44  *   checkpoint
45  *   store B in T2
46  *   commit (or abort)
47  *   signal to end the process abruptly
48  *  wait for the process to finish
49  *   open the environment doing recovery
50  *   check to see if both A and B are present (or absent)
51  */
52 #include <sys/stat.h>
53 #include "test.h"
54 
55 
56 const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
57 const char *namea="a.db";
58 const char *nameb="b.db";
59 
60 
61 static void
do_x1_shutdown(bool do_commit,bool do_abort)62 do_x1_shutdown (bool do_commit, bool do_abort) {
63     int r;
64     toku_os_recursive_delete(TOKU_TEST_FILENAME);
65     toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
66     DB_ENV *env;
67     DB *dba, *dbb;
68     r = db_env_create(&env, 0);                                                         CKERR(r);
69     r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO);                      CKERR(r);
70     r = db_create(&dba, env, 0);                                                        CKERR(r);
71     r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);    CKERR(r);
72     r = db_create(&dbb, env, 0);                                                        CKERR(r);
73     r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);    CKERR(r);
74     DB_TXN *txn;
75     r = env->txn_begin(env, NULL, &txn, 0);                                             CKERR(r);
76     {
77         DBT a,b;
78         dbt_init(&a, "a", 2);
79         dbt_init(&b, "b", 2);
80 	r = dba->put(dba, txn, &a, &b, 0);                                              CKERR(r);
81 	r = env->txn_checkpoint(env, 0, 0, 0);                                          CKERR(r);
82 	r = dbb->put(dbb, txn, &b, &a, 0);                                              CKERR(r);
83     }
84     //printf("opened\n");
85     if (do_commit) {
86 	r = txn->commit(txn, 0);                                                        CKERR(r);
87     } else if (do_abort) {
88         r = txn->abort(txn);                                                            CKERR(r);
89 
90         // force an fsync of the log
91         r = env->txn_begin(env, NULL, &txn, 0);                                         CKERR(r);
92         r = txn->commit(txn, DB_TXN_SYNC);                                              CKERR(r);
93     }
94     //printf("shutdown\n");
95     toku_hard_crash_on_purpose();
96 }
97 
98 static void
do_x1_recover(bool did_commit)99 do_x1_recover (bool did_commit) {
100     DB_ENV *env;
101     DB *dba, *dbb;
102     int r;
103     r = db_env_create(&env, 0);                                                             CKERR(r);
104     r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);                          CKERR(r);
105     r = db_create(&dba, env, 0);                                                            CKERR(r);
106     r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);        CKERR(r);
107     r = db_create(&dbb, env, 0);                                                            CKERR(r);
108     r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);        CKERR(r);
109     DBT aa, ab;
110     dbt_init(&aa, NULL, 0);
111     dbt_init(&ab, NULL, 0);
112     DBT ba, bb;
113     dbt_init(&ba, NULL, 0);
114     dbt_init(&bb, NULL, 0);
115     DB_TXN *txn;
116     DBC *ca,*cb;
117     r = env->txn_begin(env, NULL, &txn, 0);                                                 CKERR(r);
118     r = dba->cursor(dba, txn, &ca, 0);                                                      CKERR(r);
119     r = dbb->cursor(dbb, txn, &cb, 0);                                                      CKERR(r);
120     int ra = ca->c_get(ca, &aa, &ab, DB_FIRST);                                             CKERR(r);
121     int rb = cb->c_get(cb, &ba, &bb, DB_FIRST);                                             CKERR(r);
122     if (did_commit) {
123 	assert(ra==0);
124 	assert(rb==0);
125 	// verify key-value pairs
126 	assert(aa.size==2);
127 	assert(ab.size==2);
128 	assert(ba.size==2);
129 	assert(bb.size==2);
130 	const char a[2] = "a";
131 	const char b[2] = "b";
132         assert(memcmp(aa.data, &a, 2)==0);
133         assert(memcmp(ab.data, &b, 2)==0);
134         assert(memcmp(ab.data, &b, 2)==0);
135         assert(memcmp(bb.data, &a, 2)==0);
136 	// make sure no other entries in DB
137 	assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
138 	assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
139 	fprintf(stderr, "Both verified. Yay!\n");
140     } else {
141 	// It wasn't committed (it also wasn't aborted), but a checkpoint happened.
142 	assert(ra==DB_NOTFOUND);
143 	assert(rb==DB_NOTFOUND);
144 	fprintf(stderr, "Neither present. Yay!\n");
145     }
146     r = ca->c_close(ca);                                                                    CKERR(r);
147     r = cb->c_close(cb);                                                                    CKERR(r);
148     r = txn->commit(txn, 0);                                                                CKERR(r);
149     r = dba->close(dba, 0);                                                                 CKERR(r);
150     r = dbb->close(dbb, 0);                                                                 CKERR(r);
151     r = env->close(env, 0);                                                                 CKERR(r);
152     exit(0);
153 }
154 
155 static void
do_x1_recover_only(void)156 do_x1_recover_only (void) {
157     DB_ENV *env;
158     int r;
159 
160     r = db_env_create(&env, 0);                                                             CKERR(r);
161     r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);                          CKERR(r);
162     r = env->close(env, 0);                                                                 CKERR(r);
163     exit(0);
164 }
165 
166 static void
do_x1_no_recover(void)167 do_x1_no_recover (void) {
168     DB_ENV *env;
169     int r;
170 
171     r = db_env_create(&env, 0);                                                             CKERR(r);
172     r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
173     assert(r == DB_RUNRECOVERY);
174     r = env->close(env, 0);                                                                 CKERR(r);
175     exit(0);
176 }
177 
178 const char *cmd;
179 
180 #if 0
181 
182 static void
183 do_test_internal (bool commit)
184 {
185     pid_t pid;
186     if (0 == (pid=fork())) {
187 	int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
188 	assert(r==-1);
189 	printf("execl failed: %d (%s)\n", errno, strerror(errno));
190 	assert(0);
191     }
192     {
193 	int r;
194 	int status;
195 	r = waitpid(pid, &status, 0);
196 	//printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
197 	assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
198     }
199     // Now find out what happend
200 
201     if (0 == (pid = fork())) {
202 	int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
203 	assert(r==-1);
204 	printf("execl failed: %d (%s)\n", errno, strerror(errno));
205 	assert(0);
206     }
207     {
208 	int r;
209 	int status;
210 	r = waitpid(pid, &status, 0);
211 	//printf("recovery exited=%d\n", WIFEXITED(status));
212 	assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
213     }
214 }
215 
216 static void
217 do_test (void) {
218     do_test_internal(true);
219     do_test_internal(false);
220 }
221 
222 #endif
223 
224 
225 bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false,  do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
226 
227 static void
x1_parse_args(int argc,char * const argv[])228 x1_parse_args (int argc, char * const argv[]) {
229     int resultcode;
230     cmd = argv[0];
231     argc--; argv++;
232     while (argc>0) {
233 	if (strcmp(argv[0], "-v") == 0) {
234 	    verbose++;
235 	} else if (strcmp(argv[0],"-q")==0) {
236 	    verbose--;
237 	    if (verbose<0) verbose=0;
238 	} else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
239 	    do_commit=true;
240 	} else if (strcmp(argv[0], "--abort")==0) {
241 	    do_abort=true;
242 	} else if (strcmp(argv[0], "--explicit-abort")==0) {
243 	    do_explicit_abort=true;
244 	} else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
245 	    do_recover_committed=true;
246 	} else if (strcmp(argv[0], "--recover-aborted")==0) {
247 	    do_recover_aborted=true;
248         } else if (strcmp(argv[0], "--recover-only") == 0) {
249             do_recover_only=true;
250         } else if (strcmp(argv[0], "--no-recover") == 0) {
251             do_no_recover=true;
252 	} else if (strcmp(argv[0], "-h")==0) {
253 	    resultcode=0;
254 	do_usage:
255 	    fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
256 	    exit(resultcode);
257 	} else {
258 	    fprintf(stderr, "Unknown arg: %s\n", argv[0]);
259 	    resultcode=1;
260 	    goto do_usage;
261 	}
262 	argc--;
263 	argv++;
264     }
265     {
266 	int n_specified=0;
267 	if (do_commit)            n_specified++;
268 	if (do_abort)             n_specified++;
269 	if (do_explicit_abort)    n_specified++;
270 	if (do_recover_committed) n_specified++;
271 	if (do_recover_aborted)   n_specified++;
272 	if (do_recover_only)      n_specified++;
273 	if (do_no_recover)        n_specified++;
274 	if (n_specified>1) {
275 	    printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
276 	    resultcode=1;
277 	    goto do_usage;
278 	}
279     }
280 }
281 
282 int
test_main(int argc,char * const argv[])283 test_main (int argc, char * const argv[])
284 {
285     x1_parse_args(argc, argv);
286     if (do_commit) {
287 	do_x1_shutdown (true, false);
288     } else if (do_abort) {
289 	do_x1_shutdown (false, false);
290     } else if (do_explicit_abort) {
291         do_x1_shutdown(false, true);
292     } else if (do_recover_committed) {
293 	do_x1_recover(true);
294     } else if (do_recover_aborted) {
295 	do_x1_recover(false);
296     } else if (do_recover_only) {
297         do_x1_recover_only();
298     } else if (do_no_recover) {
299         do_x1_no_recover();
300     }
301 #if 0
302     else {
303 	do_test();
304     }
305 #endif
306     return 0;
307 }
308