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