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 /* Simulate disk full by making pwrite return ENOSPC */
41 /* Strategy, repeatedly run a test, and on the Ith run of the test  make the Ith write fail. */
42 
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <unistd.h>
46 #include <portability/toku_atomic.h>
47 
48 #define DOERR(r) do { if (r!=0) { did_fail=1; fprintf(error_file, "%s:%d error %d (%s)\n", __FILE__, __LINE__, r, db_strerror(r)); }} while (0)
49 
50 static void
do_db_work(void)51 do_db_work(void) {
52     int r;
53     int did_fail=0;
54     {
55 
56         toku_os_recursive_delete(TOKU_TEST_FILENAME);
57 	r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);                          assert(r==0);
58 
59 	FILE *error_file = 0;
60 	if (verbose==0) {
61             char errname[TOKU_PATH_MAX+1];
62 	    error_file = fopen(toku_path_join(errname, 2, TOKU_TEST_FILENAME, "stderr"), "w");                             assert(error_file);
63 	}
64         else error_file = stderr;
65 
66 	DB_ENV *env;
67 	DB_TXN *tid;
68 	DB     *db;
69 	DBT key,data;
70 
71 	r=db_env_create(&env, 0);                                                  assert(r==0);
72 	r = env->set_redzone(env, 0);    CKERR(r);
73 	env->set_errfile(env, error_file ? error_file : stderr);
74 	// Don't set the lg bsize for the small experiment.
75 	r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
76 	r=db_create(&db, env, 0);                                                  CKERR(r);
77 	r=env->txn_begin(env, 0, &tid, 0);                                         assert(r==0);
78 	r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);  DOERR(r);
79 	if (did_fail) {
80 	    r=tid->abort(tid);                                                     CKERR(r);
81 	} else {
82 	    r=tid->commit(tid, 0);                                                 DOERR(r);
83 	}
84 	if (did_fail) goto shutdown1;
85 
86 	r=env->txn_begin(env, 0, &tid, 0);                                         assert(r==0);
87 	r=db->put(db, tid, dbt_init(&key, "a", 2), dbt_init(&data, "b", 2), 0);    DOERR(r);
88 	if (did_fail) {
89 	    r = tid->abort(tid);                                                   CKERR2s(r, 0, ENOSPC);
90 	} else {
91 	    r=tid->commit(tid, 0);                                                 DOERR(r);
92 	}
93 
94     shutdown1:
95 	r=db->close(db, 0);                                                        DOERR(r);
96 	r=env->close(env, 0);                                                      DOERR(r);
97 	if (error_file && error_file!=stderr) fclose(error_file);
98 	if (did_fail) return;
99     }
100     {
101         toku_os_recursive_delete(TOKU_TEST_FILENAME);
102 	r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);                          assert(r==0);
103 
104 	FILE *error_file = 0;
105 	if (verbose==0) {
106             char errname[TOKU_PATH_MAX+1];
107 	    error_file = fopen(toku_path_join(errname, 2, TOKU_TEST_FILENAME, "stderr"), "w");                             assert(error_file);
108 	}
109         else error_file = stderr;
110 
111 	DB_ENV *env;
112 	DB_TXN *tid;
113 	DB     *db;
114 	DBT key,data;
115 
116 	// Repeat with more put operations
117 	r=db_env_create(&env, 0);                                                  assert(r==0);
118 	r = env->set_redzone(env, 0);    CKERR(r);
119 	env->set_errfile(env, error_file ? error_file : stderr);
120 	r=env->set_lg_bsize(env, 4096);                                            assert(r==0);
121 	r=env->set_cachesize(env, 0, 1, 1);                                        assert(r==0);
122 	r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
123 	r=db_create(&db, env, 0);                                                  CKERR(r);
124 	r=db->set_pagesize(db, 4096);
125 	r=env->txn_begin(env, 0, &tid, 0);                                         assert(r==0);
126 	r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); DOERR(r);
127 	if (did_fail) {
128 	    r = tid->abort(tid);                                                   CKERR2s(r, 0, ENOSPC);
129 	} else {
130 	    r=tid->commit(tid, 0);                                                 DOERR(r);
131 	}
132 	if (did_fail) goto shutdown2;
133 
134 	// Put an extra item in
135 	r=env->txn_begin(env, 0, &tid, 0);                                         assert(r==0);
136 	r=db->put(db, tid, dbt_init(&key, "a", 2), dbt_init(&data, "b", 2), 0);    DOERR(r);
137 	if (did_fail) {
138 	    r=tid->abort(tid);                                                     CKERR(r);
139 	} else {
140 	    r=tid->commit(tid, 0);                                                 DOERR(r);
141 	}
142 	if (did_fail) goto shutdown2;
143 
144 	r=env->txn_begin(env, 0, &tid, 0);                                         assert(r==0);
145 	{
146 	    int i;
147 	    for (i=0; i<100; i++) {
148 		int kvsize=50;
149 		int kvsize_i = kvsize / sizeof(int);
150 		int keyi[kvsize_i],vali[kvsize_i];
151 		int j;
152 		keyi[0] = vali[0] = toku_htonl(i);
153 		for (j=1; j<kvsize_i; j++) {
154 		    keyi[j] = random();
155 		    vali[j] = random();
156 		}
157 		r=db->put(db, tid, dbt_init(&key, keyi, sizeof keyi), dbt_init(&data, vali, sizeof vali), 0);
158 		DOERR(r);
159 		if (did_fail) goto break_out_of_loop;
160 	    }
161 	}
162     break_out_of_loop:
163 	//system("ls -l " TOKU_TEST_FILENAME);
164 	if (did_fail) {
165 	    r = tid->abort(tid);                                                   CKERR2s(r, 0, ENOSPC);
166 	} else {
167 	    r=tid->commit(tid, 0);                                                 DOERR(r);
168 	}
169     shutdown2:
170 	r=db->close(db, 0);                                                        DOERR(r);
171 	r=env->close(env, 0);                                                      DOERR(r);
172 	if (error_file && error_file!=stderr) fclose(error_file);
173     }
174 }
175 
176 static volatile int write_count = 0;
177 #define FAIL_NEVER 0x7FFFFFFF
178 static int fail_at = FAIL_NEVER;
179 
180 static ssize_t
pwrite_counting_and_failing(int fd,const void * buf,size_t size,toku_off_t off)181 pwrite_counting_and_failing (int fd, const void *buf, size_t size, toku_off_t off)
182 {
183     int this_count = toku_sync_add_and_fetch(&write_count, 1);
184     if (this_count>fail_at) {
185         if (verbose>1) { printf("Failure imminent at %d:\n", fail_at); fflush(stdout); }
186 	errno = ENOSPC;
187 	return -1;
188     } else {
189 	return pwrite(fd, buf, size, off);
190     }
191 }
192 
193 static ssize_t
write_counting_and_failing(int fd,const void * buf,size_t size)194 write_counting_and_failing (int fd, const void *buf, size_t size)
195 {
196     int this_count = toku_sync_add_and_fetch(&write_count, 1);
197     if (this_count>fail_at) {
198         if (verbose>1) { printf("Failure imminent at %d:\n", fail_at); fflush(stdout); }
199 	errno = ENOSPC;
200 	return -1;
201     } else {
202 	return write(fd, buf, size);
203     }
204 }
205 
206 static void
do_writes_that_fail(void)207 do_writes_that_fail (void) {
208     if (verbose) { printf("About to fail at %d:\n", fail_at); fflush(stdout); }
209     toku_set_assert_on_write_enospc(true);
210     db_env_set_func_pwrite(pwrite_counting_and_failing);
211     db_env_set_func_full_pwrite(pwrite_counting_and_failing);
212     db_env_set_func_write (write_counting_and_failing);
213     db_env_set_func_full_write (write_counting_and_failing);
214     write_count=0;
215     do_db_work();
216     if (fail_at != FAIL_NEVER && write_count <= fail_at) {
217 	abort(); // if we don't encounter the write (because there are not enough), then in fail_at mode, we need to abort so that the test will be happy.
218     }
219     printf("%d", write_count);
220 }
221 
222 static void
diskfull_parse_args(int argc,char * const argv[])223 diskfull_parse_args (int argc, char * const argv[]) {
224     int c;
225     char *argv0 = argv[0];
226     while ((c = getopt(argc, (char * const *)argv, "cC:vq")) != -1) {
227 	switch(c) {
228         case 'C':
229             fail_at =  atoi(optarg);
230 	    break;
231 	case 'v':
232 	    verbose++;
233             break;
234 	case 'q':
235 	    verbose--;
236 	    if (verbose<0) verbose=0;
237             break;
238 	default:
239 do_usage:
240 	    fprintf(stderr, "Usage:\n%s [-v|-q] [-C number]\n", argv0);
241 	    exit(1);
242 	}
243     }
244     if (argc!=optind) {
245         goto do_usage;
246     }
247 }
248 
249 int
test_main(int argc,char * const argv[])250 test_main (int argc, char * const argv[]) {
251     diskfull_parse_args(argc, argv);
252     do_writes_that_fail();
253     return 0;
254 }
255