KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > log > INFileReader


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: INFileReader.java,v 1.51 2006/11/17 23:47:23 mark Exp $
7  */

8
9 package com.sleepycat.je.log;
10
11 import java.io.IOException JavaDoc;
12 import java.nio.ByteBuffer JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.Map JavaDoc;
15
16 import com.sleepycat.je.DatabaseException;
17 import com.sleepycat.je.cleaner.TrackedFileSummary;
18 import com.sleepycat.je.cleaner.UtilizationTracker;
19 import com.sleepycat.je.dbi.DatabaseId;
20 import com.sleepycat.je.dbi.DbTree;
21 import com.sleepycat.je.dbi.EnvironmentImpl;
22 import com.sleepycat.je.log.entry.INContainingEntry;
23 import com.sleepycat.je.log.entry.INLogEntry;
24 import com.sleepycat.je.log.entry.LNLogEntry;
25 import com.sleepycat.je.log.entry.LogEntry;
26 import com.sleepycat.je.log.entry.NodeLogEntry;
27 import com.sleepycat.je.tree.FileSummaryLN;
28 import com.sleepycat.je.tree.IN;
29 import com.sleepycat.je.tree.INDeleteInfo;
30 import com.sleepycat.je.tree.INDupDeleteInfo;
31 import com.sleepycat.je.tree.MapLN;
32 import com.sleepycat.je.utilint.DbLsn;
33
34 /**
35  * INFileReader supports recovery by scanning log files during the IN rebuild
36  * pass. It looks for internal nodes (all types), segregated by whether they
37  * belong to the main tree or the duplicate trees.
38  *
39  * <p>This file reader can also be run in tracking mode to keep track of the
40  * maximum node id, database id and txn id seen so those sequences can be
41  * updated properly at recovery. In this mode it also performs utilization
42  * counting. It is only run once in tracking mode per recovery, in the
43  * first phase of recovery.</p>
44  */

