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