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