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 /**
40  * Test that various queries behave correctly
41  *
42  * Zardosht says:
43  *
44  * write a test that inserts a bunch of elements into the tree,
45  * and then verify that the following types of queries work:
46  * - db->get
47  * - next
48  * - prev
49  * - set_range
50  * - set_range_reverse
51  * - first
52  * - last
53  * - current
54  *
55  * do it on a table with:
56  * - just a leaf node
57  * - has internal nodes (make node size 4K and bn size 1K)
58  * - big cachetable such that everything fits
59  * - small cachetable such that not a lot fits
60  *
61  * make sure APIs are the callback APIs (getf_XXX)
62  * make sure your callbacks all return TOKUDB_CURSOR_CONTINUE,
63  * so we ensure that returning TOKUDB_CURSOR_CONTINUE does not
64  * mess anything up.
65  */
66 
67 #include "test.h"
68 
69 /**
70  * Calculate or verify that a value for a given key is correct
71  * Returns 0 if the value is correct, nonzero otherwise.
72  */
get_value_by_key(DBT * key,DBT * value)73 static void get_value_by_key(DBT * key, DBT * value)
74 {
75     // keys/values are always stored in the DBT in net order
76     int * CAST_FROM_VOIDP(k, key->data);
77     int v = toku_ntohl(*k) * 2 + 1;
78     memcpy(value->data, &v, sizeof(int));
79 }
80 
prepare_for_env(void)81 static void prepare_for_env(void) {
82     toku_os_recursive_delete(TOKU_TEST_FILENAME);
83     int r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
84 }
85 
init_env(DB_ENV ** env,size_t ct_size)86 static void init_env(DB_ENV ** env, size_t ct_size)
87 {
88     int r;
89     const int envflags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
90         DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
91 
92     printf("initializing environment\n");
93 
94     r = db_env_create(env, 0); { int chk_r = r; CKERR(chk_r); }
95     assert(ct_size < 1024 * 1024 * 1024L);
96     r = (*env)->set_cachesize(*env, 0, ct_size, 1); { int chk_r = r; CKERR(chk_r); }
97     r = (*env)->open(*env, TOKU_TEST_FILENAME, envflags, 0755); { int chk_r = r; CKERR(chk_r); }
98 }
99 
init_db(DB_ENV * env,DB ** db)100 static void init_db(DB_ENV * env, DB ** db)
101 {
102     int r;
103     const int node_size = 4096;
104     const int bn_size = 1024;
105 
106     printf("initializing db\n");
107 
108     DB_TXN * txn;
109     r = db_create(db, env, 0); { int chk_r = r; CKERR(chk_r); }
110     r = (*db)->set_readpagesize(*db, bn_size); { int chk_r = r; CKERR(chk_r); }
111     r = (*db)->set_pagesize(*db, node_size); { int chk_r = r; CKERR(chk_r); }
112     r = env->txn_begin(env, nullptr, &txn, 0); { int chk_r = r; CKERR(chk_r); }
113     r = (*db)->open(*db, txn, "db", nullptr, DB_BTREE, DB_CREATE, 0644); { int chk_r = r; CKERR(chk_r); }
114     r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
115 }
116 
cleanup_env_and_db(DB_ENV * env,DB * db)117 static void cleanup_env_and_db(DB_ENV * env, DB * db)
118 {
119     int r;
120 
121     printf("cleaning up environment and db\n");
122     r = db->close(db, 0); { int chk_r = r; CKERR(chk_r); }
123     r = env->close(env, 0); { int chk_r = r; CKERR(chk_r); }
124 }
125 
get_last_key_cb(const DBT * key,const DBT * value,void * extra)126 static int get_last_key_cb(const DBT *key, const DBT *value, void *extra) {
127     if (key->data) {
128         invariant_null(value);
129         int expected_key = *(int*)extra;
130         int found_key = *(int*)key->data;
131         invariant(expected_key == (int)ntohl(found_key));
132     }
133     return 0;
134 }
135 
136 
check_last_key_matches(DB * db,int expect_r,int key)137 static void check_last_key_matches(DB *db, int expect_r, int key) {
138     int r = db->get_last_key(db, get_last_key_cb, &key);
139     CKERR2(r, expect_r);
140 }
141 
do_test(size_t ct_size,int num_keys)142 static void do_test(size_t ct_size, int num_keys)
143 {
144     int i, r;
145     DB * db;
146     DB_ENV * env;
147     DB_TXN *txn = nullptr;
148     DB_TXN *txn2 = nullptr;
149     uint64_t loops_run = 0;
150 
151 
152     printf("doing tests for ct_size %lu, num_keys %d\n",
153             ct_size, num_keys);
154 
155     // initialize everything and insert data
156     prepare_for_env();
157     init_env(&env, ct_size);
158     assert(env != nullptr);
159     init_db(env, &db);
160     assert(db != nullptr);
161 
162     r = env->txn_begin(env, nullptr, &txn, 0);
163     CKERR(r);
164     {
165         DBT key, value;
166         for (i = 0; i < num_keys; i++) {
167             int v, k = toku_htonl(i);
168             dbt_init(&key, &k, sizeof(int));
169             dbt_init(&value, &v, sizeof(int));
170             get_value_by_key(&key, &value);
171             if (0) printf("put %d\n", k);
172             r = db->put(db, txn, &key, &value, 0);
173             CKERR(r);
174         }
175     }
176 
177     int expect_r = num_keys == 0 ? DB_NOTFOUND : 0;
178     check_last_key_matches(db, expect_r, num_keys - 1);
179 
180     r = txn->commit(txn, 0);
181     check_last_key_matches(db, expect_r, num_keys - 1);
182 
183     if (num_keys == 0) {
184         goto cleanup;
185     }
186     r = env->txn_begin(env, nullptr, &txn2, 0);
187     CKERR(r);
188     r = env->txn_begin(env, nullptr, &txn, 0);
189     CKERR(r);
190 
191     // Delete the last key
192     {
193         DBT key;
194         int k = toku_htonl(num_keys - 1);
195         dbt_init(&key, &k, sizeof(int));
196         if (0) printf("del %d\n", *(int*)key.data);
197         r = db->del(db, txn, &key, 0);
198         CKERR(r);
199     }
200     check_last_key_matches(db, 0, num_keys - 1);
201 
202     r = txn->commit(txn, 0);
203     CKERR(r);
204     check_last_key_matches(db, 0, num_keys - 1);
205 
206     r = txn2->commit(txn2, 0);
207     CKERR(r);
208     check_last_key_matches(db, 0, num_keys - 1);
209 
210     //Run Garbage collection (NOTE does not work when everything fits in root??? WHY)
211     r = db->hot_optimize(db, nullptr, nullptr, nullptr, nullptr, &loops_run);
212     CKERR(r);
213 
214     r = env->txn_checkpoint(env, 0, 0, 0);
215     CKERR(r);
216 
217     //Run Garbage collection (NOTE does not work when everything fits in root??? WHY)
218     r = db->hot_optimize(db, nullptr, nullptr, nullptr, nullptr, &loops_run);
219     CKERR(r);
220 
221     r = env->txn_checkpoint(env, 0, 0, 0);
222     CKERR(r);
223 
224     //Fully close and reopen
225     //This clears cachetable
226     //note that closing a db and reopening may not flush the cachetable so we close env as well
227     cleanup_env_and_db(env, db);
228     init_env(&env, ct_size);
229     assert(env != nullptr);
230     init_db(env, &db);
231     assert(db != nullptr);
232 
233     //NOTE: tried overkill (double optimize, double checkpoint.. gc still doesn't happen for everything in root in single basement
234 
235     if (num_keys >= 2) {
236         // At least one key remains.
237         check_last_key_matches(db, 0, num_keys - 2);
238     } else {
239         //no key remains.  Should find nothing.
240         check_last_key_matches(db, DB_NOTFOUND, -1);
241     }
242 cleanup:
243     cleanup_env_and_db(env, db);
244 }
245 
test_main(int argc,char * const argv[])246 int test_main(int argc, char * const argv[])
247 {
248     default_parse_args(argc, argv);
249 
250     for (int i = 0; i <= 2; i++) {
251         do_test(1024*1024, i);
252     }
253     for (int i = 4; i <= 1024; i*=2) {
254         do_test(1024*1024, i);
255     }
256 
257     return 0;
258 }
259 
260