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 // The purpose of this test is to test the error recovery of the extractor.  We inject errors into the extractor and
40 // verify that the extractor error state is set.
41 
42 #define DONT_DEPRECATE_MALLOC
43 #define DONT_DEPRECATE_WRITES
44 #include "test.h"
45 #include "loader/loader.h"
46 #include "loader/loader-internal.h"
47 #include "ftloader-error-injector.h"
48 #include "memory.h"
49 #include <portability/toku_path.h>
50 
51 
generate(DB * dest_db,DB * src_db,DBT_ARRAY * dest_keys,DBT_ARRAY * dest_vals,const DBT * src_key,const DBT * src_val)52 static int generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
53     (void) dest_db; (void) src_db; (void) src_key; (void) src_val;
54     toku_dbt_array_resize(dest_keys, 1);
55     toku_dbt_array_resize(dest_vals, 1);
56 
57     copy_dbt(&dest_keys->dbts[0], src_key);
58     copy_dbt(&dest_vals->dbts[0], src_val);
59 
60     return 0;
61 }
62 
qsort_compare_ints(const void * a,const void * b)63 static int qsort_compare_ints (const void *a, const void *b) {
64     int avalue = *(int*)a;
65     int bvalue = *(int*)b;
66     if (avalue<bvalue) return -1;
67     if (avalue>bvalue) return +1;
68     return 0;
69 }
70 
compare_int(DB * desc,const DBT * akey,const DBT * bkey)71 static int compare_int(DB *desc, const DBT *akey, const DBT *bkey) {
72     assert(desc == NULL);
73     assert(akey->size == sizeof (int));
74     assert(bkey->size == sizeof (int));
75     return qsort_compare_ints(akey->data, bkey->data);
76 }
77 
populate_rowset(struct rowset * rowset,int seq,int nrows,int keys[])78 static void populate_rowset(struct rowset *rowset, int seq, int nrows, int keys[]) {
79     for (int i = 0; i < nrows; i++) {
80         int k = keys[i];
81         int v = seq * nrows + i;
82         DBT key;
83         toku_fill_dbt(&key, &k, sizeof k);
84         DBT val;
85         toku_fill_dbt(&val, &v, sizeof v);
86         add_row(rowset, &key, &val);
87     }
88 }
89 
shuffle(int a[],int n)90 static void shuffle(int a[], int n) {
91     for (int i = 0; i < n; i++) {
92         int r = random() % n;
93         int t = a[i]; a[i] = a[r]; a[r] = t;
94     }
95 }
96 
97 static int ascending_keys = 0;
98 static int descending_keys = 0;
99 static int random_keys = 0;
100 
test_extractor(int nrows,int nrowsets,bool expect_fail,const char * testdir)101 static void test_extractor(int nrows, int nrowsets, bool expect_fail, const char *testdir) {
102     if (verbose) printf("%s %d %d %s\n", __FUNCTION__, nrows, nrowsets, testdir);
103 
104     int r;
105 
106     int nkeys = nrows * nrowsets;
107     int *XMALLOC_N(nkeys, keys);
108     for (int i = 0; i < nkeys; i++)
109         keys[i] = ascending_keys ? i : nkeys - i;
110     if (random_keys)
111         shuffle(keys, nkeys);
112 
113     // open the ft_loader. this runs the extractor.
114     const int N = 1;
115     FT_HANDLE fts[N];
116     DB* dbs[N];
117     const char *fnames[N];
118     ft_compare_func compares[N];
119     for (int i = 0; i < N; i++) {
120         fts[i] = NULL;
121         dbs[i] = NULL;
122         fnames[i] = "";
123         compares[i] = compare_int;
124     }
125 
126     char temp[strlen(testdir) + 1 + strlen("tempXXXXXX") + 1];
127     sprintf(temp, "%s/%s", testdir, "tempXXXXXX");
128 
129     FTLOADER loader;
130     r = toku_ft_loader_open(&loader, NULL, generate, NULL, N, fts, dbs, fnames, compares, "tempXXXXXX", ZERO_LSN, nullptr, true, 0, false, true);
131     assert(r == 0);
132 
133     struct rowset *rowset[nrowsets];
134     for (int i = 0 ; i < nrowsets; i++) {
135         rowset[i] = (struct rowset *) toku_malloc(sizeof (struct rowset));
136         assert(rowset[i]);
137         init_rowset(rowset[i], toku_ft_loader_get_rowset_budget_for_testing());
138         populate_rowset(rowset[i], i, nrows, &keys[i*nrows]);
139     }
140 
141     // setup error injection
142     toku_set_func_malloc(my_malloc);
143     toku_set_func_realloc(my_realloc);
144     toku_set_func_fwrite(bad_fwrite);
145     toku_set_func_write(bad_write);
146     toku_set_func_pwrite(bad_pwrite);
147     ft_loader_set_poll_function(&loader->poll_callback, loader_poll_callback, NULL);
148 
149     // feed rowsets to the extractor
150     for (int i = 0; i < nrowsets; i++) {
151         r = toku_queue_enq(loader->primary_rowset_queue, rowset[i], 1, NULL);
152         assert(r == 0);
153     }
154 
155     r = toku_ft_loader_finish_extractor(loader);
156     assert(r == 0);
157 
158     toku_set_func_malloc(NULL);
159     toku_set_func_realloc(NULL);
160     toku_set_func_fwrite(nullptr);
161     toku_set_func_write(NULL);
162     toku_set_func_pwrite(NULL);
163 
164     int error;
165     r = toku_ft_loader_get_error(loader, &error);
166     assert(r == 0);
167     assert(expect_fail ? error != 0 : error == 0);
168 
169     // verify the temp files
170 
171     // abort the ft_loader.  this ends the test
172     r = toku_ft_loader_abort(loader, true);
173     assert(r == 0);
174 
175     toku_free(keys);
176 }
177 static int nrows = 1;
178 static int nrowsets = 2;
179 
usage(const char * progname)180 static int usage(const char *progname) {
181     fprintf(stderr, "Usage: %s [options] directory\n", progname);
182     fprintf(stderr, "[-v] turn on verbose\n");
183     fprintf(stderr, "[-q] turn off verbose\n");
184     fprintf(stderr, "[-r %d] set the number of rows\n", nrows);
185     fprintf(stderr, "[--rowsets %d] set the number of rowsets\n", nrowsets);
186     fprintf(stderr, "[-s] set the small loader size factor\n");
187     fprintf(stderr, "[-m] inject big malloc and realloc errors\n");
188     fprintf(stderr, "[--malloc_limit %u] set the threshold for failing malloc and realloc\n", (unsigned) my_big_malloc_limit);
189     fprintf(stderr, "[-w] inject write errors\n");
190     fprintf(stderr, "[-u] inject user errors\n");
191     return 1;
192 }
193 
test_main(int argc,const char * argv[])194 int test_main (int argc, const char *argv[]) {
195     const char *progname=argv[0];
196     int max_error_limit = -1;
197     argc--; argv++;
198     while (argc>0) {
199         if (strcmp(argv[0],"-h")==0) {
200             return usage(progname);
201         } else if (strcmp(argv[0],"-v")==0) {
202 	    verbose=1;
203 	} else if (strcmp(argv[0],"-q")==0) {
204 	    verbose=0;
205         } else if (strcmp(argv[0],"-r") == 0 && argc >= 1) {
206             argc--; argv++;
207             nrows = atoi(argv[0]);
208         } else if (strcmp(argv[0],"--rowsets") == 0 && argc >= 1) {
209             argc--; argv++;
210             nrowsets = atoi(argv[0]);
211         } else if (strcmp(argv[0],"-s") == 0) {
212             toku_ft_loader_set_size_factor(1);
213         } else if (strcmp(argv[0],"-w") == 0) {
214             do_write_errors = 1;
215         } else if (strcmp(argv[0],"-m") == 0) {
216             do_malloc_errors = 1;
217         } else if (strcmp(argv[0],"-u") == 0) {
218             do_user_errors = 1;
219         } else if (strcmp(argv[0],"--malloc_limit") == 0 && argc > 1) {
220             argc--; argv++;
221             my_big_malloc_limit = atoi(argv[0]);
222         } else if (strcmp(argv[0],"--max_error_limit") == 0 && argc >= 1) {
223             argc--; argv++;
224             max_error_limit = atoi(argv[0]);
225         } else if (strcmp(argv[0],"--asc") == 0) {
226             ascending_keys = 1;
227         } else if (strcmp(argv[0],"--dsc") == 0) {
228             descending_keys = 1;
229         } else if (strcmp(argv[0],"--random") == 0) {
230             random_keys = 1;
231 	} else if (argc!=1) {
232             return usage(progname);
233 	    exit(1);
234 	}
235         else {
236             break;
237         }
238 	argc--; argv++;
239     }
240 
241     const char *testdir = TOKU_TEST_FILENAME;
242 
243     if (ascending_keys + descending_keys + random_keys == 0)
244         ascending_keys = 1;
245 
246     // callibrate
247     test_extractor(nrows, nrowsets, false, testdir);
248 
249     // run tests
250     int error_limit = event_count;
251     if (verbose) printf("error_limit=%d\n", error_limit);
252 
253     if (max_error_limit != -1 && error_limit > max_error_limit)
254         error_limit = max_error_limit;
255     for (int i = 1; i <= error_limit; i++) {
256         reset_event_counts();
257         reset_my_malloc_counts();
258         event_count_trigger = i;
259         test_extractor(nrows, nrowsets, true, testdir);
260     }
261 
262     return 0;
263 }
264