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