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 #include "test.h"
40 
41 // verify recovery of an update log entry which changes values at keys
42 
43 static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
44 static const unsigned int NUM_KEYS = 100;
45 
should_update(const unsigned int k)46 static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
47 
_v(const unsigned int k)48 static inline unsigned int _v(const unsigned int k) { return 10 - k; }
_e(const unsigned int k)49 static inline unsigned int _e(const unsigned int k) { return k + 4; }
_u(const unsigned int v,const unsigned int e)50 static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
51 
update_fun(DB * UU (db),const DBT * key,const DBT * old_val,const DBT * extra,void (* set_val)(const DBT * new_val,void * set_extra),void * set_extra)52 static int update_fun(DB *UU(db),
53                       const DBT *key,
54                       const DBT *old_val, const DBT *extra,
55                       void (*set_val)(const DBT *new_val,
56                                       void *set_extra),
57                       void *set_extra)
58 {
59     unsigned int *k, *ov, v;
60     assert(key->size == sizeof(*k));
61     CAST_FROM_VOIDP(k, key->data);
62     assert(old_val->size == sizeof(*ov));
63     CAST_FROM_VOIDP(ov, old_val->data);
64     assert(extra->size == 0);
65     v = _u(*ov, _e(*k));
66 
67     if (should_update(*k)) {
68         DBT newval;
69         set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
70     }
71 
72     return 0;
73 }
74 
do_inserts(DB_TXN * txn,DB * db)75 static int do_inserts(DB_TXN *txn, DB *db)
76 {
77     int r = 0;
78     DBT key, val;
79     unsigned int i, v;
80     DBT *keyp = dbt_init(&key, &i, sizeof(i));
81     DBT *valp = dbt_init(&val, &v, sizeof(v));
82     for (i = 0; i < NUM_KEYS; ++i) {
83         v = _v(i);
84         r = db->put(db, txn, keyp, valp, 0);
85         CKERR(r);
86     }
87     return r;
88 }
89 
do_updates(DB_TXN * txn,DB * db)90 static int do_updates(DB_TXN *txn, DB *db) {
91     DBT extra;
92     DBT *extrap = dbt_init(&extra, NULL, 0);
93     int r = db->update_broadcast(db, txn, extrap, 0);
94     CKERR(r);
95     return r;
96 }
97 
98 DB_ENV *env;
99 DB *db;
100 
checkpoint_callback_1(void * extra)101 static void checkpoint_callback_1(void * extra) {
102     assert(extra == NULL);
103     IN_TXN_ABORT(env, NULL, txn_2, 0, {
104             { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
105         });
106 }
107 
run_test(void)108 static void run_test(void)
109 {
110 
111     toku_os_recursive_delete(TOKU_TEST_FILENAME);
112     { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
113     { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
114     db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
115     env->set_errfile(env, stderr);
116     env->set_update(env, update_fun);
117     { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
118 
119     IN_TXN_COMMIT(env, NULL, txn_1, 0, {
120             { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
121             { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
122 
123             { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
124         });
125 
126     { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
127 
128     toku_hard_crash_on_purpose();
129 }
130 
verify_unchanged(void)131 static int verify_unchanged(void)
132 {
133     int r = 0;
134     DBT key, val;
135     unsigned int i, *vp;
136     DBT *keyp = dbt_init(&key, &i, sizeof(i));
137     DBT *valp = dbt_init(&val, NULL, 0);
138 
139     IN_TXN_COMMIT(env, NULL, txn_1, 0, {
140             for (i = 0; i < NUM_KEYS; ++i) {
141                 r = db->get(db, txn_1, keyp, valp, 0);
142                 CKERR(r);
143                 assert(val.size == sizeof(*vp));
144                 CAST_FROM_VOIDP(vp, val.data);
145                 assert(*vp == _v(i));
146             }
147         });
148 
149     return r;
150 }
151 
run_recover(void)152 static void run_recover(void)
153 {
154     { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
155     env->set_errfile(env, stderr);
156     env->set_update(env, update_fun);
157     { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
158     { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
159     { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
160     { int chk_r = verify_unchanged(); CKERR(chk_r); }
161     { int chk_r = db->close(db, 0); CKERR(chk_r); }
162     { int chk_r = env->close(env, 0); CKERR(chk_r); }
163 }
164 
usage(void)165 static int usage(void)
166 {
167     return 1;
168 }
169 
test_main(int argc,char * const argv[])170 int test_main(int argc, char * const argv[])
171 {
172     bool do_test = false;
173     bool do_recover = false;
174 
175     for (int i = 1; i < argc; i++) {
176         char * const arg = argv[i];
177         if (strcmp(arg, "-v") == 0) {
178             verbose++;
179             continue;
180         }
181         if (strcmp(arg, "-q") == 0) {
182             verbose--;
183             if (verbose < 0)
184                 verbose = 0;
185             continue;
186         }
187         if (strcmp(arg, "--test") == 0) {
188             do_test = true;
189             continue;
190         }
191         if (strcmp(arg, "--recover") == 0) {
192             do_recover = true;
193             continue;
194         }
195         if (strcmp(arg, "--help") == 0) {
196             return usage();
197         }
198     }
199 
200     if (do_test) {
201         run_test();
202     }
203     if (do_recover) {
204         run_recover();
205     }
206 
207     return 0;
208 }
209