| 1 8 9 package com.sleepycat.je.recovery; 10 11 import java.io.IOException ; 12 import java.util.ArrayList ; 13 import java.util.HashMap ; 14 import java.util.HashSet ; 15 import java.util.Iterator ; 16 import java.util.List ; 17 import java.util.Map ; 18 import java.util.Set ; 19 import java.util.logging.Level ; 20 import java.util.logging.Logger ; 21 22 import com.sleepycat.je.CheckpointConfig; 23 import com.sleepycat.je.DatabaseException; 24 import com.sleepycat.je.DbInternal; 25 import com.sleepycat.je.TransactionConfig; 26 import com.sleepycat.je.cleaner.UtilizationTracker; 27 import com.sleepycat.je.config.EnvironmentParams; 28 import com.sleepycat.je.dbi.DatabaseId; 29 import com.sleepycat.je.dbi.DatabaseImpl; 30 import com.sleepycat.je.dbi.DbConfigManager; 31 import com.sleepycat.je.dbi.DbTree; 32 import com.sleepycat.je.dbi.EnvironmentImpl; 33 import com.sleepycat.je.latch.LatchSupport; 34 import com.sleepycat.je.log.CheckpointFileReader; 35 import com.sleepycat.je.log.FileManager; 36 import com.sleepycat.je.log.INFileReader; 37 import com.sleepycat.je.log.LNFileReader; 38 import com.sleepycat.je.log.LastFileReader; 39 import com.sleepycat.je.log.LogEntryType; 40 import com.sleepycat.je.log.LogFileNotFoundException; 41 import com.sleepycat.je.tree.BIN; 42 import com.sleepycat.je.tree.ChildReference; 43 import com.sleepycat.je.tree.DIN; 44 import com.sleepycat.je.tree.IN; 45 import com.sleepycat.je.tree.Key; 46 import com.sleepycat.je.tree.LN; 47 import com.sleepycat.je.tree.Node; 48 import com.sleepycat.je.tree.SearchResult; 49 import com.sleepycat.je.tree.TrackingInfo; 50 import com.sleepycat.je.tree.Tree; 51 import com.sleepycat.je.tree.TreeLocation; 52 import com.sleepycat.je.tree.WithRootLatched; 53 import com.sleepycat.je.txn.LockType; 54 import com.sleepycat.je.txn.Txn; 55 import com.sleepycat.je.utilint.DbLsn; 56 import com.sleepycat.je.utilint.Tracer; 57 58 public class RecoveryManager { 59 private static final String TRACE_DUP_ROOT_REPLACE = 60 "DupRootRecover:"; 61 private static final String TRACE_LN_REDO = "LNRedo:"; 62 private static final String TRACE_LN_UNDO = "LNUndo"; 63 private static final String TRACE_IN_REPLACE = "INRecover:"; 64 private static final String TRACE_ROOT_REPLACE = "RootRecover:"; 65 private static final String TRACE_IN_DEL_REPLAY = "INDelReplay:"; 66 private static final String TRACE_IN_DUPDEL_REPLAY = "INDupDelReplay:"; 67 private static final String TRACE_ROOT_DELETE = "RootDelete:"; 68 69 private static final int CLEAR_INCREMENT = 50; 70 71 private EnvironmentImpl env; 72 private int readBufferSize; 73 private RecoveryInfo info; private Set committedTxnIds; private Set abortedTxnIds; private Map preparedTxns; private Set inListRebuildDbIds; 80 private Level detailedTraceLevel; private Map fileSummaryLsns; private int inListClearCounter; 84 87 public RecoveryManager(EnvironmentImpl env) 88 throws DatabaseException { 89 90 this.env = env; 91 DbConfigManager cm = env.getConfigManager(); 92 readBufferSize = 93 cm.getInt(EnvironmentParams.LOG_ITERATOR_READ_SIZE); 94 committedTxnIds = new HashSet (); 95 abortedTxnIds = new HashSet (); 96 preparedTxns = new HashMap (); 97 inListRebuildDbIds = new HashSet (); 98 fileSummaryLsns = new HashMap (); 99 100 105 detailedTraceLevel = 106 Tracer.parseLevel(env, 107 EnvironmentParams.JE_LOGGING_LEVEL_RECOVERY); 108 } 109 110 116 public RecoveryInfo recover(boolean readOnly) 117 throws DatabaseException { 118 119 info = new RecoveryInfo(); 120 121 try { 122 FileManager fileManager = env.getFileManager(); 123 DbConfigManager configManager = env.getConfigManager(); 124 boolean forceCheckpoint = 125 configManager.getBoolean 126 (EnvironmentParams.ENV_RECOVERY_FORCE_CHECKPOINT); 127 if (fileManager.filesExist()) { 128 129 134 findEndOfLog(readOnly); 135 Tracer.trace(Level.CONFIG, env, 136 "Recovery underway, found end of log"); 137 138 142 findLastCheckpoint(); 143 env.getLogManager().setLastLsnAtRecovery 144 (fileManager.getLastUsedLsn()); 145 Tracer.trace(Level.CONFIG, env, 146 "Recovery checkpoint search, " + 147 info); 148 149 150 env.readMapTreeFromLog(info.useRootLsn); 151 152 153 buildTree(); 154 } else { 155 156 160 env.enableDebugLoggingToDbLog(); 161 Tracer.trace(Level.CONFIG, env, "Recovery w/no files."); 162 env.logMapTreeRoot(); 163 164 167 forceCheckpoint = true; 168 } 169 170 if (preparedTxns.size() > 0) { 171 Tracer.trace(Level.INFO, env, 172 "There are " + preparedTxns.size() + 173 " prepared but unfinished txns."); 174 175 179 preparedTxns = null; 180 } 181 182 187 if (DbInternal.getCreateUP 188 (env.getConfigManager().getEnvironmentConfig())) { 189 env.getUtilizationProfile().populateCache(); 190 } 191 192 215 if (!readOnly && 216 (env.getLogManager().getLastLsnAtRecovery() != 217 info.checkpointEndLsn || 218 forceCheckpoint)) { 219 CheckpointConfig config = new CheckpointConfig(); 220 config.setForce(true); 221 config.setMinimizeRecoveryTime(true); 222 env.invokeCheckpoint 223 (config, 224 false, "recovery"); 226 } else { 227 228 env.getCheckpointer().initIntervals 229 (info.checkpointEndLsn, System.currentTimeMillis()); 230 } 231 232 } catch (IOException e) { 233 Tracer.trace(env, "RecoveryManager", "recover", 234 "Couldn't recover", e); 235 throw new RecoveryException(env, "Couldn't recover: " + 236 e.getMessage(), e); 237 } finally { 238 Tracer.trace(Level.CONFIG, env, "Recovery finished: " + info); 239 } 240 241 return info; 242 } 243 244 248 private void findEndOfLog(boolean readOnly) 249 throws IOException , DatabaseException { 250 251 LastFileReader reader = new LastFileReader(env, readBufferSize); 252 253 259 while (reader.readNextEntry()) { 260 LogEntryType type = reader.getEntryType(); 261 if (LogEntryType.LOG_CKPT_END.equals(type)) { 262 info.checkpointEndLsn = reader.getLastLsn(); 263 info.partialCheckpointStartLsn = DbLsn.NULL_LSN; 264 } else if (LogEntryType.LOG_CKPT_START.equals(type)) { 265 if (info.partialCheckpointStartLsn == DbLsn.NULL_LSN) { 266 info.partialCheckpointStartLsn = reader.getLastLsn(); 267 } 268 } 269 } 270 271 276 assert (reader.getLastValidLsn() != reader.getEndOfLog()): 277 "lastUsed=" + DbLsn.getNoFormatString(reader.getLastValidLsn()) + 278 " end=" + DbLsn.getNoFormatString(reader.getEndOfLog()); 279 280 281 282 if (!readOnly) { 283 reader.setEndOfFile(); 284 } 285 286 287 info.lastUsedLsn = reader.getLastValidLsn(); 288 info.nextAvailableLsn = reader.getEndOfLog(); 289 info.nRepeatIteratorReads += reader.getNRepeatIteratorReads(); 290 env.getFileManager().setLastPosition(info.nextAvailableLsn, 291 info.lastUsedLsn, 292 reader.getPrevOffset()); 293 294 299 env.enableDebugLoggingToDbLog(); 300 } 301 302 306 private void findLastCheckpoint() 307 throws IOException , DatabaseException { 308 309 315 if (info.checkpointEndLsn == DbLsn.NULL_LSN) { 316 317 321 CheckpointFileReader searcher = 322 new CheckpointFileReader(env, readBufferSize, false, 323 info.lastUsedLsn, DbLsn.NULL_LSN, 324 info.nextAvailableLsn); 325 326 while (searcher.readNextEntry()) { 327 328 333 if (searcher.isCheckpointEnd()) { 334 335 339 info.checkpointEndLsn = searcher.getLastLsn(); 340 break; 341 } else if (searcher.isCheckpointStart()) { 342 343 346 info.partialCheckpointStartLsn = searcher.getLastLsn(); 347 348 } else if (searcher.isRoot()) { 349 350 354 if (info.useRootLsn == DbLsn.NULL_LSN) { 355 info.useRootLsn = searcher.getLastLsn(); 356 } 357 } 358 } 359 info.nRepeatIteratorReads += searcher.getNRepeatIteratorReads(); 360 } 361 362 366 if (info.checkpointEndLsn == DbLsn.NULL_LSN) { 367 info.checkpointStartLsn = DbLsn.NULL_LSN; 368 info.firstActiveLsn = DbLsn.NULL_LSN; 369 } else { 370 371 CheckpointEnd checkpointEnd = 372 (CheckpointEnd) (env.getLogManager().get 373 (info.checkpointEndLsn)); 374 info.checkpointEnd = checkpointEnd; 375 info.checkpointStartLsn = checkpointEnd.getCheckpointStartLsn(); 376 info.firstActiveLsn = checkpointEnd.getFirstActiveLsn(); 377 if (checkpointEnd.getRootLsn() != DbLsn.NULL_LSN) { 378 info.useRootLsn = checkpointEnd.getRootLsn(); 379 } 380 381 382 env.getCheckpointer().setCheckpointId(checkpointEnd.getId()); 383 env.getCheckpointer().setFirstActiveLsn 384 (checkpointEnd.getFirstActiveLsn()); 385 } 386 if (info.useRootLsn == DbLsn.NULL_LSN) { 387 throw new NoRootException 388 (env, 389 "This environment's log file has no root. Since the root " + 390 "is the first entry written into a log at environment " + 391 "creation, this should only happen if the initial creation " + 392 "of the environment was never checkpointed or synced. " + 393 "Please move aside the existing log files to allow the " + 394 "creation of a new environment"); 395 } 396 } 397 398 401 private void buildTree() 402 throws IOException , DatabaseException { 403 404 inListClearCounter = 0; 405 406 411 int passNum = buildINs(1, 412 true, 413 false); 414 415 416 420 Tracer.trace(Level.CONFIG, env, passStartHeader(passNum) + 421 "undo map LNs"); 422 long start = System.currentTimeMillis(); 423 Set mapLNSet = new HashSet (); 424 mapLNSet.add(LogEntryType.LOG_MAPLN_TRANSACTIONAL); 425 mapLNSet.add(LogEntryType.LOG_TXN_COMMIT); 426 mapLNSet.add(LogEntryType.LOG_TXN_ABORT); 427 mapLNSet.add(LogEntryType.LOG_TXN_PREPARE); 428 undoLNs(info, mapLNSet); 429 long end = System.currentTimeMillis(); 430 Tracer.trace(Level.CONFIG, env, passEndHeader(passNum, start, end) + 431 info.toString()); 432 passNum++; 433 434 438 Tracer.trace(Level.CONFIG, env, passStartHeader(passNum) + 439 "redo map LNs"); 440 start = System.currentTimeMillis(); 441 mapLNSet.add(LogEntryType.LOG_MAPLN); 442 redoLNs(info, mapLNSet); 443 end = System.currentTimeMillis(); 444 Tracer.trace(Level.CONFIG, env, passEndHeader(passNum, start, end) + 445 info.toString()); 446 passNum++; 447 448 451 passNum = buildINs(passNum, 452 false, 453 false); 454 455 458 passNum = buildINs(passNum, 459 false, 460 true); 461 462 467 rebuildINList(); 468 env.invokeEvictor(); 469 470 474 Tracer.trace(Level.CONFIG, env, passStartHeader(9) + "undo LNs"); 475 start = System.currentTimeMillis(); 476 Set lnSet = new HashSet (); 477 lnSet.add(LogEntryType.LOG_LN_TRANSACTIONAL); 478 lnSet.add(LogEntryType.LOG_NAMELN_TRANSACTIONAL); 479 lnSet.add(LogEntryType.LOG_DEL_DUPLN_TRANSACTIONAL); 480 lnSet.add(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL); 481 482 undoLNs(info, lnSet); 483 end = System.currentTimeMillis(); 484 Tracer.trace(Level.CONFIG, env, passEndHeader(9, start, end) + 485 info.toString()); 486 487 488 Tracer.trace(Level.CONFIG, env, passStartHeader(10) + "redo LNs"); 489 start = System.currentTimeMillis(); 490 lnSet.add(LogEntryType.LOG_LN); 491 lnSet.add(LogEntryType.LOG_NAMELN); 492 lnSet.add(LogEntryType.LOG_DEL_DUPLN); 493 lnSet.add(LogEntryType.LOG_DUPCOUNTLN); 494 lnSet.add(LogEntryType.LOG_FILESUMMARYLN); 495 redoLNs(info, lnSet); 496 end = System.currentTimeMillis(); 497 Tracer.trace(Level.CONFIG, env, passEndHeader(10, start, end) + 498 info.toString()); 499 } 500 501 506 private int buildINs(int passNum, 507 boolean mappingTree, 508 boolean dupTree) 509 throws IOException , DatabaseException { 510 511 Set targetEntries = new HashSet (); 512 Set deltaType = new HashSet (); 513 String passADesc = null; 514 String passBDesc = null; 515 String passCDesc = null; 516 517 if (mappingTree) { 518 passADesc = "read mapping INs"; 519 passBDesc = "redo mapping INs"; 520 passCDesc = "read mapping BINDeltas"; 521 } else if (dupTree) { 522 passADesc = "read dup INs"; 523 passBDesc = "redo dup INs"; 524 passCDesc = "read dup BINDeltas"; 525 } else { 526 passADesc = "read main INs"; 527 passBDesc = "redo main INs"; 528 passCDesc = "read main BINDeltas"; 529 } 530 531 if (dupTree) { 532 533 targetEntries.add(LogEntryType.LOG_DIN); 534 targetEntries.add(LogEntryType.LOG_DBIN); 535 targetEntries.add(LogEntryType.LOG_IN_DUPDELETE_INFO); 536 deltaType.add(LogEntryType.LOG_DUP_BIN_DELTA); 537 } else { 538 539 targetEntries.add(LogEntryType.LOG_IN); 540 targetEntries.add(LogEntryType.LOG_BIN); 541 targetEntries.add(LogEntryType.LOG_IN_DELETE_INFO); 542 deltaType.add(LogEntryType.LOG_BIN_DELTA); 543 } 544 545 548 Tracer.trace(Level.CONFIG, env, passStartHeader(passNum) + passADesc); 549 LevelRecorder recorder = new LevelRecorder(); 550 long start = System.currentTimeMillis(); 551 if (mappingTree) { 552 readINsAndTrackIds(info.checkpointStartLsn, recorder); 553 } else { 554 int numINsSeen = readINs(info.checkpointStartLsn, 555 false, targetEntries, 557 562 (dupTree? true: false), 563 recorder); 564 if (dupTree) { 565 info.numDuplicateINs += numINsSeen; 566 } else { 567 info.numOtherINs += numINsSeen; 568 } 569 } 570 long end = System.currentTimeMillis(); 571 Tracer.trace(Level.CONFIG, env, passEndHeader(passNum, start, end) + 572 info.toString()); 573 passNum++; 574 575 579 Set redoSet = recorder.getDbsWithDifferentLevels(); 580 if (redoSet.size() > 0) { 581 Tracer.trace(Level.CONFIG, env, 582 passStartHeader(passNum) + passBDesc); 583 start = System.currentTimeMillis(); 584 repeatReadINs(info.checkpointStartLsn, 585 targetEntries, 586 redoSet); 587 end = System.currentTimeMillis(); 588 Tracer.trace(Level.CONFIG, env, 589 passEndHeader(passNum, start, end) + info.toString()); 590 passNum++; 591 } 592 593 603 Tracer.trace(Level.CONFIG, env, passStartHeader(passNum) + passCDesc); 604 start = System.currentTimeMillis(); 605 info.numBinDeltas += readINs(info.checkpointStartLsn, 606 mappingTree, 607 deltaType, 608 true, null); end = System.currentTimeMillis(); 611 Tracer.trace(Level.CONFIG, env, 612 passEndHeader(passNum, start, end) + info.toString()); 613 passNum++; 614 615 return passNum; 616 } 617 618 622 private void readINsAndTrackIds(long rollForwardLsn, 623 LevelRecorder recorder) 624 throws IOException , DatabaseException { 625 626 INFileReader reader = 627 new INFileReader(env, 628 readBufferSize, 629 rollForwardLsn, info.nextAvailableLsn, true, false, info.partialCheckpointStartLsn, 634 fileSummaryLsns); 635 reader.addTargetType(LogEntryType.LOG_IN); 636 reader.addTargetType(LogEntryType.LOG_BIN); 637 reader.addTargetType(LogEntryType.LOG_IN_DELETE_INFO); 638 639 640 reader.setAlwaysValidateChecksum(true); 641 642 try { 643 info.numMapINs = 0; 644 DbTree dbMapTree = env.getDbMapTree(); 645 646 650 while (reader.readNextEntry()) { 651 DatabaseId dbId = reader.getDatabaseId(); 652 if (dbId.equals(DbTree.ID_DB_ID)) { 653 DatabaseImpl db = dbMapTree.getDb(dbId); 654 replayOneIN(reader, db, false, recorder); 655 info.numMapINs++; 656 } 657 } 658 659 663 info.useMaxNodeId = reader.getMaxNodeId(); 664 info.useMaxDbId = reader.getMaxDbId(); 665 info.useMaxTxnId = reader.getMaxTxnId(); 666 if (info.checkpointEnd != null) { 667 if (info.useMaxNodeId < info.checkpointEnd.getLastNodeId()) { 668 info.useMaxNodeId = info.checkpointEnd.getLastNodeId(); 669 } 670 if (info.useMaxDbId < info.checkpointEnd.getLastDbId()) { 671 info.useMaxDbId = info.checkpointEnd.getLastDbId(); 672 } 673 if (info.useMaxTxnId < info.checkpointEnd.getLastTxnId()) { 674 info.useMaxTxnId = info.checkpointEnd.getLastTxnId(); 675 } 676 } 677 678 Node.setLastNodeId(info.useMaxNodeId); 679 env.getDbMapTree().setLastDbId(info.useMaxDbId); 680 env.getTxnManager().setLastTxnId(info.useMaxTxnId); 681 682 info.nRepeatIteratorReads += reader.getNRepeatIteratorReads(); 683 } catch (Exception e) { 684 traceAndThrowException(reader.getLastLsn(), "readMapIns", e); 685 } 686 } 687 688 691 private int readINs(long rollForwardLsn, 692 boolean mapDbOnly, 693 Set targetLogEntryTypes, 694 boolean requireExactMatch, 695 LevelRecorder recorder) 696 throws IOException , DatabaseException { 697 698 INFileReader reader = 700 new INFileReader(env, 701 readBufferSize, 702 rollForwardLsn, info.nextAvailableLsn, false, 705 mapDbOnly, 706 info.partialCheckpointStartLsn, 707 fileSummaryLsns); 708 709 Iterator iter = targetLogEntryTypes.iterator(); 710 while (iter.hasNext()) { 711 reader.addTargetType((LogEntryType) iter.next()); 712 } 713 714 int numINsSeen = 0; 715 try { 716 717 721 DbTree dbMapTree = env.getDbMapTree(); 722 while (reader.readNextEntry()) { 723 DatabaseId dbId = reader.getDatabaseId(); 724 boolean isMapDb = dbId.equals(DbTree.ID_DB_ID); 725 boolean isTarget = false; 726 727 if (mapDbOnly && isMapDb) { 728 isTarget = true; 729 } else if (!mapDbOnly && !isMapDb) { 730 isTarget = true; 731 } 732 if (isTarget) { 733 DatabaseImpl db = dbMapTree.getDb(dbId); 734 if (db == null) { 735 } else { 737 replayOneIN(reader, db, requireExactMatch, recorder); 738 numINsSeen++; 739 740 745 inListRebuildDbIds.add(dbId); 746 } 747 } 748 } 749 750 info.nRepeatIteratorReads += reader.getNRepeatIteratorReads(); 751 return numINsSeen; 752 } catch (Exception e) { 753 traceAndThrowException(reader.getLastLsn(), "readNonMapIns", e); 754 return 0; 755 } 756 } 757 758 761 private void repeatReadINs(long rollForwardLsn, 762 Set targetLogEntryTypes, 763  
|