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 #include "toku_os.h"
42 #include "cachetable/checkpoint.h"
43 
44 #define ENVDIR TOKU_TEST_FILENAME
45 #include "test-ft-txns.h"
46 
do_txn(TOKULOGGER logger,bool readonly)47 static void do_txn(TOKULOGGER logger, bool readonly) {
48     int r;
49     TOKUTXN txn;
50     r = toku_txn_begin_txn((DB_TXN*)NULL, (TOKUTXN)0, &txn, logger, TXN_SNAPSHOT_NONE, false);
51     CKERR(r);
52 
53     if (!readonly) {
54         toku_maybe_log_begin_txn_for_write_operation(txn);
55     }
56     r = toku_txn_commit_txn(txn, false, NULL, NULL);
57     CKERR(r);
58 
59     toku_txn_close_txn(txn);
60 }
61 
test_xid_lsn_independent(int N)62 static void test_xid_lsn_independent(int N) {
63     TOKULOGGER logger;
64     CACHETABLE ct;
65     int r;
66 
67     test_setup(TOKU_TEST_FILENAME, &logger, &ct);
68 
69     FT_HANDLE ft;
70 
71     TOKUTXN txn;
72     r = toku_txn_begin_txn((DB_TXN*)NULL, (TOKUTXN)0, &txn, logger, TXN_SNAPSHOT_NONE, false);
73     CKERR(r);
74 
75     r = toku_open_ft_handle("ftfile", 1, &ft, 1024, 256, TOKU_DEFAULT_COMPRESSION_METHOD, ct, txn, toku_builtin_compare_fun);
76     CKERR(r);
77 
78     r = toku_txn_commit_txn(txn, false, NULL, NULL);
79     CKERR(r);
80     toku_txn_close_txn(txn);
81 
82     r = toku_txn_begin_txn((DB_TXN*)NULL, (TOKUTXN)0, &txn, logger, TXN_SNAPSHOT_NONE, false);
83     CKERR(r);
84     TXNID xid_first = txn->txnid.parent_id64;
85     unsigned int rands[N];
86     for (int i=0; i<N; i++) {
87         char key[100],val[300];
88         DBT k, v;
89         rands[i] = random();
90         snprintf(key, sizeof(key), "key%x.%x", rands[i], i);
91         memset(val, 'v', sizeof(val));
92         val[sizeof(val)-1]=0;
93         toku_ft_insert(ft, toku_fill_dbt(&k, key, 1+strlen(key)), toku_fill_dbt(&v, val, 1+strlen(val)), txn);
94     }
95     {
96         TOKUTXN txn2;
97         r = toku_txn_begin_txn((DB_TXN*)NULL, (TOKUTXN)0, &txn2, logger, TXN_SNAPSHOT_NONE, false);
98     CKERR(r);
99         // Verify the txnid has gone up only by one (even though many log entries were done)
100         invariant(txn2->txnid.parent_id64 == xid_first + 1);
101         r = toku_txn_commit_txn(txn2, false, NULL, NULL);
102     CKERR(r);
103         toku_txn_close_txn(txn2);
104     }
105     r = toku_txn_commit_txn(txn, false, NULL, NULL);
106     CKERR(r);
107     toku_txn_close_txn(txn);
108     {
109         //TODO(yoni) #5067 will break this portion of the test. (End ids are also assigned, so it would increase by 4 instead of 2.)
110         // Verify the txnid has gone up only by two (even though many log entries were done)
111         TOKUTXN txn3;
112         r = toku_txn_begin_txn((DB_TXN*)NULL, (TOKUTXN)0, &txn3, logger, TXN_SNAPSHOT_NONE, false);
113     CKERR(r);
114         invariant(txn3->txnid.parent_id64 == xid_first + 2);
115         r = toku_txn_commit_txn(txn3, false, NULL, NULL);
116     CKERR(r);
117         toku_txn_close_txn(txn3);
118     }
119     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
120     r = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
121     CKERR(r);
122     r = toku_close_ft_handle_nolsn(ft, NULL);
123     CKERR(r);
124 
125     clean_shutdown(&logger, &ct);
126 }
127 
128 static TXNID
logger_get_last_xid(TOKULOGGER logger)129 logger_get_last_xid(TOKULOGGER logger) {
130     TXN_MANAGER mgr = toku_logger_get_txn_manager(logger);
131     return toku_txn_manager_get_last_xid(mgr);
132 }
133 
test_xid_lsn_independent_crash_recovery(int N)134 static void test_xid_lsn_independent_crash_recovery(int N) {
135     TOKULOGGER logger;
136     CACHETABLE ct;
137     int r;
138 
139     test_setup(TOKU_TEST_FILENAME, &logger, &ct);
140 
141     for (int i=0; i < N - 1; i++) {
142         do_txn(logger, true);
143     }
144     do_txn(logger, false);
145 
146     TXNID last_xid_before = logger_get_last_xid(logger);
147 
148     toku_logger_close_rollback(logger);
149 
150     toku_cachetable_close(&ct);
151     // "Crash"
152     r = toku_logger_close(&logger);
153     CKERR(r);
154     ct = NULL;
155     logger = NULL;
156 
157     // "Recover"
158     test_setup_and_recover(TOKU_TEST_FILENAME, &logger, &ct);
159 
160     TXNID last_xid_after = logger_get_last_xid(logger);
161 
162     invariant(last_xid_after == last_xid_before);
163 
164     shutdown_after_recovery(&logger, &ct);
165 }
166 
test_xid_lsn_independent_shutdown_recovery(int N)167 static void test_xid_lsn_independent_shutdown_recovery(int N) {
168     TOKULOGGER logger;
169     CACHETABLE ct;
170     test_setup(TOKU_TEST_FILENAME, &logger, &ct);
171 
172     for (int i=0; i < N - 1; i++) {
173         do_txn(logger, true);
174     }
175     do_txn(logger, false);
176 
177     TXNID last_xid_before = logger_get_last_xid(logger);
178 
179     clean_shutdown(&logger, &ct);
180 
181     // Did a clean shutdown.
182 
183     // "Recover"
184     test_setup_and_recover(TOKU_TEST_FILENAME, &logger, &ct);
185 
186     TXNID last_xid_after = logger_get_last_xid(logger);
187 
188     invariant(last_xid_after == last_xid_before);
189 
190     shutdown_after_recovery(&logger, &ct);
191 }
192 
test_xid_lsn_independent_parents(int N)193 static void test_xid_lsn_independent_parents(int N) {
194     TOKULOGGER logger;
195     CACHETABLE ct;
196     int r;
197 
198     // Lets txns[-1] be NULL
199     TOKUTXN txns_hack[N+1];
200     TOKUTXN *txns=&txns_hack[1];
201 
202     int num_non_cascade = N;
203     do {
204         test_setup(TOKU_TEST_FILENAME, &logger, &ct);
205         ZERO_ARRAY(txns_hack);
206 
207         for (int i = 0; i < N; i++) {
208             r = toku_txn_begin_txn((DB_TXN*)NULL, txns[i-1], &txns[i], logger, TXN_SNAPSHOT_NONE, false);
209             CKERR(r);
210 
211             if (i < num_non_cascade) {
212                 toku_maybe_log_begin_txn_for_write_operation(txns[i]);
213                 invariant(txns[i]->begin_was_logged);
214             }
215             else {
216                 invariant(!txns[i]->begin_was_logged);
217             }
218         }
219         for (int i = 0; i < N; i++) {
220             if (i < num_non_cascade) {
221                 toku_maybe_log_begin_txn_for_write_operation(txns[i]);
222                 invariant(txns[i]->begin_was_logged);
223             }
224             else {
225                 invariant(!txns[i]->begin_was_logged);
226             }
227         }
228         toku_maybe_log_begin_txn_for_write_operation(txns[N-1]);
229         for (int i = 0; i < N; i++) {
230             invariant(txns[i]->begin_was_logged);
231         }
232         for (int i = N-1; i >= 0; i--) {
233             r = toku_txn_commit_txn(txns[i], false, NULL, NULL);
234             CKERR(r);
235 
236             toku_txn_close_txn(txns[i]);
237         }
238         clean_shutdown(&logger, &ct);
239 
240         num_non_cascade /= 2;
241     } while (num_non_cascade > 0);
242 }
243 
test_main(int argc,const char * argv[])244 int test_main (int argc, const char *argv[]) {
245     default_parse_args(argc, argv);
246     for (int i=1; i<=128; i *= 2) {
247         test_xid_lsn_independent(i);
248         test_xid_lsn_independent_crash_recovery(i);
249         test_xid_lsn_independent_shutdown_recovery(i);
250         test_xid_lsn_independent_parents(i);
251     }
252     return 0;
253 }
254