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 // Create a rollback log with a rollinclude log entry, crash after the txn commits and before the last checkpoint.
40 // Recovery crashes 7.1.0, should succeed.
41 
42 #include "test.h"
43 
44 // Insert max_rows key/val pairs into the db
45 
46 // We want to force a rollinclude so we use a child transaction and insert enough rows so that it spills.
47 // It spills at about 144K and 289K rows.
do_inserts(DB_ENV * env,DB * db,uint64_t max_rows,size_t val_size)48 static void do_inserts(DB_ENV *env, DB *db, uint64_t max_rows, size_t val_size) {
49     char val_data[val_size]; memset(val_data, 0, val_size);
50     int r;
51     DB_TXN *parent = nullptr;
52     r = env->txn_begin(env, nullptr, &parent, 0);
53     CKERR(r);
54 
55     DB_TXN *child = nullptr;
56     r = env->txn_begin(env, parent, &child, 0);
57     CKERR(r);
58 
59     for (uint64_t i = 0; i < max_rows; i++) {
60         // pick a sequential key but it does not matter for this test.
61         uint64_t k[2] = {
62             htonl(i), random64(),
63         };
64 
65         DBT key = { .data = k, .size = sizeof k };
66         DBT val = { .data = val_data, .size = (uint32_t) val_size };
67         r = db->put(db, child, &key, &val, 0);
68         CKERR(r);
69 
70         if (i == max_rows-1) {
71             r = child->commit(child, 0);
72             CKERR(r);
73 
74             r = env->txn_checkpoint(env, 0, 0, 0);
75             CKERR(r);
76         }
77     }
78 
79     r = parent->commit(parent, 0);
80     CKERR(r);
81 }
82 
run_test(uint64_t num_rows,size_t val_size,bool do_crash)83 static void run_test(uint64_t num_rows, size_t val_size, bool do_crash) {
84     int r;
85 
86     DB_ENV *env = nullptr;
87     r = db_env_create(&env, 0);
88     CKERR(r);
89     r = env->set_cachesize(env, 8, 0, 1);
90     CKERR(r);
91     r = env->open(env, TOKU_TEST_FILENAME,
92                   DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
93                   S_IRWXU+S_IRWXG+S_IRWXO);
94     CKERR(r);
95 
96     DB *db = nullptr;
97     r = db_create(&db, env, 0);
98     CKERR(r);
99     r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
100     CKERR(r);
101 
102     r = env->txn_checkpoint(env, 0, 0, 0);
103     CKERR(r);
104 
105     do_inserts(env, db, num_rows, val_size);
106 
107     if (do_crash)
108         assert(0); // crash on purpose
109 
110     r = db->close(db, 0);
111     CKERR(r);
112 
113     r = env->close(env, 0);
114     CKERR(r);
115 }
116 
do_verify(DB_ENV * env,DB * db,uint64_t num_rows,size_t val_size UU ())117 static void do_verify(DB_ENV *env, DB *db, uint64_t num_rows, size_t val_size UU()) {
118     int r;
119     DB_TXN *txn = nullptr;
120     r = env->txn_begin(env, nullptr, &txn, 0);
121     CKERR(r);
122 
123     DBC *c = nullptr;
124     r = db->cursor(db, txn, &c, 0);
125     CKERR(r);
126 
127     uint64_t i = 0;
128     while (1) {
129         DBT key = {};
130         DBT val = {};
131         r = c->c_get(c, &key, &val, DB_NEXT);
132         if (r == DB_NOTFOUND)
133             break;
134         CKERR(r);
135         assert(key.size == 16);
136         uint64_t k[2];
137         memcpy(k, key.data, key.size);
138         assert(htonl(k[0]) == i);
139         assert(val.size == val_size);
140         i++;
141     }
142     assert(i == num_rows);
143 
144     r = c->c_close(c);
145     CKERR(r);
146 
147     r = txn->commit(txn, 0);
148     CKERR(r);
149 }
150 
run_recover(uint64_t num_rows,size_t val_size)151 static void run_recover(uint64_t num_rows, size_t val_size) {
152     int r;
153 
154     DB_ENV *env = nullptr;
155     r = db_env_create(&env, 0);
156     CKERR(r);
157     r = env->set_cachesize(env, 8, 0, 1);
158     CKERR(r);
159     r = env->open(env, TOKU_TEST_FILENAME,
160                   DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
161                   S_IRWXU+S_IRWXG+S_IRWXO);
162     CKERR(r);
163 
164     DB *db = nullptr;
165     r = db_create(&db, env, 0);
166     CKERR(r);
167     r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO);
168     CKERR(r);
169 
170     do_verify(env, db, num_rows, val_size);
171 
172     r = db->close(db, 0);
173     CKERR(r);
174 
175     r = env->close(env, 0);
176     CKERR(r);
177 }
178 
test_main(int argc,char * const argv[])179 int test_main (int argc, char *const argv[]) {
180     bool do_test = false;
181     bool do_recover = false;
182     bool do_crash = true;
183     for (int i = 1; i < argc; i++) {
184         if (strcmp(argv[i], "-v") == 0) {
185             verbose++;
186             continue;
187         }
188         if (strcmp(argv[i], "-q") == 0) {
189             if (verbose > 0) verbose--;
190             continue;
191         }
192         if (strcmp(argv[i], "--test") == 0) {
193             do_test = true;
194             continue;
195         }
196         if (strcmp(argv[i], "--recover") == 0) {
197             do_recover = true;
198             continue;
199         }
200         if (strcmp(argv[i], "--crash") == 0 && i+1 < argc) {
201             do_crash = atoi(argv[++i]);
202             continue;
203         }
204     }
205 
206     uint64_t num_rows = 300000;
207     size_t val_size = 1;
208 
209     if (do_test) {
210         // init the env directory
211         toku_os_recursive_delete(TOKU_TEST_FILENAME);
212         int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
213         CKERR(r);
214         run_test(num_rows, val_size, do_crash);
215     }
216     if (do_recover) {
217         run_recover(num_rows, val_size);
218     }
219 
220     return 0;
221 }
222