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