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_COMMIT(env, NULL, txn_2, 0, {
104             { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
105         });
106 }
107 
108 
run_test(void)109 static void run_test(void)
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 
129     toku_hard_crash_on_purpose();
130 }
131 
verify_updated(void)132 static int verify_updated(void)
133 {
134     int r = 0;
135     DBT key, val;
136     unsigned int i, *vp;
137     DBT *keyp = dbt_init(&key, &i, sizeof(i));
138     DBT *valp = dbt_init(&val, NULL, 0);
139 
140     IN_TXN_COMMIT(env, NULL, txn_1, 0, {
141             for (i = 0; i < NUM_KEYS; ++i) {
142                 r = db->get(db, txn_1, keyp, valp, 0);
143                 CKERR(r);
144                 assert(val.size == sizeof(*vp));
145                 CAST_FROM_VOIDP(vp, val.data);
146                 if (should_update(i)) {
147                     assert(*vp == _u(_v(i), _e(i)));
148                 } else {
149                     assert(*vp == _v(i));
150                 }
151             }
152         });
153 
154     return r;
155 }
156 
run_recover(void)157 static void run_recover(void)
158 {
159     { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
160     env->set_errfile(env, stderr);
161     env->set_update(env, update_fun);
162     { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
163     { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
164     { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
165     { int chk_r = verify_updated(); CKERR(chk_r); }
166     { int chk_r = db->close(db, 0); CKERR(chk_r); }
167     { int chk_r = env->close(env, 0); CKERR(chk_r); }
168 }
169 
usage(void)170 static int usage(void)
171 {
172     return 1;
173 }
174 
test_main(int argc,char * const argv[])175 int test_main(int argc, char * const argv[])
176 {
177     bool do_test = false;
178     bool do_recover = false;
179 
180     for (int i = 1; i < argc; i++) {
181         char * const arg = argv[i];
182         if (strcmp(arg, "-v") == 0) {
183             verbose++;
184             continue;
185         }
186         if (strcmp(arg, "-q") == 0) {
187             verbose--;
188             if (verbose < 0)
189                 verbose = 0;
190             continue;
191         }
192         if (strcmp(arg, "--test") == 0) {
193             do_test = true;
194             continue;
195         }
196         if (strcmp(arg, "--recover") == 0) {
197             do_recover = true;
198             continue;
199         }
200         if (strcmp(arg, "--help") == 0) {
201             return usage();
202         }
203     }
204 
205     if (do_test) {
206         run_test();
207     }
208     if (do_recover) {
209         run_recover();
210     }
211 
212     return 0;
213 }
214