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