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 #include "toku_pthread.h"
41 #include <db.h>
42 #include <sys/stat.h>
43 #include "ydb-internal.h"
44 
45 #include "test_kv_gen.h"
46 
47 /****************************************************************************************
48  *
49  * Test sequence is run four times, two in outer loop, two in inner loop
50  * Outer loop is for default or small node and cachetable sizes,
51  * inner loop is for insert and delete.
52  *
53  * open dbs
54  * read and verify first n rows of primary, a few interspersed rows of secondaries  (n is very small so only a few nodes of secondaries are upgraded, even with prefetch)
55  * close dbs  (dictionaries now partially upgraded)
56  * open dbs
57  * read and verify a few more rows of primary, a few more interspersed rows of secondaries
58  * close dbs  (some more nodes now upgraded)
59  * open dbs
60  * if (insert test)
61  *   insert at end of primary and interspersed in secondary dictionaries
62  * else (delete test)
63  *   delete from beginning of primary and interspersed in secondary dictionaries
64  * close dbs
65  * open dbs
66  * verify all rows (including newly inserted ones)
67  * close dbs
68  *
69  */
70 
71 DB_ENV *env;
72 enum {MAX_NAME=128};
73 int NUM_DBS=5;
74 int NUM_ROWS=100000;
75 int CHECK_RESULTS=0;
76 int SRC_VERSION = 4;
77 int littlenode = 0;
78 
79 #define OLDDATADIR "../../../../tokudb.data/"
80 
81 char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
82 char *db_v5_dir = "dir.preload-db.c.tdb";
83 char *db_v4_dir        = OLDDATADIR "env_preload.4.2.0.cleanshutdown";
84 char *db_v4_dir_node4k = OLDDATADIR "env_preload.4.2.0.node4k.cleanshutdown";
85 
86 
87 enum {ROWS_PER_TRANSACTION=10000};
88 
89 static int idx[MAX_DBS];
90 
91 typedef enum {insert, delete} test_type;
92 
93 static void
open_dbs(DB ** dbs)94 open_dbs(DB **dbs) {
95     int r;
96     DBT desc;
97     dbt_init(&desc, "foo", sizeof("foo"));
98     char name[MAX_NAME*2];
99 
100     for(int i=0;i<NUM_DBS;i++) {
101 	idx[i] = i;
102 	r = db_create(&dbs[i], env, 0);                                                                       CKERR(r);
103 	dbs[i]->app_private = &idx[i];
104 	snprintf(name, sizeof(name), "db_%04x", i);
105 	r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666);                                CKERR(r);
106     IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
107             { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
108     });
109     }
110 }
111 
112 
113 static void
close_dbs(DB ** dbs)114 close_dbs(DB **dbs) {
115     for(int i=0;i<NUM_DBS;i++) {
116 	int r = dbs[i]->close(dbs[i], 0);                                                                          CKERR(r);
117 	dbs[i] = NULL;
118     }
119 }
120 
121 
upgrade_test_4(DB ** dbs,test_type test_to_do)122 static void upgrade_test_4(DB **dbs, test_type test_to_do) {
123     int r;
124     int n = 4;  // number of rows to check to partially upgrade dictionary
125     char * msg;
126     if (test_to_do == insert)
127 	msg = "insert";
128     else if (test_to_do == delete)
129 	msg = "delete";
130     else assert(0);
131 
132 
133     // open the DBS
134     open_dbs(dbs);
135 
136     // check first few rows of primary, some (pseudo)random rows of secondaries
137     {
138 	check_results(env, dbs, NUM_DBS, n);
139 	if (verbose)
140 	    printf("First %d rows checked, now close and reopen\n", n);
141     }
142 
143     // close and reopen
144     close_dbs(dbs);
145     open_dbs(dbs);
146 
147     // check first few rows of primary, some (pseudo)random rows of secondaries
148     {
149 	n *= 2;
150 	check_results(env, dbs, NUM_DBS, n);
151 	if (verbose)
152 	    printf("\nFirst %d rows checked, now %s some rows\n", n, msg);
153     }
154 
155     // close and reopen
156     close_dbs(dbs);
157     open_dbs(dbs);
158 
159     // insert or delete some rows
160     DB_TXN    *txn;
161     DBT skey, sval;
162     DBT key, val;
163     dbt_init_realloc(&key);
164     dbt_init_realloc(&val);
165 
166     unsigned int k, v;
167     if ( verbose ) {
168 	printf("%s some rows\n", msg);
169 	fflush(stdout);
170     }
171     int num_rows_to_modify, base;
172     if (test_to_do == insert) {
173 	num_rows_to_modify = NUM_ROWS;
174 	base = NUM_ROWS;  // insert after existing rows in primary
175     }
176     else if (test_to_do == delete) {
177 	num_rows_to_modify = 2*n;
178 	base = 0;  // delete some rows from primary
179     }
180     else assert(0);
181     int outer_loop_num = ( num_rows_to_modify <= ROWS_PER_TRANSACTION ) ? 1 : (num_rows_to_modify / ROWS_PER_TRANSACTION);
182     for(int x=0;x<outer_loop_num;x++) {
183         r = env->txn_begin(env, NULL, &txn, 0);                                                              CKERR(r);
184         for(int i=1; (i<=ROWS_PER_TRANSACTION && i<=num_rows_to_modify); i++) {
185             k = i + (x*ROWS_PER_TRANSACTION) + base;
186             v = generate_val(k, 0);
187             dbt_init(&skey, &k, sizeof(unsigned int));
188             dbt_init(&sval, &v, sizeof(unsigned int));
189 
190             for(int db = 0;db < NUM_DBS;db++) {
191                 put_multiple_generate(dbs[db], // dest_db
192                                       NULL, // src_db, ignored
193                                       &key, &val, //
194                                       &skey, &sval, // src_key, src_val
195                                       NULL); // extra, ignored
196 		if (test_to_do == insert) {
197 		    r = dbs[db]->put(dbs[db], txn, &key, &val, 0);
198 		    CKERR(r);
199 		}
200 		else if (test_to_do == delete) {
201 		    r = dbs[db]->del(dbs[db], txn, &key, 0);
202 		    CKERR(r);
203 		}
204 		else assert(0);
205 
206                 if (key.flags == 0) { dbt_init_realloc(&key); }
207                 if (val.flags == 0) { dbt_init_realloc(&val); }
208             }
209         }
210         r = txn->commit(txn, 0);                                                                             CKERR(r);
211         if ( verbose ) {printf(".");fflush(stdout);}
212     }
213     if ( key.flags ) { toku_free(key.data); key.data = NULL; }
214     if ( val.flags ) { toku_free(val.data); key.data = NULL; }
215 
216     // close
217     close_dbs(dbs);
218 
219     // open
220     open_dbs(dbs);
221 
222     // read and verify all rows
223     {
224         if ( verbose ) {printf("\nchecking");fflush(stdout);}
225 	if (test_to_do == insert)
226 	    check_results(env, dbs, NUM_DBS, NUM_ROWS * 2);
227 	else if (test_to_do == delete)
228 	    check_results_after_row_n(env, dbs, NUM_DBS, NUM_ROWS, num_rows_to_modify);
229 	else assert(0);
230         if ( verbose) {printf("\ndone\n");fflush(stdout);}
231     }
232     // close
233     {
234         for(int i=0;i<NUM_DBS;i++) {
235             r = dbs[i]->close(dbs[i], 0);                                                                         CKERR(r);
236             dbs[i] = NULL;
237         }
238     }
239 }
240 
setup(void)241 static void setup(void) {
242     int r;
243     int len = 256;
244     char syscmd[len];
245     char * src_db_dir;
246 
247     if ( SRC_VERSION == 4 ) {
248 	if (littlenode)
249 	    src_db_dir = db_v4_dir_node4k;
250 	else
251 	    src_db_dir = db_v4_dir;
252     }
253     else if ( SRC_VERSION == 5 ) {
254         src_db_dir = db_v5_dir;
255     }
256     else {
257         fprintf(stderr, "unsupported PerconaFT version %d to upgrade\n", SRC_VERSION);
258         assert(0);
259     }
260 
261     r = snprintf(syscmd, len, "rm -rf %s", env_dir);
262     assert(r<len);
263     r = system(syscmd);
264     CKERR(r);
265 
266     r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
267     assert(r<len);
268     r = system(syscmd);
269     CKERR(r);
270     generate_permute_tables();
271 
272 }
273 
run_test(test_type test_to_do)274 static void run_test(test_type test_to_do)
275 {
276     int r;
277 
278     r = db_env_create(&env, 0);                                                                               CKERR(r);
279     if (littlenode) {
280 	r = env->set_cachesize(env, 0, 512*1024, 1);                                                              CKERR(r);
281     }
282     r = env->set_redzone(env, 0);                                                                             CKERR(r);
283     int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
284     r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO);                                            CKERR(r);
285     env->set_errfile(env, stderr);
286     r = env->checkpointing_set_period(env, 5);                                                                CKERR(r);
287 
288     DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
289     assert(dbs != NULL);
290 
291     // --------------------------
292     upgrade_test_4(dbs, test_to_do);
293     // --------------------------
294 
295     if (verbose >= 2)
296 	print_engine_status(env);
297     r = env->close(env, 0);                                                                                   CKERR(r);
298     toku_free(dbs);
299 
300 }
301 
302 // ------------ infrastructure ----------
303 static void do_args(int argc, char * const argv[]);
304 
test_main(int argc,char * const * argv)305 int test_main(int argc, char * const *argv) {
306     do_args(argc, argv);
307     littlenode = 0;
308     setup();
309     run_test(insert);
310     setup();
311     run_test(delete);
312     if (SRC_VERSION == 4) {
313 	if (verbose)
314 	    printf("Now repeat test with small nodes and small cache.\n");
315 	littlenode = 1;  // 4k nodes, small cache
316 	setup();
317 	run_test(insert);
318 	setup();
319 	run_test(delete);
320     }
321     return 0;
322 }
323 
do_args(int argc,char * const argv[])324 static void do_args(int argc, char * const argv[]) {
325     int resultcode;
326     char *cmd = argv[0];
327     argc--; argv++;
328 
329     while (argc>0) {
330 	if (strcmp(argv[0], "-v")==0) {
331 	    verbose++;
332 	} else if (strcmp(argv[0],"-q")==0) {
333 	    verbose--;
334 	    if (verbose<0) verbose=0;
335         } else if (strcmp(argv[0], "-h")==0) {
336 	    resultcode=0;
337 	do_usage:
338 	    fprintf(stderr, "Usage: -h -c -d <num_dbs> -r <num_rows> %s\n", cmd);
339 	    exit(resultcode);
340         } else if (strcmp(argv[0], "-d")==0) {
341             argc--; argv++;
342             NUM_DBS = atoi(argv[0]);
343             if ( NUM_DBS > MAX_DBS ) {
344                 fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
345                 resultcode=1;
346                 goto do_usage;
347             }
348         } else if (strcmp(argv[0], "-r")==0) {
349             argc--; argv++;
350             NUM_ROWS = atoi(argv[0]);
351         } else if (strcmp(argv[0], "-c")==0) {
352             CHECK_RESULTS = 1;
353         } else if (strcmp(argv[0], "-V")==0) {
354             argc--; argv++;
355             SRC_VERSION = atoi(argv[0]);
356 	} else {
357 	    fprintf(stderr, "Unknown arg: %s\n", argv[0]);
358 	    resultcode=1;
359 	    goto do_usage;
360 	}
361 	argc--;
362 	argv++;
363     }
364 }
365