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 // this test makes sure the LSN filtering is used during recovery of put_multiple
40
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include "test.h"
44
45 const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
46
47 const char *namea="a.db";
48 const char *nameb="b.db";
49 enum {num_dbs = 2};
50 static DBT dest_keys[num_dbs];
51 static DBT dest_vals[num_dbs];
52
53 bool do_test=false, do_recover=false;
54
55 static int
put_multiple_generate(DB * dest_db,DB * src_db,DBT_ARRAY * dest_key_arrays,DBT_ARRAY * dest_val_arrays,const DBT * src_key,const DBT * src_val)56 put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
57 toku_dbt_array_resize(dest_key_arrays, 1);
58 toku_dbt_array_resize(dest_val_arrays, 1);
59 DBT *dest_key = &dest_key_arrays->dbts[0];
60 DBT *dest_val = &dest_val_arrays->dbts[0];
61 if (src_db) {
62 assert(src_db->descriptor);
63 assert(src_db->descriptor->dbt.size == 4);
64 assert((*(uint32_t*)src_db->descriptor->dbt.data) == 0);
65 }
66 assert(dest_db->descriptor->dbt.size == 4);
67 uint32_t which = *(uint32_t*)dest_db->descriptor->dbt.data;
68 assert(which < num_dbs);
69
70 if (dest_key->data) toku_free(dest_key->data);
71 if (dest_val->data) toku_free(dest_val->data);
72 dest_key->data = toku_xmemdup (src_key->data, src_key->size);
73 dest_key->size = src_key->size;
74 dest_val->data = toku_xmemdup (src_val->data, src_val->size);
75 dest_val->size = src_val->size;
76 return 0;
77 }
78
run_test(void)79 static void run_test (void) {
80 int r;
81
82 toku_os_recursive_delete(TOKU_TEST_FILENAME);
83 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
84
85 DB_ENV *env;
86 r = db_env_create(&env, 0); CKERR(r);
87 r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
88 CKERR(r);
89 r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
90
91 // create a txn that never closes, forcing recovery to run from the beginning of the log
92 {
93 DB_TXN *oldest_living_txn;
94 r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
95 }
96
97 DBT descriptor;
98 uint32_t which;
99 for (which = 0; which < num_dbs; which++) {
100 dbt_init_realloc(&dest_keys[which]);
101 dbt_init_realloc(&dest_vals[which]);
102 }
103 dbt_init(&descriptor, &which, sizeof(which));
104 DB *dba;
105 DB *dbb;
106 r = db_create(&dba, env, 0); CKERR(r);
107 r = db_create(&dbb, env, 0); CKERR(r);
108 r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
109 which = 0;
110 IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
111 { int chk_r = dba->change_descriptor(dba, txn_desc, &descriptor, 0); CKERR(chk_r); }
112 });
113 r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
114 which = 1;
115 IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
116 { int chk_r = dbb->change_descriptor(dbb, txn_desc, &descriptor, 0); CKERR(chk_r); }
117 });
118 DB *dbs[num_dbs] = {dba, dbb};
119 uint32_t flags[num_dbs] = {0, 0};
120 // txn_begin; insert <a,a>; txn_abort
121 {
122 DB_TXN *txn;
123 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
124 DBT k,v;
125 dbt_init(&k, "a", 2);
126 dbt_init(&v, "b", 2);
127 r = env_put_multiple_test_no_array(env, dba, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
128 CKERR(r);
129 r = txn->abort(txn); CKERR(r);
130 }
131 r = dbb->close(dbb, 0); CKERR(r);
132 r = db_create(&dbb, env, 0); CKERR(r);
133 r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(r);
134 dbs[1] = dbb;
135
136 // txn_begin; insert <a,b>;
137 {
138 DB_TXN *txn;
139 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
140 DBT k,v;
141 dbt_init(&k, "a", 2);
142 dbt_init(&v, "b", 2);
143 r = env_put_multiple_test_no_array(env, NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
144 CKERR(r);
145 }
146
147 // checkpoint
148 r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
149
150 // abort the process
151 toku_hard_crash_on_purpose();
152 }
153
154
run_recover(void)155 static void run_recover (void) {
156 DB_ENV *env;
157 int r;
158
159 // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
160 // so recovery always runs over the entire log.
161
162 // run recovery
163 r = db_env_create(&env, 0); CKERR(r);
164 r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
165 CKERR(r);
166 r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
167
168 // verify the data
169 {
170 DB *db;
171 r = db_create(&db, env, 0); CKERR(r);
172 r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
173
174 DB_TXN *txn;
175 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
176 DBC *cursor;
177 r = db->cursor(db, txn, &cursor, 0); CKERR(r);
178 DBT k, v;
179 r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
180 assert(r == DB_NOTFOUND);
181
182 r = cursor->c_close(cursor); CKERR(r);
183
184 r = txn->commit(txn, 0); CKERR(r);
185 r = db->close(db, 0); CKERR(r);
186 }
187 {
188 DB *db;
189 r = db_create(&db, env, 0); CKERR(r);
190 r = db->open(db, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
191
192 DB_TXN *txn;
193 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
194 DBC *cursor;
195 r = db->cursor(db, txn, &cursor, 0); CKERR(r);
196 DBT k, v;
197 r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
198 assert(r == DB_NOTFOUND);
199
200 r = cursor->c_close(cursor); CKERR(r);
201
202 r = txn->commit(txn, 0); CKERR(r);
203 r = db->close(db, 0); CKERR(r);
204 }
205 r = env->close(env, 0); CKERR(r);
206 exit(0);
207 }
208
209 const char *cmd;
210
test_parse_args(int argc,char * const argv[])211 static void test_parse_args (int argc, char * const argv[]) {
212 int resultcode;
213 cmd = argv[0];
214 argc--; argv++;
215 while (argc>0) {
216 if (strcmp(argv[0], "-v") == 0) {
217 verbose++;
218 } else if (strcmp(argv[0],"-q")==0) {
219 verbose--;
220 if (verbose<0) verbose=0;
221 } else if (strcmp(argv[0], "--test")==0) {
222 do_test=true;
223 } else if (strcmp(argv[0], "--recover") == 0) {
224 do_recover=true;
225 } else if (strcmp(argv[0], "-h")==0) {
226 resultcode=0;
227 do_usage:
228 fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
229 exit(resultcode);
230 } else {
231 fprintf(stderr, "Unknown arg: %s\n", argv[0]);
232 resultcode=1;
233 goto do_usage;
234 }
235 argc--;
236 argv++;
237 }
238 }
239
test_main(int argc,char * const argv[])240 int test_main (int argc, char * const argv[]) {
241 test_parse_args(argc, argv);
242 if (do_test) {
243 run_test();
244 } else if (do_recover) {
245 run_recover();
246 }
247 return 0;
248 }
249