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