KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > recovery > RecoveryManager


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: RecoveryManager.java,v 1.211 2006/12/04 18:47:41 cwl Exp $
7  */

8
9 package com.sleepycat.je.recovery;
10
11 import java.io.IOException JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.Set JavaDoc;
19 import java.util.logging.Level JavaDoc;
20 import java.util.logging.Logger JavaDoc;
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 JavaDoc TRACE_DUP_ROOT_REPLACE =
60         "DupRootRecover:";
61     private static final String JavaDoc TRACE_LN_REDO = "LNRedo:";
62     private static final String JavaDoc TRACE_LN_UNDO = "LNUndo";
63     private static final String JavaDoc TRACE_IN_REPLACE = "INRecover:";
64     private static final String JavaDoc TRACE_ROOT_REPLACE = "RootRecover:";
65     private static final String JavaDoc TRACE_IN_DEL_REPLAY = "INDelReplay:";
66     private static final String JavaDoc TRACE_IN_DUPDEL_REPLAY = "INDupDelReplay:";
67     private static final String JavaDoc 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; // stat info
74
private Set JavaDoc committedTxnIds; // committed txns
75
private Set JavaDoc abortedTxnIds; // aborted txns
76
private Map JavaDoc preparedTxns; // txnid -> prepared Txn
77
private Set JavaDoc inListRebuildDbIds; // dbs for which we have to rebuild the
78
// in memory IN list.
79

80     private Level JavaDoc detailedTraceLevel; // level value for detailed trace msgs
81
private Map JavaDoc fileSummaryLsns; // file number -> LSN of FileSummaryLN
82
private int inListClearCounter; // governs intermediate IN list clearing
83

84     /**
85      * Make a recovery manager
86      */

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 JavaDoc();
95         abortedTxnIds = new HashSet JavaDoc();
96     preparedTxns = new HashMap JavaDoc();
97         inListRebuildDbIds = new HashSet JavaDoc();
98         fileSummaryLsns = new HashMap JavaDoc();
99
100         /*
101          * Figure out the level to use for detailed trace messages, by choosing
102          * the more verbose of the recovery manager's trace setting vs the
103          * general trace setting.
104          */

105         detailedTraceLevel =
106             Tracer.parseLevel(env,
107                               EnvironmentParams.JE_LOGGING_LEVEL_RECOVERY);
108     }
109     
110     /**
111      * Look for an existing log and use it to create an in memory structure for
112      * accessing existing databases. The file manager and logging system are
113      * only available after recovery.
114      * @return RecoveryInfo statistics about the recovery process.
115      */

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                 /*
130                  * Establish the location of the end of the log. After this, we
131                  * can write to the log. No Tracer calls are allowed until
132                  * after this point is established in the log.
133                  */

134                 findEndOfLog(readOnly);
135                 Tracer.trace(Level.CONFIG, env,
136                              "Recovery underway, found end of log");
137         
138                 /*
139                  * Establish the location of the root, the last checkpoint, and
140                  * the first active LSN by finding the last checkpoint.
141                  */

142                 findLastCheckpoint();
143         env.getLogManager().setLastLsnAtRecovery
144             (fileManager.getLastUsedLsn());
145                 Tracer.trace(Level.CONFIG, env,
146                              "Recovery checkpoint search, " +
147                              info);
148
149                 /* Read in the root. */
150                 env.readMapTreeFromLog(info.useRootLsn);
151
152                 /* Rebuild the in memory tree from the log. */
153                 buildTree();
154             } else {
155
156                 /*
157                  * Nothing more to be done. Enable publishing of debug log
158                  * messages to the database log.
159                  */

160                 env.enableDebugLoggingToDbLog();
161                 Tracer.trace(Level.CONFIG, env, "Recovery w/no files.");
162                 env.logMapTreeRoot();
163
164         /*
165          * Always force a checkpoint during creation.
166          */

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         /*
176          * We don't need this set any more since these are all
177          * registered with the TxnManager now.
178          */

179         preparedTxns = null;
180         }
181
182             /*
183              * Open the UP database and populate the cache before the first
184              * checkpoint so that the checkpoint may flush file summary
185              * information. May be disabled for unittests.
186              */

