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 <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 | |
46 | static uint64_t = 0; // for debug and accountability |
47 | |
48 | uint64_t |
49 | (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 (x) function_footprint=(x*footprint_increment) |
57 | #define (increment) uint64_t = 0; uint64_t =increment; |
58 | #define footprint+=function_footprint; |
59 | |
60 | |
61 | // return 0 if clean shutdown, TOKUDB_UPGRADE_FAILURE if not clean shutdown |
62 | static int |
63 | verify_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 |
96 | static int |
97 | verify_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 | } |
156 | cleanup: |
157 | r = toku_logcursor_destroy(&cursor); |
158 | assert(r == 0); |
159 | cleanup_no_logcursor: |
160 | toku_logger_free_logfiles(logfiles, n_logfiles); |
161 | FOOTPRINTCAPTURE; |
162 | return rval; |
163 | } |
164 | |
165 | |
166 | static int |
167 | verify_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 |
188 | static int |
189 | upgrade_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). |
230 | int |
231 | toku_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 | } |
284 | cleanup: |
285 | { |
286 | //Clean up |
287 | int rc; |
288 | rc = toku_recover_unlock(lockfd); |
289 | if (r==0) r = rc; |
290 | } |
291 | cleanup_no_lock: |
292 | FOOTPRINTCAPTURE; |
293 | return r; |
294 | } |
295 | |
296 | |