45 public class INFileReader extends FileReader {
46
47     /* Information about the last entry seen. */
48     private boolean lastEntryWasDelete;
49     private boolean lastEntryWasDupDelete;
50     private LogEntryType fromLogType;
51     private boolean isProvisional;
52
53     /*
54      * targetEntryMap maps DbLogEntryTypes to log entries. We use this
55      * collection to find the right LogEntry instance to read in the
56      * current entry.
57      */

58     private Map JavaDoc targetEntryMap;
59     private LogEntry targetLogEntry;
60
61     /*
62      * For tracking non-target log entries.
63      * Note that dbIdTrackingEntry and txnIdTrackingEntry do not overlap with
64      * targetLogEntry, since the former are LNs and the latter are INs.
65      * But nodeTrackingEntry and inTrackingEntry can overlap with the others,
66      * and we only load one of them when they do overlap.
67      */

68     private Map JavaDoc dbIdTrackingMap;
69     private LNLogEntry dbIdTrackingEntry;
70     private Map JavaDoc txnIdTrackingMap;
71     private LNLogEntry txnIdTrackingEntry;
72     private Map JavaDoc otherNodeTrackingMap;
73     private NodeLogEntry nodeTrackingEntry;
74     private INLogEntry inTrackingEntry;
75     private LNLogEntry fsTrackingEntry;
76
77     /*
78      * If trackIds is true, peruse all node entries for the maximum
79      * node id, check all MapLNs for the maximum db id, and check all
80      * LNs for the maximum txn id
81      */

82     private boolean trackIds;
83     private long maxNodeId;
84     private int maxDbId;
85     private long maxTxnId;
86     private boolean mapDbOnly;
87
88     /* Used for utilization tracking. */
89     private long partialCkptStart;
90     private UtilizationTracker tracker;
91     private Map JavaDoc fileSummaryLsns;
92
93     /**
94      * Create this reader to start at a given LSN.
95      */

96     public INFileReader(EnvironmentImpl env,
97                         int readBufferSize,
98                         long startLsn,
99                         long finishLsn,
100                         boolean trackIds,
101             boolean mapDbOnly,
102                         long partialCkptStart,
103                         Map JavaDoc fileSummaryLsns)
104         throws IOException JavaDoc, DatabaseException {
105
106         super(env, readBufferSize, true, startLsn, null,
107           DbLsn.NULL_LSN, finishLsn);
108
109         this.trackIds = trackIds;
110     this.mapDbOnly = mapDbOnly;
111         targetEntryMap = new HashMap JavaDoc();
112
113         if (trackIds) {
114             maxNodeId = 0;
115             maxDbId = 0;
116             tracker = env.getUtilizationTracker();
117             this.partialCkptStart = partialCkptStart;
118             this.fileSummaryLsns = fileSummaryLsns;
119             fsTrackingEntry = (LNLogEntry)
120                 LogEntryType.LOG_FILESUMMARYLN.getNewLogEntry();
121
122             dbIdTrackingMap = new HashMap JavaDoc();
123             txnIdTrackingMap = new HashMap JavaDoc();
124             otherNodeTrackingMap = new HashMap JavaDoc();
125
126             dbIdTrackingMap.put(LogEntryType.LOG_MAPLN_TRANSACTIONAL,
127                                 LogEntryType.LOG_MAPLN_TRANSACTIONAL.
128                 getNewLogEntry());
129             dbIdTrackingMap.put(LogEntryType.LOG_MAPLN,
130                                 LogEntryType.LOG_MAPLN.getNewLogEntry());
131             txnIdTrackingMap.put(LogEntryType.LOG_LN_TRANSACTIONAL,
132                                  LogEntryType.LOG_LN_TRANSACTIONAL.
133                  getNewLogEntry());
134             txnIdTrackingMap.put(LogEntryType.LOG_MAPLN_TRANSACTIONAL,
135                                  LogEntryType.LOG_MAPLN_TRANSACTIONAL.
136                  getNewLogEntry());
137             txnIdTrackingMap.put(LogEntryType.LOG_NAMELN_TRANSACTIONAL,
138                                  LogEntryType.LOG_NAMELN_TRANSACTIONAL.
139                  getNewLogEntry());
140             txnIdTrackingMap.put(LogEntryType.LOG_DEL_DUPLN_TRANSACTIONAL,
141                                  LogEntryType.LOG_DEL_DUPLN_TRANSACTIONAL.
142                  getNewLogEntry());
143             txnIdTrackingMap.put(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL,
144                                  LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL.
145                  getNewLogEntry());
146         }
147     }
148
149     /**
150      * Configure this reader to target this kind of entry.
151      */

152     public void addTargetType(LogEntryType entryType)
153         throws DatabaseException {
154
155         targetEntryMap.put(entryType, entryType.getNewLogEntry());
156     }
157
158     /**
159      * If we're tracking node, database and txn ids, we want to see all node
160      * log entries. If not, we only want to see IN entries.
161      * @return true if this is an IN entry.
162      */

163     protected boolean isTargetEntry(byte entryTypeNum,
164                                     byte entryTypeVersion)
165         throws DatabaseException {
166
167         lastEntryWasDelete = false;
168         lastEntryWasDupDelete = false;
169         targetLogEntry = null;
170         dbIdTrackingEntry = null;
171         txnIdTrackingEntry = null;
172         nodeTrackingEntry = null;
173         inTrackingEntry = null;
174         fsTrackingEntry = null;
175         isProvisional = LogEntryType.isProvisional(entryTypeVersion);
176
177         /* Get the log entry type instance we need to read the entry. */
178         fromLogType = LogEntryType.findType(entryTypeNum, entryTypeVersion);
179         LogEntry possibleTarget = (LogEntry) targetEntryMap.get(fromLogType);
180
181         /*
182          * If the entry is provisional, we won't be reading it in its entirety;
183          * otherwise, we try to establish targetLogEntry.
184          */

185         if (!isProvisional) {
186             targetLogEntry = possibleTarget;
187         }
188
189         /* Was the log entry an IN deletion? */
190         if (LogEntryType.LOG_IN_DELETE_INFO.equals(fromLogType)) {
191             lastEntryWasDelete = true;
192         }
193
194         if (LogEntryType.LOG_IN_DUPDELETE_INFO.equals(fromLogType)) {
195             lastEntryWasDupDelete = true;
196         }
197
198         if (trackIds) {
199
200             /*
201              * Check if it's a db or txn id tracking entry. Note that these
202              * entries do not overlap with targetLogEntry.
203              */

204             if (!isProvisional) {
205                 dbIdTrackingEntry = (LNLogEntry)
206                     dbIdTrackingMap.get(fromLogType);
207                 txnIdTrackingEntry = (LNLogEntry)
208                     txnIdTrackingMap.get(fromLogType);
209             }
210
211             /*
212              * Determine nodeTrackingEntry, inTrackingEntry, fsTrackingEntry.
213              * Note that these entries do overlap with targetLogEntry.
214              */

215             if (fromLogType.isNodeType()) {
216                 if (possibleTarget != null) {
217                     nodeTrackingEntry = (NodeLogEntry) possibleTarget;
218                 } else if (dbIdTrackingEntry != null) {
219                     nodeTrackingEntry = dbIdTrackingEntry;
220                 } else if (txnIdTrackingEntry != null) {
221                     nodeTrackingEntry = txnIdTrackingEntry;
222                 } else {
223                     nodeTrackingEntry = (NodeLogEntry)
224                         otherNodeTrackingMap.get(fromLogType);
225                     if (nodeTrackingEntry == null) {
226                         nodeTrackingEntry = (NodeLogEntry)
227                             fromLogType.getNewLogEntry();
228                         otherNodeTrackingMap.put(fromLogType,
229                                                  nodeTrackingEntry);
230                     }
231                 }
232                 if (nodeTrackingEntry instanceof INLogEntry) {
233                     inTrackingEntry = (INLogEntry) nodeTrackingEntry;
234                 }
235                 if (LogEntryType.LOG_FILESUMMARYLN.equals(fromLogType)) {
236                     fsTrackingEntry = (LNLogEntry) nodeTrackingEntry;
237                 }
238             }
239
240             /*
241              * Count all entries except for the file header as new.
242              * UtilizationTracker does not count the file header.
243              */

244             if (!LogEntryType.LOG_FILE_HEADER.equals(fromLogType)) {
245                 tracker.countNewLogEntry(getLastLsn(), fromLogType,
246                                          LogManager.HEADER_BYTES +
247                                          currentEntrySize);
248             }
249
250             /*
251              * Return true if this entry should be passed on to processEntry.
252              * If we're tracking ids, return if this is a targeted entry
253              * or if it's any kind of tracked entry or node.
254              */

255             return (targetLogEntry != null) ||
256         (dbIdTrackingEntry != null) ||
257         (txnIdTrackingEntry != null) ||
258         (nodeTrackingEntry != null);
259         } else {
260
261             /*
262              * Return true if this entry should be passed on to processEntry.
263              * If we're not tracking ids, only return true if it's a targeted
264              * entry.
265              */

266             return (targetLogEntry != null);
267         }
268     }
269
270     /**
271      * This reader looks at all nodes for the max node id and database id. It
272      * only returns non-provisional INs and IN delete entries.
273      */

274     protected boolean processEntry(ByteBuffer JavaDoc entryBuffer)
275         throws DatabaseException {
276
277         boolean useEntry = false;
278         boolean entryLoaded = false;
279
280         /* If this is a targetted entry, read the entire log entry. */
281         if (targetLogEntry != null) {
282             targetLogEntry.readEntry(entryBuffer, currentEntrySize,
283                                      currentEntryTypeVersion, true);
284         DatabaseId dbId = getDatabaseId();
285         boolean isMapDb = dbId.equals(DbTree.ID_DB_ID);
286             useEntry = (!mapDbOnly || isMapDb);
287             entryLoaded = true;
288         }
289         
290         /* Do a partial load during tracking if necessary. */
291         if (trackIds) {
292
293             /*
294              * Do partial load of db and txn id tracking entries if necessary.
295              * Note that these entries do not overlap with targetLogEntry.
296              *
297              * XXX We're doing a full load for now, since LNLogEntry does not
298              * read the db and txn id in a partial load, only the node id.
299              */

300             LNLogEntry lnEntry = null;
301             if (dbIdTrackingEntry != null) {
302                 /* This entry has a db id */
303         lnEntry = dbIdTrackingEntry;
304         lnEntry.readEntry(entryBuffer, currentEntrySize,
305                   currentEntryTypeVersion,
306                   true /* full load */);
307         entryLoaded = true;
308                 MapLN mapLN = (MapLN) lnEntry.getMainItem();
309                 int dbId = mapLN.getDatabase().getId().getId();
310                 if (dbId > maxDbId) {
311                     maxDbId = dbId;
312                 }
313             }
314             if (txnIdTrackingEntry != null) {
315                 /* This entry has a txn id */
316                 if (lnEntry == null) {
317                     lnEntry = txnIdTrackingEntry;
318                     lnEntry.readEntry(entryBuffer, currentEntrySize,
319                                       currentEntryTypeVersion,
320                                       true /* full load */);
321                     entryLoaded = true;
322                 }
323                 long txnId = lnEntry.getTxnId().longValue();
324                 if (txnId > maxTxnId) {
325                     maxTxnId = txnId;
326                 }
327             }
328
329             /*
330              * Perform utilization counting under trackIds to prevent
331              * double-counting.
332              */

333             if (fsTrackingEntry != null) {
334
335                 /* Must do full load to get key from file summary LN. */
336                 if (!entryLoaded) {
337                     nodeTrackingEntry.readEntry(entryBuffer, currentEntrySize,
338                                                 currentEntryTypeVersion,
339                                                 true /* full load */);
340                     entryLoaded = true;
341                 }
342
343                 /*
344                  * When a FileSummaryLN is encountered, reset the tracked
345                  * summary for that file to replay what happens when a
346                  * FileSummaryLN log entry is written.
347                  */

348                 byte[] keyBytes = fsTrackingEntry.getKey();
349                 FileSummaryLN fsln =
350                     (FileSummaryLN) fsTrackingEntry.getMainItem();
351                 long fileNum = fsln.getFileNumber(keyBytes);
352                 TrackedFileSummary trackedLN = tracker.getTrackedFile(fileNum);
353                 if (trackedLN != null) {
354                     trackedLN.reset();
355                 }
356
357                 /* Save the LSN of the FileSummaryLN for use by undo/redo. */
358                 fileSummaryLsns.put(new Long JavaDoc(fileNum), new Long JavaDoc(getLastLsn()));
359
360                 /*
361                  * SR 10395: Do not cache the file summary in the
362                  * UtilizationProfile here, since it may be for a deleted log
363                  * file.
364                  */

365             }
366
367             /*
368              * Do partial load of nodeTrackingEntry (and inTrackingEntry) if
369              * not already loaded. We only need the node id.
370              */

371             if (nodeTrackingEntry != null) {
372                 if (!entryLoaded) {
373                     nodeTrackingEntry.readEntry(entryBuffer, currentEntrySize,
374                                                 currentEntryTypeVersion,
375                                                 false /* partial load */);
376                     entryLoaded = true;
377                 }
378                 /* Keep track of the largest node id seen. */
379                 long nodeId = nodeTrackingEntry.getNodeId();
380                 maxNodeId = (nodeId > maxNodeId) ? nodeId: maxNodeId;
381             }
382
383             if (inTrackingEntry != null) {
384                 assert entryLoaded : "All nodes should have been loaded";
385
386                 /*
387                  * Count the obsolete LSN of the previous version, if available
388                  * and if not already counted. Use inexact counting for two
389                  * reasons: 1) we don't always have the full LSN because
390                  * earlier log versions only had the file number, and 2) we
391                  * can't guarantee obsoleteness for provisional INs.
392                  */

393                 long oldLsn = inTrackingEntry.getObsoleteLsn();
394                 if (oldLsn != DbLsn.NULL_LSN) {
395                     long newLsn = getLastLsn();
396                     if (!isObsoleteLsnAlreadyCounted(oldLsn, newLsn)) {
397                         tracker.countObsoleteNodeInexact
398                             (oldLsn, fromLogType, 0);
399                     }
400                 }
401
402                 /*
403                  * Count a provisional IN as obsolete if it follows
404                  * partialCkptStart. It cannot have been already counted,
405                  * because provisional INs are not normally counted as
406                  * obsolete; they are only considered obsolete when they are
407                  * part of a partial checkpoint.
408                  *
409                  * Depending on the exact point at which the checkpoint was
410                  * aborted, this technique is not always accurate; therefore
411                  * inexact counting must be used.
412                  */

413                 if (isProvisional && partialCkptStart != DbLsn.NULL_LSN) {
414                     oldLsn = getLastLsn();
415                     if (DbLsn.compareTo(partialCkptStart, oldLsn) < 0) {
416                         tracker.countObsoleteNodeInexact
417                             (oldLsn, fromLogType, 0);
418                     }
419                 }
420             }
421         }
422
423         /* Return true if this entry should be processed */
424         return useEntry;
425     }
426
427     /**
428      * Returns whether a given obsolete LSN has already been counted in the
429      * utilization profile. If true is returned, it should not be counted
430      * again, to prevent double-counting.
431      */

432     private boolean isObsoleteLsnAlreadyCounted(long oldLsn, long newLsn) {
433
434         /* If the file summary follows the new LSN, it was already counted. */
435         Long JavaDoc fileNum = new Long JavaDoc(DbLsn.getFileNumber(oldLsn));
436         long fileSummaryLsn =
437         DbLsn.longToLsn((Long JavaDoc) fileSummaryLsns.get(fileNum));
438         int cmpFsLsnToNewLsn = (fileSummaryLsn != DbLsn.NULL_LSN) ?
439         DbLsn.compareTo(fileSummaryLsn, newLsn) : -1;
440         return (cmpFsLsnToNewLsn >= 0);
441     }
442
443     /**
444      * Get the last IN seen by the reader.
445      */

446     public IN getIN()
447         throws DatabaseException {
448             
449         return ((INContainingEntry) targetLogEntry).getIN(env);
450     }
451
452     /**
453      * Get the last databaseId seen by the reader.
454      */

455     public DatabaseId getDatabaseId() {
456         if (lastEntryWasDelete) {
457             return ((INDeleteInfo) targetLogEntry.getMainItem()).
458                 getDatabaseId();
459         } else if (lastEntryWasDupDelete) {
460             return ((INDupDeleteInfo) targetLogEntry.getMainItem()).
461                 getDatabaseId();
462         } else {
463             return ((INContainingEntry) targetLogEntry).getDbId();
464         }
465     }
466
467     /**
468      * Get the maximum node id seen by the reader.
469      */

470     public long getMaxNodeId() {
471         return maxNodeId;
472     }
473
474     /**
475      * Get the maximum db id seen by the reader.
476      */

477     public int getMaxDbId() {
478         return maxDbId;
479     }
480
481     /**
482      * Get the maximum txn id seen by the reader.
483      */

484     public long getMaxTxnId() {
485         return maxTxnId;
486     }
487
488     /**
489      * @return true if the last entry was a delete info entry.
490      */

491     public boolean isDeleteInfo() {
492         return lastEntryWasDelete;
493     }
494     
495     /**
496      * @return true if the last entry was a dup delete info entry.
497      */

498     public boolean isDupDeleteInfo() {
499         return lastEntryWasDupDelete;
500     }
501     
502     /**
503      * Get the deleted node id stored in the last delete info log entry.
504      */

505     public long getDeletedNodeId() {
506         return ((INDeleteInfo)
507                 targetLogEntry.getMainItem()).getDeletedNodeId();
508     }
509
510     /**
511      * Get the deleted id key stored in the last delete info log entry.
512      */

513     public byte[] getDeletedIdKey() {
514         return ((INDeleteInfo)
515                 targetLogEntry.getMainItem()).getDeletedIdKey();
516     }
517
518     /**
519      * Get the deleted node id stored in the last delete info log entry.
520      */

521     public long getDupDeletedNodeId() {
522         return ((INDupDeleteInfo)
523                 targetLogEntry.getMainItem()).getDeletedNodeId();
524     }
525
526     /**
527      * Get the deleted main key stored in the last delete info log entry.
528      */

529     public byte[] getDupDeletedMainKey() {
530         return ((INDupDeleteInfo)
531                 targetLogEntry.getMainItem()).getDeletedMainKey();
532     }
533
534     /**
535      * Get the deleted main key stored in the last delete info log entry.
536      */

537     public byte[] getDupDeletedDupKey() {
538         return ((INDupDeleteInfo)
539                 targetLogEntry.getMainItem()).getDeletedDupKey();
540     }
541
542     /**
543      * Get the LSN that should represent this IN. For most INs, it's the LSN
544      * that was just read. For BINDelta entries, it's the LSN of the last
545      * full version.
546      */

547     public long getLsnOfIN() {
548         return ((INContainingEntry) targetLogEntry).getLsnOfIN(getLastLsn());
549     }
550
551     /**
552      * Get the current log entry type.
553      */

554     public LogEntryType getLogEntryType() {
555         return LogEntryType.findType(currentEntryTypeNum,
556                                      currentEntryTypeVersion);
557     }
558 }
559
Popular Tags