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 /* Idea:
40 * create a dictionary
41 * repeat:
42 * lots of inserts
43 * checkpoint
44 * note file size
45 * lots of deletes
46 * optimize (flatten tree)
47 * checkpoint
48 * note file size
49 *
50 */
51
52 #include "test.h"
53
54 #define PATHSIZE 1024
55
56 DB_ENV *env;
57 DB *db;
58 char dbname[] = "foo.db";
59 char path[PATHSIZE];
60
61 const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_PRIVATE;
62
63 int ninsert, nread, nread_notfound, nread_failed, ndelete, ndelete_notfound, ndelete_failed;
64
65 static TOKU_DB_FRAGMENTATION_S report;
66
67 static void
check_fragmentation(void)68 check_fragmentation(void) {
69 int r = db->get_fragmentation(db, &report);
70 CKERR(r);
71 }
72
73 static void
print_fragmentation(void)74 print_fragmentation(void) {
75 printf("Fragmentation:\n");
76 printf("\tTotal file size in bytes (file_size_bytes): %" PRIu64 "\n", report.file_size_bytes);
77 printf("\tCompressed User Data in bytes (data_bytes): %" PRIu64 "\n", report.data_bytes);
78 printf("\tNumber of blocks of compressed User Data (data_blocks): %" PRIu64 "\n", report.data_blocks);
79 printf("\tAdditional bytes used for checkpoint system (checkpoint_bytes_additional): %" PRIu64 "\n", report.checkpoint_bytes_additional);
80 printf("\tAdditional blocks used for checkpoint system (checkpoint_blocks_additional): %" PRIu64 "\n", report.checkpoint_blocks_additional);
81 printf("\tUnused space in file (unused_bytes): %" PRIu64 "\n", report.unused_bytes);
82 printf("\tNumber of contiguous regions of unused space (unused_blocks): %" PRIu64 "\n", report.unused_blocks);
83 printf("\tSize of largest contiguous unused space (largest_unused_block): %" PRIu64 "\n", report.largest_unused_block);
84 }
85
86 static void
close_em(void)87 close_em (void)
88 {
89 int r;
90 r = db->close(db, 0); CKERR(r);
91 r = env->close(env, 0); CKERR(r);
92 }
93
94
95 static void
setup(void)96 setup(void)
97 {
98 int r;
99 toku_os_recursive_delete(TOKU_TEST_FILENAME);
100 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
101 r = db_env_create(&env, 0); CKERR(r);
102 r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
103 r = db_create(&db, env, 0); CKERR(r);
104 r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
105 }
106
107
108 static void
fill_rand(int n,uint64_t * d)109 fill_rand(int n, uint64_t * d) {
110 for (int i = 0; i < n; i++){
111 *(d+i) = random64();
112 }
113 }
114
115 #define INSERT_BIG 1500
116 #define INSERT_SMALL 0
117 static void
insert_n(uint32_t ah,int datasize)118 insert_n (uint32_t ah, int datasize) {
119 uint64_t vdata[datasize];
120 fill_rand(datasize, vdata);
121 uint32_t an = htonl(ah);
122 // if (verbose) printf("insert an = %0X (ah = %0X)\n", an, ah);
123 DBT key;
124 dbt_init(&key, &an, 4);
125 DBT val;
126 dbt_init(&val, vdata, sizeof vdata);
127 int r = db->put(db, NULL, &key, &val, 0);
128 CKERR(r);
129 ninsert++;
130 }
131
132 static void
delete_n(uint32_t ah)133 delete_n (uint32_t ah)
134 {
135 uint32_t an = htonl(ah);
136 // if (verbose) printf("delete an = %0X (ah = %0X)\n", an, ah);
137 DBT key;
138 dbt_init(&key, &an, 4);
139 int r = db->del(db, NULL, &key, DB_DELETE_ANY);
140 if (r == 0)
141 ndelete++;
142 else if (r == DB_NOTFOUND)
143 ndelete_notfound++;
144 else
145 ndelete_failed++;
146 CKERR(r);
147 }
148
149 static void
optimize(void)150 optimize(void) {
151 if (verbose) printf("Filesize: begin optimize dictionary\n");
152 uint64_t loops_run;
153 int r = db->hot_optimize(db, NULL, NULL, NULL, NULL, &loops_run);
154 CKERR(r);
155 if (verbose) printf("Filesize: end optimize dictionary\n");
156 }
157
158
159 static void
get_file_pathname(void)160 get_file_pathname(void) {
161 DBT dname;
162 DBT iname;
163 dbt_init(&dname, dbname, sizeof(dbname));
164 dbt_init(&iname, NULL, 0);
165 iname.flags |= DB_DBT_MALLOC;
166 int r = env->get_iname(env, &dname, &iname);
167 CKERR(r);
168 sprintf(path, "%s/%s", TOKU_TEST_FILENAME, (char*)iname.data);
169 toku_free(iname.data);
170 if (verbose) printf("path = %s\n", path);
171 }
172
getsizeM(void)173 static int getsizeM(void) {
174 toku_struct_stat buf;
175 int r = toku_stat(path, &buf, toku_uninstrumented);
176 CKERR(r);
177 int sizeM = (int)buf.st_size >> 20;
178 check_fragmentation();
179 if (verbose>1)
180 print_fragmentation();
181 return sizeM;
182 }
183
184 static void
test_filesize(bool sequential)185 test_filesize (bool sequential)
186 {
187 int N=1<<14;
188 int r, i, sizeM;
189
190 get_file_pathname();
191
192 for (int iter = 0; iter < 3; iter++) {
193 int offset = N * iter;
194
195 if (sequential) {
196 for (i=0; i<N; i++) {
197 insert_n(i + offset, INSERT_BIG);
198 }
199 } else {
200 for (i=N-1; i>=0; --i) {
201 insert_n(i + offset, INSERT_BIG);
202 }
203 }
204
205 r = env->txn_checkpoint(env, 0, 0, 0);
206 CKERR(r);
207 int sizefirst = sizeM = getsizeM();
208 if (verbose) printf("Filesize after iteration %d insertion and checkpoint = %dM\n", iter, sizeM);
209
210 int preserve = 2;
211 for (i = preserve; i<(N); i++) { // leave a little at the beginning
212 delete_n(i + offset);
213 }
214 optimize();
215
216 r = env->txn_checkpoint(env, 0, 0, 0);
217 CKERR(r);
218 sizeM = getsizeM();
219 if (verbose) printf("Filesize after iteration %d deletion and checkpoint 1 = %dM\n", iter, sizeM);
220
221 if (sequential) {
222 for (i=0; i<N; i++) {
223 insert_n(i + offset, INSERT_SMALL);
224 }
225 } else {
226 for (i=N-1; i>=0; --i) {
227 insert_n(i + offset, INSERT_SMALL);
228 }
229 }
230 for (i = preserve; i<(N); i++) { // leave a little at the beginning
231 delete_n(i + offset);
232 }
233 optimize();
234 r = env->txn_checkpoint(env, 0, 0, 0);
235 CKERR(r);
236 sizeM = getsizeM();
237 if (verbose) printf("Filesize after iteration %d deletion and checkpoint 2 = %dM\n", iter, sizeM);
238 assert(sizeM <= sizefirst);
239
240 if (verbose) printf("ninsert = %d\n", ninsert);
241 if (verbose) printf("nread = %d, nread_notfound = %d, nread_failed = %d\n", nread, nread_notfound, nread_failed);
242 if (verbose) printf("ndelete = %d, ndelete_notfound = %d, ndelete_failed = %d\n", ndelete, ndelete_notfound, ndelete_failed);
243 }
244 }
245
test_main(int argc,char * const argv[])246 int test_main (int argc __attribute__((__unused__)), char * const argv[] __attribute__((__unused__))) {
247 parse_args(argc, argv);
248 setup();
249 if (verbose) print_engine_status(env);
250 test_filesize(true);
251 if (verbose) {
252 print_engine_status(env);
253 }
254 check_fragmentation();
255 if (verbose) print_fragmentation();
256 close_em();
257 setup();
258 if (verbose) print_engine_status(env);
259 test_filesize(false);
260 if (verbose) {
261 print_engine_status(env);
262 }
263 check_fragmentation();
264 if (verbose) print_fragmentation();
265 close_em();
266 return 0;
267 }
268