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