187             if (DbInternal.getCreateUP
188                 (env.getConfigManager().getEnvironmentConfig())) {
189                 env.getUtilizationProfile().populateCache();
190             }
191
192             /*
193              * At this point, we've recovered (or there were no log files at
194              * all. Write a checkpoint into the log.
195              *
196              * NOTE: The discussion of deltas below may be obsolete now that
197              * we use dirty bits to determine what to include in a delta.
198              * However, we still want to disallow deltas to flush full versions
199              * after a crash.
200              *
201              * Don't allow deltas, because the delta-determining scheme that
202              * compares child entries to the last full LSN doesn't work in
203              * recovery land. New child entries may have an earlier LSN than
204              * the owning BIN's last full, because of the act of splicing in
205              * LNs during recovery.
206              *
207              * For example, suppose that during LN redo, bin 10 was split into
208              * bin 10 and bin 12. That splitting causes a full log. Then later
209              * on, the redo splices LN x, which is from before the last full of
210              * bin 10, into bin 10. If we checkpoint allowing deltas after
211              * recovery finishes, we won't pick up the LNx diff, because that
212              * LN is an earlier LSN than the split-induced full log entry of
213              * bin 10.
214              */

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, // flushAll
225
"recovery");
226             } else {
227                 /* Initialze intervals when there is no initial checkpoint. */
228                 env.getCheckpointer().initIntervals
229                     (info.checkpointEndLsn, System.currentTimeMillis());
230             }
231
232         } catch (IOException JavaDoc 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     /**
245      * Find the end of the log, initialize the FileManager. While we're
246      * perusing the log, return the last checkpoint LSN if we happen to see it.
247      */

248     private void findEndOfLog(boolean readOnly)
249         throws IOException JavaDoc, DatabaseException {
250
251         LastFileReader reader = new LastFileReader(env, readBufferSize);
252
253         /*
254          * Tell the reader to iterate through the log file until we hit the end
255          * of the log or an invalid entry.
256          * Remember the last seen CkptEnd, and the first CkptStart with no
257          * following CkptEnd.
258          */

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         /*
272          * The last valid lsn should point to the start of the last valid log entry,
273          * while the end of the log should point to the first byte of blank space,
274          * so these two should not be the same.
275          */

276         assert (reader.getLastValidLsn() != reader.getEndOfLog()):
277             "lastUsed=" + DbLsn.getNoFormatString(reader.getLastValidLsn()) +
278             " end=" + DbLsn.getNoFormatString(reader.getEndOfLog());
279
280
281         /* Now truncate if necessary. */
282         if (!readOnly) {
283             reader.setEndOfFile();
284         }
285
286         /* Tell the fileManager where the end of the log is. */
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         /*
295          * Now the logging system is initialized and can do more
296          * logging. Enable publishing of debug log messages to the database
297          * log.
298          */

299         env.enableDebugLoggingToDbLog();
300     }
301
302     /**
303      * Find the last checkpoint and establish the firstActiveLsn point,
304      * checkpoint start, and checkpoint end.
305      */

306     private void findLastCheckpoint()
307         throws IOException JavaDoc, DatabaseException {
308
309         /*
310          * The checkpointLsn might have been already found when establishing
311          * the end of the log. If it was found, then partialCheckpointStartLsn
312          * was also found. If it was not found, search backwards for it now
313          * and also set partialCheckpointStartLsn.
314          */

315         if (info.checkpointEndLsn == DbLsn.NULL_LSN) {
316             
317             /*
318              * Search backwards though the log for a checkpoint end entry and a
319              * root entry.
320              */

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                 /*
329                  * Continue iterating until we find a checkpoint end entry.
330                  * While we're at it, remember the last root seen in case we
331                  * don't find a checkpoint end entry.
332                  */

333                 if (searcher.isCheckpointEnd()) {
334
335                     /*
336                      * We're done, the checkpoint end will tell us where the
337                      * root is.
338                      */

339                     info.checkpointEndLsn = searcher.getLastLsn();
340                     break;
341                 } else if (searcher.isCheckpointStart()) {
342
343                     /*
344                      * Remember the first CkptStart following the CkptEnd.
345                      */

346                     info.partialCheckpointStartLsn = searcher.getLastLsn();
347
348                 } else if (searcher.isRoot()) {
349
350                     /*
351                      * Save the last root that was found in the log in case we
352                      * don't see a checkpoint.
353                      */

354                     if (info.useRootLsn == DbLsn.NULL_LSN) {
355                         info.useRootLsn = searcher.getLastLsn();
356                     }
357                 }
358             }
359             info.nRepeatIteratorReads += searcher.getNRepeatIteratorReads();
360         }
361
362         /*
363          * If we haven't found a checkpoint, we'll have to recover without
364          * one. At a minimium, we must have found a root.
365          */

366         if (info.checkpointEndLsn == DbLsn.NULL_LSN) {
367             info.checkpointStartLsn = DbLsn.NULL_LSN;
368             info.firstActiveLsn = DbLsn.NULL_LSN;
369         } else {
370             /* Read in the checkpoint entry. */
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             /* Init the checkpointer's id sequence and FirstActiveLsn.*/
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     /**
399      * Use the log to recreate an in memory tree.
400      */

401     private void buildTree()
402         throws IOException JavaDoc, DatabaseException {
403
404         inListClearCounter = 0;
405
406         /*
407          * Read all map database INs, find largest node id before any
408          * possiblity of splits, find largest txn Id before any need for a root
409          * update (which would use an AutoTxn)
410          */

411         int passNum = buildINs(1,
412                                true, /* mapping tree */
413                                false); /* dup tree */
414
415
416         /*
417          * Undo all aborted map LNs. Read and remember all committed
418          * transaction ids.
419          */

420         Tracer.trace(Level.CONFIG, env, passStartHeader(passNum) +
421                      "undo map LNs");
422     long start = System.currentTimeMillis();
423         Set JavaDoc mapLNSet = new HashSet JavaDoc();
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         /*
435          * Replay all mapLNs, mapping tree in place now. Use the set of
436          * committed txns found from the undo pass.
437          */

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         /*
449          * Reconstruct the internal nodes for the main level trees.
450          */

451         passNum = buildINs(passNum,
452                            false, /* mapping tree*/
453                            false); /* dup tree */
454
455         /*
456          * Reconstruct the internal nodes for the duplicate level trees.
457          */

458         passNum = buildINs(passNum,
459                            false, /* mapping tree*/
460                            true); /* dup tree */
461
462         /*
463          * Rebuild the in memory IN list. Once the tree is complete we can
464          * invoke the evictor. The evictor will also be invoked during the
465          * undo and redo passes.
466          */

467         rebuildINList();
468         env.invokeEvictor();
469
470         /*
471          * Undo aborted LNs. No need to collect committed txn ids again, was
472          * done in the undo of all aborted MapLNs.
473          */

474         Tracer.trace(Level.CONFIG, env, passStartHeader(9) + "undo LNs");
475     start = System.currentTimeMillis();
476         Set JavaDoc lnSet = new HashSet JavaDoc();
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         /* Replay LNs. Also read non-transactional LNs. */
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     /*
502      * Up to three passes for the INs of a given level
503      * @param mappingTree if true, we're rebuilding the mapping tree
504      * @param dupTree if true, we're rebuilding the dup tree.
505      */

506     private int buildINs(int passNum,
507                          boolean mappingTree,
508                          boolean dupTree)
509         throws IOException JavaDoc, DatabaseException {
510
511         Set JavaDoc targetEntries = new HashSet JavaDoc();
512         Set JavaDoc deltaType = new HashSet JavaDoc();
513         String JavaDoc passADesc = null;
514         String JavaDoc passBDesc = null;
515         String JavaDoc 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             /* Duplicate trees read these entries. */
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             /* Main tree and mapping tree read these types of entries. */
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         /*
546          * Pass a: Read all INs and place into the proper location.
547          */

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, // mapping tree only
556
targetEntries,
557                                      /*
558                                       * requireExactMatch -- why is it true
559                                       * for dups, false for main tree?
560                                       * Keeping logic the same for now.
561                                       */

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         /*
576          * Pass b: Redo INs if the LevelRecorder detects a split/compression
577          * was done after ckpt [#14424]
578          */

579         Set JavaDoc 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         /*
594          * Pass c: Read BIN Deltas.
595          * BINDeltas must be processed after all INs so the delta is properly
596          * applied to the last version. For example, suppose BINDeltas were not
597          * done in a later pass, the tree is INa->BINb, and the log has
598          * INa
599          * BINDelta for BINb
600          * INa
601          * the splicing in of the second INa would override the BINDelta.
602          */

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, // requireExactMatch
609
null); // LevelRecorder
610
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     /*
619      * Read every internal node and IN DeleteInfo in the mapping tree and place
620      * in the in-memory tree.
621      */

622     private void readINsAndTrackIds(long rollForwardLsn,
623                                     LevelRecorder recorder)
624         throws IOException JavaDoc, DatabaseException {
625
626         INFileReader reader =
627             new INFileReader(env,
628                              readBufferSize,
629                              rollForwardLsn, // start lsn
630
info.nextAvailableLsn, // end lsn
631
true, // track node and db ids
632
false, // map db only
633
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         /* Validate all entries in at least one full recovery pass. */
640         reader.setAlwaysValidateChecksum(true);
641
642         try {
643             info.numMapINs = 0;
644             DbTree dbMapTree = env.getDbMapTree();
645
646             /*
647          * Process every IN, INDeleteInfo, and INDupDeleteInfo in the
648          * mapping tree.
649          */

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             /*
660              * Update node id and database sequences. Use either the maximum of
661              * the ids seen by the reader vs the ids stored in the checkpoint.
662              */

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 JavaDoc e) {
684             traceAndThrowException(reader.getLastLsn(), "readMapIns", e);
685         }
686     }
687
688     /**
689      * Read INs and process.
690      */

691     private int readINs(long rollForwardLsn,
692                         boolean mapDbOnly,
693                         Set JavaDoc targetLogEntryTypes,
694                         boolean requireExactMatch,
695                         LevelRecorder recorder)
696         throws IOException JavaDoc, DatabaseException {
697
698         // don't need to track NodeIds
699
INFileReader reader =
700         new INFileReader(env,
701                              readBufferSize,
702                              rollForwardLsn, // startlsn
703
info.nextAvailableLsn, // finish
704
false,
705                  mapDbOnly,
706                              info.partialCheckpointStartLsn,
707                              fileSummaryLsns);
708
709         Iterator JavaDoc iter = targetLogEntryTypes.iterator();
710         while (iter.hasNext()) {
711             reader.addTargetType((LogEntryType) iter.next());
712         }
713
714         int numINsSeen = 0;
715         try {
716
717             /*
718              * Read all non-provisional INs, and process if they don't belong
719              * to the mapping tree.
720              */

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                         // This db has been deleted, ignore the entry.
736
} else {
737                         replayOneIN(reader, db, requireExactMatch, recorder);
738                         numINsSeen++;
739
740                         /*
741                          * Add any db that we encounter IN's for because
742                          * they'll be part of the in-memory tree and therefore
743                          * should be included in the INList rebuild.
744                          */

745                         inListRebuildDbIds.add(dbId);
746                     }
747                 }
748             }
749
750             info.nRepeatIteratorReads += reader.getNRepeatIteratorReads();
751             return numINsSeen;
752         } catch (Exception JavaDoc e) {
753             traceAndThrowException(reader.getLastLsn(), "readNonMapIns", e);
754             return 0;
755         }
756     }
757
758     /**
759      * Read INs and process.
760      */

761     private void repeatReadINs(long rollForwardLsn,
762                                Set JavaDoc targetLogEntryTypes,
763