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 assert(src_db != NULL);
62 assert(dest_db->descriptor->dbt.size == 4);
63 uint32_t which = *(uint32_t*)dest_db->descriptor->dbt.data;
64 assert(which < num_dbs);
65
66 if (dest_key->data) toku_free(dest_key->data);
67 if (dest_val->data) toku_free(dest_val->data);
68 dest_key->data = toku_xmemdup (src_key->data, src_key->size);
69 dest_key->size = src_key->size;
70 dest_val->data = toku_xmemdup (src_val->data, src_val->size);
71 dest_val->size = src_val->size;
72 return 0;
73 }
74
run_test(void)75 static void run_test (void) {
76 int r;
77
78 toku_os_recursive_delete(TOKU_TEST_FILENAME);
79 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
80
81 DB_ENV *env;
82 r = db_env_create(&env, 0); CKERR(r);
83 r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
84 CKERR(r);
85 r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
86
87 // create a txn that never closes, forcing recovery to run from the beginning of the log
88 {
89 DB_TXN *oldest_living_txn;
90 r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
91 }
92
93 DBT descriptor;
94 uint32_t which;
95 for (which = 0; which < num_dbs; which++) {
96 dbt_init_realloc(&dest_keys[which]);
97 dbt_init_realloc(&dest_vals[which]);
98 }
99 dbt_init(&descriptor, &which, sizeof(which));
100 DB *dba;
101 DB *dbb;
102 r = db_create(&dba, env, 0); CKERR(r);
103 r = db_create(&dbb, env, 0); CKERR(r);
104 r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
105 which = 0;
106 IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
107 { int chk_r = dba->change_descriptor(dba, txn_desc, &descriptor, 0); CKERR(chk_r); }
108 });
109 r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
110 which = 1;
111 IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
112 { int chk_r = dbb->change_descriptor(dbb, txn_desc, &descriptor, 0); CKERR(chk_r); }
113 });
114
115 r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
116
117 DB *dbs[num_dbs] = {dba, dbb};
118 uint32_t flags[num_dbs] = {0, 0};
119 // txn_begin; insert <a,a>; txn_abort
120 {
121 DB_TXN *txn;
122 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
123 DBT k,v;
124 dbt_init(&k, "a", 2);
125 dbt_init(&v, "b", 2);
126
127 r = env_put_multiple_test_no_array(env, num_dbs > 0 ? dbs[0] : NULL, 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
144 r = env_put_multiple_test_no_array(env, num_dbs > 0 ? dbs[0] : NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
145 CKERR(r);
146 r = txn->commit(txn, 0); CKERR(r);
147 }
148 {
149 DB_TXN *txn;
150 r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
151 r = dba->close(dba, 0); CKERR(r);
152 r = env->dbremove(env, txn, namea, NULL, 0); CKERR(r);
153 r = dbb->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, namea, 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, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
188 r = db->close(db, 0); CKERR(r);
189 }
190 r = env->close(env, 0); CKERR(r);
191 exit(0);
192 }
193
194 const char *cmd;
195
test_parse_args(int argc,char * const argv[])196 static void test_parse_args (int argc, char * const argv[]) {
197 int resultcode;
198 cmd = argv[0];
199 argc--; argv++;
200 while (argc>0) {
201 if (strcmp(argv[0], "-v") == 0) {
202 verbose++;
203 } else if (strcmp(argv[0],"-q")==0) {
204 verbose--;
205 if (verbose<0) verbose=0;
206 } else if (strcmp(argv[0], "--test")==0) {
207 do_test=true;
208 } else if (strcmp(argv[0], "--recover") == 0) {
209 do_recover=true;
210 } else if (strcmp(argv[0], "-h")==0) {
211 resultcode=0;
212 do_usage:
213 fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
214 exit(resultcode);
215 } else {
216 fprintf(stderr, "Unknown arg: %s\n", argv[0]);
217 resultcode=1;
218 goto do_usage;
219 }
220 argc--;
221 argv++;
222 }
223 }
224
test_main(int argc,char * const argv[])225 int test_main (int argc, char * const argv[]) {
226 test_parse_args(argc, argv);
227 if (do_test) {
228 run_test();
229 } else if (do_recover) {
230 run_recover();
231 }
232 return 0;
233 }
234