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/*======
5This file is part of PerconaFT.
6
7
8Copyright (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 <my_global.h>
40#include <ft/log_header.h>
41
42#include "log-internal.h"
43#include "logger/logcursor.h"
44#include "cachetable/checkpoint.h"
45
46static uint64_t footprint = 0; // for debug and accountability
47
48uint64_t
49toku_log_upgrade_get_footprint(void) {
50 return footprint;
51}
52
53// Footprint concept here is that each function increments a different decimal digit.
54// The cumulative total shows the path taken for the upgrade.
55// Each function must have a single return for this to work.
56#define FOOTPRINT(x) function_footprint=(x*footprint_increment)
57#define FOOTPRINTSETUP(increment) uint64_t function_footprint = 0; uint64_t footprint_increment=increment;
58#define FOOTPRINTCAPTURE footprint+=function_footprint;
59
60
61// return 0 if clean shutdown, TOKUDB_UPGRADE_FAILURE if not clean shutdown
62static int
63verify_clean_shutdown_of_log_version_current(const char *log_dir, LSN * last_lsn, TXNID *last_xid) {
64 int rval = TOKUDB_UPGRADE_FAILURE;
65 TOKULOGCURSOR cursor = NULL;
66 int r;
67 FOOTPRINTSETUP(100);
68
69 FOOTPRINT(1);
70
71 r = toku_logcursor_create(&cursor, log_dir);
72 assert(r == 0);
73 struct log_entry *le = NULL;
74 r = toku_logcursor_last(cursor, &le);
75 if (r == 0) {
76 FOOTPRINT(2);
77 if (le->cmd==LT_shutdown) {
78 LSN lsn = le->u.shutdown.lsn;
79 if (last_lsn) {
80 *last_lsn = lsn;
81 }
82 if (last_xid) {
83 *last_xid = le->u.shutdown.last_xid;
84 }
85 rval = 0;
86 }
87 }
88 r = toku_logcursor_destroy(&cursor);
89 assert(r == 0);
90 FOOTPRINTCAPTURE;
91 return rval;
92}
93
94
95// return 0 if clean shutdown, TOKUDB_UPGRADE_FAILURE if not clean shutdown
96static int
97verify_clean_shutdown_of_log_version_old(const char *log_dir, LSN * last_lsn, TXNID *last_xid, uint32_t version) {
98 int rval = TOKUDB_UPGRADE_FAILURE;
99 int r;
100 FOOTPRINTSETUP(10);
101
102 FOOTPRINT(1);
103
104 int n_logfiles;
105 char **logfiles;
106 r = toku_logger_find_logfiles(log_dir, &logfiles, &n_logfiles);
107 if (r!=0) return r;
108
109 char *basename;
110 TOKULOGCURSOR cursor;
111 struct log_entry *entry;
112 // Only look at newest log
113 // basename points to first char after last / in file pathname
114 basename = strrchr(logfiles[n_logfiles-1], '/') + 1;
115 uint32_t version_name;
116 long long index = -1;
117 r = sscanf(basename, "log%lld.tokulog%u", &index, &version_name);
118 assert(r==2); // found index and version
119 invariant(version_name == version);
120 assert(version>=TOKU_LOG_MIN_SUPPORTED_VERSION);
121 assert(version< TOKU_LOG_VERSION); //Must be old
122 // find last LSN
123 r = toku_logcursor_create_for_file(&cursor, log_dir, basename);
124 if (r != 0) {
125 goto cleanup_no_logcursor;
126 }
127 r = toku_logcursor_last(cursor, &entry);
128 if (r != 0) {
129 goto cleanup;
130 }
131 FOOTPRINT(2);
132 //TODO: Remove this special case once FT_LAYOUT_VERSION_19 (and older) are not supported.
133 if (version <= FT_LAYOUT_VERSION_19) {
134 if (entry->cmd==LT_shutdown_up_to_19) {
135 LSN lsn = entry->u.shutdown_up_to_19.lsn;
136 if (last_lsn) {
137 *last_lsn = lsn;
138 }
139 if (last_xid) {
140 // Use lsn as last_xid.
141 *last_xid = lsn.lsn;
142 }
143 rval = 0;
144 }
145 }
146 else if (entry->cmd==LT_shutdown) {
147 LSN lsn = entry->u.shutdown.lsn;
148 if (last_lsn) {
149 *last_lsn = lsn;
150 }
151 if (last_xid) {
152 *last_xid = entry->u.shutdown.last_xid;
153 }
154 rval = 0;
155 }
156cleanup:
157 r = toku_logcursor_destroy(&cursor);
158 assert(r == 0);
159cleanup_no_logcursor:
160 toku_logger_free_logfiles(logfiles, n_logfiles);
161 FOOTPRINTCAPTURE;
162 return rval;
163}
164
165
166static int
167verify_clean_shutdown_of_log_version(const char *log_dir, uint32_t version, LSN *last_lsn, TXNID *last_xid) {
168 // return 0 if clean shutdown, TOKUDB_UPGRADE_FAILURE if not clean shutdown
169 int r = 0;
170 FOOTPRINTSETUP(1000);
171
172 if (version < TOKU_LOG_VERSION) {
173 FOOTPRINT(1);
174 r = verify_clean_shutdown_of_log_version_old(log_dir, last_lsn, last_xid, version);
175 }
176 else {
177 FOOTPRINT(2);
178 assert(version == TOKU_LOG_VERSION);
179 r = verify_clean_shutdown_of_log_version_current(log_dir, last_lsn, last_xid);
180 }
181 FOOTPRINTCAPTURE;
182 return r;
183}
184
185
186// Actually create a log file of the current version, making the environment be of the current version.
187// TODO: can't fail
188static int
189upgrade_log(const char *env_dir, const char *log_dir, LSN last_lsn, TXNID last_xid) { // the real deal
190 int r;
191 FOOTPRINTSETUP(10000);
192
193 LSN initial_lsn = last_lsn;
194 initial_lsn.lsn++;
195 CACHETABLE ct;
196 TOKULOGGER logger;
197
198 FOOTPRINT(1);
199
200 { //Create temporary environment
201 toku_cachetable_create(&ct, 1<<25, initial_lsn, NULL);
202 toku_cachetable_set_env_dir(ct, env_dir);
203 r = toku_logger_create(&logger);
204 assert(r == 0);
205 toku_logger_set_cachetable(logger, ct);
206 r = toku_logger_open_with_last_xid(log_dir, logger, last_xid);
207 assert(r==0);
208 }
209 { //Checkpoint
210 CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
211 r = toku_checkpoint(cp, logger, NULL, NULL, NULL, NULL, UPGRADE_CHECKPOINT); //fsyncs log dir
212 assert(r == 0);
213 }
214 { //Close cachetable and logger
215 toku_logger_shutdown(logger);
216 toku_cachetable_close(&ct);
217 r = toku_logger_close(&logger);
218 assert(r==0);
219 }
220 {
221 r = verify_clean_shutdown_of_log_version(log_dir, TOKU_LOG_VERSION, NULL, NULL);
222 assert(r==0);
223 }
224 FOOTPRINTCAPTURE;
225 return 0;
226}
227
228// If log on disk is old (environment is old) and clean shutdown, then create log of current version,
229// which will make the environment of the current version (and delete the old logs).
230int
231toku_maybe_upgrade_log(const char *env_dir, const char *log_dir, LSN * lsn_of_clean_shutdown, bool * upgrade_in_progress) {
232 int r;
233 int lockfd = -1;
234 FOOTPRINTSETUP(100000);
235
236 footprint = 0;
237 *upgrade_in_progress = false; // set true only if all criteria are met and we're actually doing an upgrade
238
239 FOOTPRINT(1);
240 r = toku_recover_lock(log_dir, &lockfd);
241 if (r != 0) {
242 goto cleanup_no_lock;
243 }
244 FOOTPRINT(2);
245 assert(log_dir);
246 assert(env_dir);
247
248 uint32_t version_of_logs_on_disk;
249 bool found_any_logs;
250 r = toku_get_version_of_logs_on_disk(log_dir, &found_any_logs, &version_of_logs_on_disk);
251 if (r != 0) {
252 goto cleanup;
253 }
254 FOOTPRINT(3);
255 if (!found_any_logs)
256 r = 0; //No logs means no logs to upgrade.
257 else if (version_of_logs_on_disk > TOKU_LOG_VERSION)
258 r = TOKUDB_DICTIONARY_TOO_NEW;
259 else if (version_of_logs_on_disk < TOKU_LOG_MIN_SUPPORTED_VERSION)
260 r = TOKUDB_DICTIONARY_TOO_OLD;
261 else if (version_of_logs_on_disk == TOKU_LOG_VERSION)
262 r = 0; //Logs are up to date
263 else {
264 FOOTPRINT(4);
265 LSN last_lsn = ZERO_LSN;
266 TXNID last_xid = TXNID_NONE;
267 r = verify_clean_shutdown_of_log_version(log_dir, version_of_logs_on_disk, &last_lsn, &last_xid);
268 if (r != 0) {
269 if (version_of_logs_on_disk >= TOKU_LOG_VERSION_25 &&
270 version_of_logs_on_disk <= TOKU_LOG_VERSION_29 &&
271 TOKU_LOG_VERSION_29 == TOKU_LOG_VERSION) {
272 r = 0; // can do recovery on dirty shutdown
273 } else {
274 fprintf(stderr, "Cannot upgrade PerconaFT version %d database.", version_of_logs_on_disk);
275 fprintf(stderr, " Previous improper shutdown detected.\n");
276 }
277 goto cleanup;
278 }
279 FOOTPRINT(5);
280 *lsn_of_clean_shutdown = last_lsn;
281 *upgrade_in_progress = true;
282 r = upgrade_log(env_dir, log_dir, last_lsn, last_xid);
283 }
284cleanup:
285 {
286 //Clean up
287 int rc;
288 rc = toku_recover_unlock(lockfd);
289 if (r==0) r = rc;
290 }
291cleanup_no_lock:
292 FOOTPRINTCAPTURE;
293 return r;
294}
295
296