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
41 /* Like test_log6 except abort.
42 * And abort some stuff, but not others (unlike test_log6_abort which aborts everything) */
43
44
45 #include <db.h>
46 #include <stdlib.h>
47 #include <search.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <memory.h>
51
52
53 #ifndef DB_DELETE_ANY
54 #define DB_DELETE_ANY 0
55 #endif
56
57 // TOKU_TEST_FILENAME is defined in the Makefile
58
59 // How many iterations are we going to do insertions and deletions. This is a bound to the number of distinct keys in the DB.
60 #define N 1000
61
62 static int n_keys_mentioned=0;
63 static int random_keys_mentioned[N];
64
65 static DB *pending_i, *pending_d, *committed;
66
67 // Keep track of what's in the committed database separately
68 struct pair {int x,y;};
69
70 static void
insert_in_mem(int x,int y,int * count,struct pair * pairs)71 insert_in_mem (int x, int y, int *count, struct pair *pairs) {
72 assert(*count<N);
73 pairs[(*count)++]=(struct pair){x,y};
74 }
75 static void
delete_in_mem(int x,int * count,struct pair * pairs)76 delete_in_mem (int x, int *count, struct pair *pairs) {
77 int i;
78 for (i=0; i<*count; i++) {
79 if (pairs[i].x==x) {
80 pairs[i]=pairs[--(*count)];
81 return;
82 }
83 }
84 }
85
86 static int com_count=0, pend_count=0, peni_count=0;
87 static struct pair com_data[N], pend_data[N], peni_data[N];
88
89 static void
insert_pending(int key,int val,DB_TXN * bookx)90 insert_pending (int key, int val, DB_TXN *bookx) {
91 DBT keyd,datad;
92 //printf("IP %u,%u\n", key,val);
93
94 insert_in_mem(key, val, &peni_count, peni_data);
95 pending_i->put(pending_i, bookx,
96 dbt_init(&keyd, &key, sizeof(key)),
97 dbt_init(&datad, &val, sizeof(val)),
98 0);
99
100 delete_in_mem(key, &pend_count, pend_data);
101 pending_d->del(pending_d, bookx,
102 dbt_init(&keyd, &key, sizeof(key)),
103 0);
104 }
105
put_a_random_item(DB * db,DB_TXN * tid,int i,DB_TXN * bookx)106 static void put_a_random_item (DB *db, DB_TXN *tid, int i, DB_TXN *bookx) {
107 char hello[30], there[30];
108 DBT key,data;
109 int randv = myrandom();
110 random_keys_mentioned[n_keys_mentioned++] = randv;
111 insert_pending(randv, i, bookx);
112 //printf("Insert %u\n", randv);
113 snprintf(hello, sizeof(hello), "hello%d.%d", randv, i);
114 snprintf(there, sizeof(hello), "there%d", i);
115 memset(&key, 0, sizeof(key));
116 memset(&data, 0, sizeof(data));
117 key.data = hello; key.size=strlen(hello)+1;
118 data.data = there; data.size=strlen(there)+1;
119 int r=db->put(db, tid, &key, &data, 0);
120 if (r!=0) printf("%s:%d i=%d r=%d (%s)\n", __FILE__, __LINE__, i, r, strerror(r));
121 assert(r==0);
122 }
123
delete_a_random_item(DB * db,DB_TXN * tid,DB_TXN * bookx)124 static void delete_a_random_item (DB *db, DB_TXN *tid, DB_TXN *bookx) {
125 if (n_keys_mentioned==0) return;
126 int ridx = myrandom()%n_keys_mentioned;
127 int randv = random_keys_mentioned[ridx];
128 DBT keyd;
129 DBT vald;
130 //printf("Delete %u\n", randv);
131 dbt_init(&keyd, &randv, sizeof(randv));
132 dbt_init(&vald, &randv, sizeof(randv));
133
134 pending_i->del(pending_i, bookx, &keyd, 0);
135 delete_in_mem(randv, &peni_count, peni_data);
136
137 pending_d->put(pending_d, bookx, &keyd, &vald, 0);
138 insert_in_mem(randv, randv, &pend_count, pend_data);
139
140 db->del(db, tid, &keyd, DB_DELETE_ANY);
141 }
142
commit_items(DB_ENV * env,int UU (i))143 static void commit_items (DB_ENV *env, int UU(i)) {
144 //printf("commit_items %d\n", i);
145 DB_TXN *txn;
146 int r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
147 DBC *cursor;
148 r = pending_i->cursor(pending_i, txn, &cursor, 0); assert(r==0);
149 DBT k,v;
150 memset(&k,0,sizeof(k));
151 memset(&v,0,sizeof(v));
152 //printf("%d items in peni\n", peni_count);
153 while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
154 assert(k.size==4);
155 assert(v.size==4);
156 int ki=*(int*)k.data;
157 int vi=*(int*)v.data;
158 //printf(" put %u %u\n", ki, vi);
159 r=committed->put(committed, txn, dbt_init(&k, &ki, sizeof(ki)), dbt_init(&v, &vi, sizeof(vi)), 0);
160 insert_in_mem(ki, vi, &com_count, com_data);
161 assert(r==0);
162 r=pending_i->del(pending_i, txn, &k, 0);
163 assert(r==0);
164 }
165 r=cursor->c_close(cursor);
166 assert(r==0);
167
168 r = pending_d->cursor(pending_d, txn, &cursor, 0); assert(r==0);
169 memset(&k,0,sizeof(k));
170 memset(&v,0,sizeof(v));
171 while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
172 assert(k.size==4);
173 assert(v.size==4);
174 int ki=*(int*)k.data;
175 int vi=*(int*)v.data;
176 assert(ki==vi);
177 //printf(" del %u\n", ki);
178 committed->del(committed, txn, dbt_init(&k, &ki, sizeof(ki)), 0);
179 delete_in_mem(ki, &com_count, com_data);
180 // ignore result from that del
181 r=pending_d->del(pending_d, txn, &k, 0);
182 assert(r==0);
183 }
184 r=cursor->c_close(cursor);
185 assert(r==0);
186 r=txn->commit(txn, 0); assert(r==0);
187 }
188
abort_items(DB_ENV * env)189 static void abort_items (DB_ENV *env) {
190 DB_TXN *txn;
191 int r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
192 //printf("abort_items\n");
193 DBC *cursor;
194 r = pending_i->cursor(pending_i, txn, &cursor, 0); assert(r==0);
195 DBT k,v;
196 memset(&k,0,sizeof(k));
197 memset(&v,0,sizeof(v));
198 while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
199 assert(k.size==4);
200 assert(v.size==4);
201 int ki=*(int*)k.data;
202 //printf("Deleting %u\n", ki);
203 r=pending_i->del(pending_i, txn, dbt_init(&k, &ki, sizeof(ki)), 0);
204 assert(r==0);
205 }
206 r=cursor->c_close(cursor);
207 assert(r==0);
208
209 r = pending_d->cursor(pending_d, txn, &cursor, 0); assert(r==0);
210 memset(&k,0,sizeof(k));
211 memset(&v,0,sizeof(v));
212 while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
213 assert(k.size==4);
214 assert(v.size==4);
215 int ki=*(int*)k.data;
216 r=pending_d->del(pending_d, txn, dbt_init(&k, &ki, sizeof(ki)), 0);
217 assert(r==0);
218 }
219 r=cursor->c_close(cursor);
220 assert(r==0);
221 r=txn->commit(txn, 0); assert(r==0);
222 }
223
224 static int
compare_pairs(const void * a,const void * b)225 compare_pairs (const void *a, const void *b) {
226 return memcmp(a,b,4);
227 }
228
verify_items(DB_ENV * env,DB * db)229 static void verify_items (DB_ENV *env, DB *db) {
230 DB_TXN *txn;
231 int r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
232 DBC *cursor;
233 DBT k,v;
234 memset(&k,0,sizeof(k));
235 memset(&v,0,sizeof(v));
236
237 #if 0
238 r=db->cursor(db, txn, &cursor, 0);
239 assert(r==0);
240 while (cursor->c_get(cursor, &k, &v, DB_NEXT)==0) {
241 }
242 r=cursor->c_close(cursor);
243 assert(r==0);
244 #endif
245
246 r = committed->cursor(committed, txn, &cursor, 0);
247 assert(r==0);
248 qsort(com_data, com_count, sizeof(com_data[0]), compare_pairs);
249 int curscount=0;
250 //printf(" count=%d\n", com_count);
251 while (cursor->c_get(cursor, &k, &v, DB_NEXT)==0) {
252 int kv=*(int*)k.data;
253 int dv=*(int*)v.data;
254 //printf(" sorted com_data[%d]=%d, cursor got %d\n", curscount, com_data[curscount].x, kv);
255 assert(com_data[curscount].x==kv);
256 DBT k2,v2;
257 memset(&k2, 0, sizeof(k2));
258 memset(&v2, 0, sizeof(v2));
259 char hello[30], there[30];
260 snprintf(hello, sizeof(hello), "hello%d.%d", kv, dv);
261 snprintf(there, sizeof(hello), "there%d", dv);
262 k2.data = hello; k2.size=strlen(hello)+1;
263 //printf("committed: %u,%u\n", kv, dv);
264 r=db->get(db, txn, &k2, &v2, 0);
265 assert(r==0);
266 assert(strcmp((char*)v2.data, there)==0);
267 curscount++;
268 }
269 assert(curscount==com_count);
270 r=cursor->c_close(cursor);
271 assert(r==0);
272
273 r=txn->commit(txn, 0); assert(r==0);
274 }
275
make_db(void)276 static void make_db (void) {
277 DB_ENV *env;
278 DB *db;
279 DB_TXN *tid, *bookx;
280 int r;
281 int i;
282
283 toku_os_recursive_delete(TOKU_TEST_FILENAME);
284 r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
285 r=db_env_create(&env, 0); assert(r==0);
286 env->set_errfile(env, stderr);
287 r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
288 r=db_create(&db, env, 0); CKERR(r);
289 r=db_create(&pending_i, env, 0); CKERR(r);
290 r=db_create(&pending_d, env, 0); CKERR(r);
291 r=db_create(&committed, env, 0); CKERR(r);
292 r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
293 r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
294 r=pending_i->open(pending_i, tid, "pending_i.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
295 r=pending_d->open(pending_d, tid, "pending_d.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
296 r=committed->open(committed, tid, "committed.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
297 r=tid->commit(tid, 0); assert(r==0);
298 r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
299 r=env->txn_begin(env, 0, &bookx, 0); assert(r==0);
300
301 for (i=0; i<N; i++) {
302 int randv = myrandom();
303 //if (i%10000==0) printf(".");
304 if (randv%100==0) {
305 r=tid->abort(tid); assert(r==0);
306 r=bookx->commit(bookx, 0); assert(r==0);
307 r=env->txn_begin(env, 0, &bookx, 0); assert(r==0);
308 abort_items(env);
309 r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
310 } else if (randv%1000==1) {
311 r=tid->commit(tid, 0); assert(r==0);
312 r=bookx->commit(bookx, 0); assert(r==0);
313 r=env->txn_begin(env, 0, &bookx, 0); assert(r==0);
314 commit_items(env, i);
315 r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
316 } else if (randv%3==0) {
317 delete_a_random_item(db, tid, bookx);
318 } else {
319 put_a_random_item(db, tid, i, bookx);
320 }
321 }
322 r=tid->commit(tid, 0); assert(r==0);
323 r=bookx->commit(bookx, 0); assert(r==0);
324 commit_items(env, i);
325 verify_items(env, db);
326
327 r=pending_i->close(pending_i, 0); assert(r==0);
328 r=pending_d->close(pending_d, 0); assert(r==0);
329 r=committed->close(committed, 0); assert(r==0);
330 r=db->close(db, 0); assert(r==0);
331 r=env->close(env, 0); assert(r==0);
332 }
333
334 int
test_main(int argc,char * const argv[])335 test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
336 make_db();
337 return 0;
338 }
339