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