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