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 static void
182 do_test_internal (bool commit)
183 {
184     pid_t pid;
185     if (0 == (pid=fork())) {
186 	int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
187 	assert(r==-1);
188 	printf("execl failed: %d (%s)\n", errno, strerror(errno));
189 	assert(0);
190     }
191     {
192 	int r;
193 	int status;
194 	r = waitpid(pid, &status, 0);
195 	//printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
196 	assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
197     }
198     // Now find out what happend
199 
200     if (0 == (pid = fork())) {
201 	int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
202 	assert(r==-1);
203 	printf("execl failed: %d (%s)\n", errno, strerror(errno));
204 	assert(0);
205     }
206     {
207 	int r;
208 	int status;
209 	r = waitpid(pid, &status, 0);
210 	//printf("recovery exited=%d\n", WIFEXITED(status));
211 	assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
212     }
213 }
214 
215 static void
216 do_test (void) {
217     do_test_internal(true);
218     do_test_internal(false);
219 }
220 #endif
221 
222 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;
223 
224 static void
x1_parse_args(int argc,char * const argv[])225 x1_parse_args (int argc, char * const argv[]) {
226     int resultcode;
227     cmd = argv[0];
228     argc--; argv++;
229     while (argc>0) {
230 	if (strcmp(argv[0], "-v") == 0) {
231 	    verbose++;
232 	} else if (strcmp(argv[0],"-q")==0) {
233 	    verbose--;
234 	    if (verbose<0) verbose=0;
235 	} else if (strcmp(argv[0], "--commit")==0) {
236 	    do_commit=true;
237 	} else if (strcmp(argv[0], "--abort")==0 || strcmp(argv[0], "--test") == 0) {
238 	    do_abort=true;
239 	} else if (strcmp(argv[0], "--explicit-abort")==0) {
240 	    do_explicit_abort=true;
241 	} else if (strcmp(argv[0], "--recover-committed")==0) {
242 	    do_recover_committed=true;
243 	} else if (strcmp(argv[0], "--recover-aborted")==0 || strcmp(argv[0], "--recover") == 0) {
244 	    do_recover_aborted=true;
245         } else if (strcmp(argv[0], "--recover-only") == 0) {
246             do_recover_only=true;
247         } else if (strcmp(argv[0], "--no-recover") == 0) {
248             do_no_recover=true;
249 	} else if (strcmp(argv[0], "-h")==0) {
250 	    resultcode=0;
251 	do_usage:
252 	    fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
253 	    exit(resultcode);
254 	} else {
255 	    fprintf(stderr, "Unknown arg: %s\n", argv[0]);
256 	    resultcode=1;
257 	    goto do_usage;
258 	}
259 	argc--;
260 	argv++;
261     }
262     {
263 	int n_specified=0;
264 	if (do_commit)            n_specified++;
265 	if (do_abort)             n_specified++;
266 	if (do_explicit_abort)    n_specified++;
267 	if (do_recover_committed) n_specified++;
268 	if (do_recover_aborted)   n_specified++;
269 	if (do_recover_only)      n_specified++;
270 	if (do_no_recover)        n_specified++;
271 	if (n_specified>1) {
272 	    printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
273 	    resultcode=1;
274 	    goto do_usage;
275 	}
276     }
277 }
278 
279 int
test_main(int argc,char * const argv[])280 test_main (int argc, char *const argv[])
281 {
282     x1_parse_args(argc, argv);
283     if (do_commit) {
284 	do_x1_shutdown (true, false);
285     } else if (do_abort) {
286 	do_x1_shutdown (false, false);
287     } else if (do_explicit_abort) {
288         do_x1_shutdown(false, true);
289     } else if (do_recover_committed) {
290 	do_x1_recover(true);
291     } else if (do_recover_aborted) {
292 	do_x1_recover(false);
293     } else if (do_recover_only) {
294         do_x1_recover_only();
295     } else if (do_no_recover) {
296         do_x1_no_recover();
297     }
298 #if 0
299     else {
300 	do_test();
301     }
302 #endif
303     return 0;
304 }
305