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 #include <db.h>
41 #include <sys/stat.h>
42
43
44 /*****************
45 * Purpose: Verify fix for 3113
46 * Bug: Rollback log is checkpointed along with other cachefiles,
47 * but system crashes before checkpoint_end is written to recovery log.
48 * When recovery runs, it uses latest rollback log, which is out of synch
49 * with recovery log. Latest version of rollback log would be correct for
50 * last checkpoint if it completed, but version of rollback log needed
51 * is for last complete checkpoint.
52 * Fix: When opening rollback log for recovery, do not use latest, but use
53 * latest that is no newer than last complete checkpoint.
54 * Test: begin txn
55 * insert
56 * commit
57 * complete checkpoint (no live txns in checkpoint)
58 * begin txn
59 * insert
60 * begin checkpoint (txn in checkpointed rollback log)
61 * crash using callback2 (just before checkpoint_end is written to disk)
62 * attempt to recover, should crash with 3113
63 */
64
65 static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
66 static const char *namea="a.db";
67 static void checkpoint_callback_2(void * UU(extra));
68 static DB_ENV *env;
69 static bool do_test=false, do_recover=false;
70
71 static void
run_test(void)72 run_test(void) {
73 int r;
74
75 toku_os_recursive_delete(TOKU_TEST_FILENAME);
76 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
77
78 r = db_env_create(&env, 0); CKERR(r);
79 r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
80
81 DB *db;
82 r = db_create(&db, env, 0); CKERR(r);
83 r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
84
85 // txn_begin; insert <a,a>; txn_abort
86 {
87 DB_TXN *txn;
88 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
89 DBT k,v;
90 dbt_init(&k, "a", 2);
91 dbt_init(&v, "a", 2);
92 r = db->put(db, txn, &k, &v, 0); CKERR(r);
93 r = txn->commit(txn, 0); CKERR(r);
94 }
95
96 // checkpoint, no live txns in rollback log
97 r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
98
99 {
100 DB_TXN *txn;
101 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
102 DBT k,v;
103 dbt_init(&k, "b", 2);
104 dbt_init(&v, "b", 2);
105 r = db->put(db, txn, &k, &v, 0); CKERR(r);
106 }
107
108 // cause crash at next checkpoint, after xstillopen written, before checkpoint_end is written
109 db_env_set_checkpoint_callback2(checkpoint_callback_2, NULL);
110
111 // checkpoint, putting xstillopen in recovery log (txn is still active)
112 r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
113
114 }
115
116
checkpoint_callback_2(void * UU (extra))117 static void checkpoint_callback_2(void * UU(extra)) {
118 toku_hard_crash_on_purpose();
119 }
120
121
run_recover(void)122 static void run_recover (void) {
123 int r;
124
125 // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
126 // so recovery always runs over the entire log.
127
128 // run recovery
129 r = db_env_create(&env, 0); CKERR(r);
130 r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
131 r = env->close(env, 0); CKERR(r);
132 exit(0);
133
134 }
135
test_parse_args(int argc,char * const argv[])136 static void test_parse_args (int argc, char * const argv[]) {
137 int resultcode;
138 char * cmd = argv[0];
139 argc--; argv++;
140 while (argc>0) {
141 if (strcmp(argv[0], "-v") == 0) {
142 verbose++;
143 } else if (strcmp(argv[0],"-q")==0) {
144 verbose--;
145 if (verbose<0) verbose=0;
146 } else if (strcmp(argv[0], "--test")==0) {
147 do_test=true;
148 } else if (strcmp(argv[0], "--recover") == 0) {
149 do_recover=true;
150 } else if (strcmp(argv[0], "-h")==0) {
151 resultcode=0;
152 do_usage:
153 fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
154 exit(resultcode);
155 } else {
156 fprintf(stderr, "Unknown arg: %s\n", argv[0]);
157 resultcode=1;
158 goto do_usage;
159 }
160 argc--;
161 argv++;
162 }
163 }
164
165
166
167 int
test_main(int argc,char * const argv[])168 test_main (int argc, char * const argv[]) {
169 test_parse_args(argc, argv);
170
171 if (do_test)
172 run_test();
173 else if (do_recover)
174 run_recover();
175
176 return 0;
177 }
178
179