| 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 | |