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