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 #include <pthread.h>
41 
42 // verify that a commit of a big txn does not block the commits of other txn's
43 // commit writer (0) happens before bigtxn commit (1) happens before checkpoint (2)
44 static int test_state = 0;
45 
checkpoint_thread(void * arg)46 static void *checkpoint_thread(void *arg) {
47     sleep(1);
48     DB_ENV *env = (DB_ENV *) arg;
49     printf("%s start\n", __FUNCTION__);
50     int r = env->txn_checkpoint(env, 0, 0, 0);
51     assert(r == 0);
52     printf("%s done\n", __FUNCTION__);
53     int old_state = toku_sync_fetch_and_add(&test_state, 1);
54     assert(old_state == 2);
55     return arg;
56 }
57 
58 struct writer_arg {
59     DB_ENV *env;
60     DB *db;
61     int k;
62 };
63 
w_thread(void * arg)64 static void *w_thread(void *arg) {
65     sleep(2);
66     struct writer_arg *warg = (struct writer_arg *) arg;
67     DB_ENV *env = warg->env;
68     DB *db = warg->db;
69     int k = warg->k;
70     printf("%s start\n", __FUNCTION__);
71     int r;
72     DB_TXN *txn;
73     r = env->txn_begin(env, NULL, &txn, 0);
74     assert(r == 0);
75     if (1) {
76         DBT key = { .data = &k, .size = sizeof k };
77         DBT val = { .data = &k, .size = sizeof k };
78         r = db->put(db, txn, &key, &val, 0);
79         assert(r == 0);
80     }
81     r = txn->commit(txn, 0);
82     assert(r == 0);
83     printf("%s done\n", __FUNCTION__);
84     int old_state = toku_sync_fetch_and_add(&test_state, 1);
85     assert(old_state == 0);
86     return arg;
87 }
88 
bigtxn_progress(TOKU_TXN_PROGRESS progress,void * extra)89 static void bigtxn_progress(TOKU_TXN_PROGRESS progress, void *extra) {
90     printf("%s %" PRIu64 " %" PRIu64 " %p\n", __FUNCTION__, progress->entries_processed, progress->entries_total, extra);
91     sleep(1);
92 }
93 
test_main(int argc,char * const argv[])94 int test_main (int argc, char *const argv[]) {
95     int r;
96     int N = 25000;
97     for (int i = 1; i < argc; i++) {
98         if (strcmp(argv[i], "--N") == 0 && i+1 < argc) {
99             N = atoi(argv[++i]);
100             continue;
101         }
102     }
103 
104     toku_os_recursive_delete(TOKU_TEST_FILENAME);
105     r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
106     assert(r == 0);
107 
108     DB_ENV *env;
109     r = db_env_create(&env, 0);
110     assert(r == 0);
111 
112     // avoid locktree escalation by picking a big enough lock tree
113     r = env->set_lk_max_memory(env, 128*1024*1024);
114     assert(r == 0);
115 
116     r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
117     assert(r == 0);
118 
119     DB *db = NULL;
120     r = db_create(&db, env, 0);
121     assert(r == 0);
122 
123     r = db->open(db, NULL, "testit", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
124     assert(r == 0);
125 
126     DB_TXN *bigtxn = NULL;
127     r = env->txn_begin(env, NULL, &bigtxn, 0);
128     assert(r == 0);
129 
130     // use a big key so that the rollback log spills
131     char k[1024]; memset(k, 0, sizeof k);
132     char v[8]; memset(v, 0, sizeof v);
133 
134     for (int i = 0; i < N; i++) {
135         memcpy(k, &i, sizeof i);
136         memcpy(v, &i, sizeof i);
137         DBT key = { .data = k, .size = sizeof k };
138         DBT val = { .data = v, .size = sizeof v };
139         r = db->put(db, bigtxn, &key, &val, 0);
140         assert(r == 0);
141         if ((i % 10000) == 0)
142             printf("put %d\n", i);
143     }
144 
145     pthread_t checkpoint_tid = 0;
146     r = pthread_create(&checkpoint_tid, NULL, checkpoint_thread, env);
147     assert(r == 0);
148 
149     pthread_t w_tid = 0;
150     struct writer_arg w_arg = { env, db, N };
151     r = pthread_create(&w_tid, NULL, w_thread, &w_arg);
152     assert(r == 0);
153 
154     r = bigtxn->commit_with_progress(bigtxn, 0, bigtxn_progress, NULL);
155     assert(r == 0);
156     int old_state = toku_sync_fetch_and_add(&test_state, 1);
157     assert(old_state == 1);
158 
159     void *ret;
160     r = pthread_join(w_tid, &ret);
161     assert(r == 0);
162     r = pthread_join(checkpoint_tid, &ret);
163     assert(r == 0);
164 
165     r = db->close(db, 0);
166     assert(r == 0);
167 
168     r = env->close(env, 0);
169     assert(r == 0);
170 
171     return 0;
172 }
173