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 // Verify that a commit of a prepared txn in recovery retains a db created by it.
42 // A checkpoint is taken between the db creation and the txn prepare.
43 
44 // Verify that an abort of a prepared txn in recovery discards the rows that were inserted.
45 // A checkpoint is taken after the rows are inserted and before and the txn prepare.
46 
47 const int test_nrows = 1000000;
48 
create_foo(DB_ENV * env,DB_TXN * txn)49 static void create_foo(DB_ENV *env, DB_TXN *txn) {
50     int r;
51     DB *db = nullptr;
52     r = db_create(&db, env, 0);
53     CKERR(r);
54     r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE,  S_IRWXU+S_IRWXG+S_IRWXO);
55     CKERR(r);
56     r = db->close(db, 0);
57     CKERR(r);
58 }
59 
populate_foo(DB_ENV * env,DB_TXN * txn)60 static void populate_foo(DB_ENV *env, DB_TXN *txn) {
61     int r;
62     DB *db = nullptr;
63     r = db_create(&db, env, 0);
64     CKERR(r);
65     r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, 0);
66     CKERR(r);
67 
68     for (int i = 0; i < test_nrows; i++) {
69         int k = htonl(i);
70         DBT key = { .data = &k, .size = sizeof k }; DBT val = { .data = &i, .size = sizeof i };
71         r = db->put(db, txn, &key, &val, 0);
72         CKERR(r);
73     }
74 
75     r = db->close(db, 0);
76     CKERR(r);
77 }
78 
check_foo(DB_ENV * env,DB_TXN * txn)79 static void check_foo(DB_ENV *env, DB_TXN *txn) {
80     int r;
81     DB *db;
82     r = db_create(&db, env, 0);
83     CKERR(r);
84     r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, 0);
85     CKERR(r);
86 
87     DBC *c = nullptr;
88     r = db->cursor(db, txn, &c, 0);
89     CKERR(r);
90 
91     DBT key = {}; key.flags = DB_DBT_REALLOC;
92     DBT val = {}; val.flags = DB_DBT_REALLOC;
93     int i;
94     for (i = 0; 1; i++) {
95         r = c->c_get(c, &key, &val, DB_NEXT);
96         if (r != 0)
97             break;
98         int k, v;
99         assert(key.size == sizeof k);
100         memcpy(&k, key.data, key.size);
101         assert(k == (int) htonl(i));
102         assert(val.size == sizeof v);
103         memcpy(&v, val.data, val.size);
104         assert(v == i);
105     }
106     assert(i == 0); // no rows found
107     toku_free(key.data);
108     toku_free(val.data);
109 
110     r = c->c_close(c);
111     CKERR(r);
112 
113     r = db->close(db, 0);
114     CKERR(r);
115 }
116 
create_prepared_txn(void)117 static void create_prepared_txn(void) {
118     int r;
119 
120     DB_ENV *env = nullptr;
121     r = db_env_create(&env, 0);
122     CKERR(r);
123     r = env->open(env, TOKU_TEST_FILENAME,
124                   DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
125                   S_IRWXU+S_IRWXG+S_IRWXO);
126     CKERR(r);
127 
128     DB_TXN *txn = nullptr;
129     r = env->txn_begin(env, nullptr, &txn, 0);
130     CKERR(r);
131 
132     create_foo(env, txn);
133 
134     r = txn->commit(txn, 0);
135     CKERR(r);
136 
137     r = env->txn_begin(env, nullptr, &txn, 0);
138     CKERR(r);
139 
140     populate_foo(env, txn);
141 
142     r = env->txn_checkpoint(env, 0, 0, 0);
143     CKERR(r);
144 
145     TOKU_XA_XID xid = { 0x1234, 8, 9 };
146     for (int i = 0; i < 8+9; i++) {
147         xid.data[i] = i;
148     }
149     r = txn->xa_prepare(txn, &xid, 0);
150     CKERR(r);
151 
152     // discard the txn so that we can close the env and run xa recovery later
153     r = txn->discard(txn, 0);
154     CKERR(r);
155 
156     r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
157     CKERR(r);
158 }
159 
run_xa_recovery(void)160 static void run_xa_recovery(void) {
161     int r;
162 
163     DB_ENV *env;
164     r = db_env_create(&env, 0);
165     CKERR(r);
166     r = env->open(env, TOKU_TEST_FILENAME,
167                   DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
168                   S_IRWXU+S_IRWXG+S_IRWXO);
169     CKERR(r);
170 
171     // get prepared xid
172     long count;
173     TOKU_XA_XID xid;
174     r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
175     CKERR(r);
176 
177     // abort it
178     DB_TXN *txn = nullptr;
179     r = env->get_txn_from_xid(env, &xid, &txn);
180     CKERR(r);
181     r = txn->abort(txn);
182     CKERR(r);
183 
184     r = env->txn_begin(env, nullptr, &txn, 0);
185     CKERR(r);
186 
187     check_foo(env, txn);
188 
189     r = txn->commit(txn, 0);
190     CKERR(r);
191 
192     r = env->close(env, 0);
193     CKERR(r);
194 }
195 
test_main(int argc,char * const argv[])196 int test_main (int argc, char *const argv[]) {
197     default_parse_args(argc, argv);
198 
199     // init the env directory
200     toku_os_recursive_delete(TOKU_TEST_FILENAME);
201     int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
202     CKERR(r);
203 
204     // run the test
205     create_prepared_txn();
206     run_xa_recovery();
207 
208     return 0;
209 }
210