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