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