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