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
119 DB *dbs[num_dbs] = {dba, dbb};
120 uint32_t flags[num_dbs] = {0, 0};
121 // txn_begin; insert <a,a>; txn_abort
122 {
123 DB_TXN *txn;
124 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
125 DBT k,v;
126 dbt_init(&k, "a", 2);
127 dbt_init(&v, "b", 2);
128
129 r = env_put_multiple_test_no_array(env, dba, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
130 CKERR(r);
131 r = txn->abort(txn); CKERR(r);
132 }
133 r = dbb->close(dbb, 0); CKERR(r);
134 r = db_create(&dbb, env, 0); CKERR(r);
135 r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(r);
136 dbs[1] = dbb;
137
138 // txn_begin; insert <a,b>;
139 {
140 DB_TXN *txn;
141 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
142 DBT k,v;
143 dbt_init(&k, "a", 2);
144 dbt_init(&v, "b", 2);
145
146 r = env_put_multiple_test_no_array(env, NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
147 CKERR(r);
148 r = txn->commit(txn, 0); CKERR(r);
149 }
150 {
151 DB_TXN *txn;
152 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
153 r = dba->close(dbb, 0); CKERR(r);
154 r = env->dbremove(env, txn, nameb, NULL, 0); CKERR(r);
155 r = txn->commit(txn, 0); CKERR(r);
156 }
157
158 r = env->log_flush(env, NULL); CKERR(r);
159 // abort the process
160 toku_hard_crash_on_purpose();
161 }
162
163
run_recover(void)164 static void run_recover (void) {
165 DB_ENV *env;
166 int r;
167
168 // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
169 // so recovery always runs over the entire log.
170
171 // run recovery
172 r = db_env_create(&env, 0); CKERR(r);
173 r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
174 CKERR(r);
175 r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
176
177 // verify the data
178 {
179 DB *db;
180 r = db_create(&db, env, 0); CKERR(r);
181 r = db->open(db, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
182 r = db->close(db, 0); CKERR(r);
183 }
184 {
185 DB *db;
186 r = db_create(&db, env, 0); CKERR(r);
187 r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
188
189 DB_TXN *txn;
190 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
191 DBC *cursor;
192 r = db->cursor(db, txn, &cursor, 0); CKERR(r);
193 DBT k, v;
194 r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
195 CKERR(r);
196 assert(k.size == 2);
197 assert(v.size == 2);
198 assert(memcmp(k.data, "a", 2) == 0);
199 assert(memcmp(v.data, "b", 2) == 0);
200 toku_free(k.data);
201 toku_free(v.data);
202
203 r = cursor->c_close(cursor); CKERR(r);
204
205 r = txn->commit(txn, 0); CKERR(r);
206 r = db->close(db, 0); CKERR(r);
207 }
208 r = env->close(env, 0); CKERR(r);
209 exit(0);
210 }
211
212 const char *cmd;
213
test_parse_args(int argc,char * const argv[])214 static void test_parse_args (int argc, char * const argv[]) {
215 int resultcode;
216 cmd = argv[0];
217 argc--; argv++;
218 while (argc>0) {
219 if (strcmp(argv[0], "-v") == 0) {
220 verbose++;
221 } else if (strcmp(argv[0],"-q")==0) {
222 verbose--;
223 if (verbose<0) verbose=0;
224 } else if (strcmp(argv[0], "--test")==0) {
225 do_test=true;
226 } else if (strcmp(argv[0], "--recover") == 0) {
227 do_recover=true;
228 } else if (strcmp(argv[0], "-h")==0) {
229 resultcode=0;
230 do_usage:
231 fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
232 exit(resultcode);
233 } else {
234 fprintf(stderr, "Unknown arg: %s\n", argv[0]);
235 resultcode=1;
236 goto do_usage;
237 }
238 argc--;
239 argv++;
240 }
241 }
242
test_main(int argc,char * const argv[])243 int test_main (int argc, char * const argv[]) {
244 test_parse_args(argc, argv);
245 if (do_test) {
246 run_test();
247 } else if (do_recover) {
248 run_recover();
249 }
250 return 0;
251 }
252