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