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