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