package com.sleepycat.je.dbi; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.cleaner.OffsetList; import com.sleepycat.je.log.LogEntryType; import com.sleepycat.je.tree.BIN; import com.sleepycat.je.tree.DBIN; import com.sleepycat.je.tree.DIN; import com.sleepycat.je.tree.IN; import com.sleepycat.je.tree.Node; import com.sleepycat.je.utilint.DbLsn; import de.ovgu.cide.jakutil.*; /** * Class to walk over the tree using sorted LSN fetching for parts of the tree * that are not in memory. Returns LSNs for each node in the tree <b>except</b> * the root IN, but in an arbitrary order (i.e. not key order). The caller is * responsible for getting the root IN's LSN explicitly. * <p> * A calllback function specified in the constructor is executed for each LSN * found. * <p> * The walker works in two phases. The first phase is to gather and return all * the INs from the INList that match the database being iterated over. For each * IN, all of the LSNs of the children are passed to the callback method * (processLSN). If the child was not in memory, it is added to a list of LSNs * to read. When all of the in-memory INs have been processed, the list of LSNs * that were harvested are sorted. * <p> * Then for each of the sorted LSNs, the target is fetched, the type determined, * and the LSN and type passed to the callback method for processing. LSNs of * the children of those nodes are retrieved and the process repeated until * there are no more nodes to be fetched for this database's tree. */ public class SortedLSNTreeWalker { public interface TreeNodeProcessor { void processLSN( long childLSN, LogEntryType childType); } protected DatabaseImpl dbImpl; private EnvironmentImpl envImpl; private long rootLsn; private boolean dups; private boolean removeINsFromINList; private boolean setDbState; private long[] currentLSNs; private int currentLSNIdx=0; private OffsetList accumulatedLSNFileNumbers; private OffsetList accumulatedLSNFileOffsets; private TreeNodeProcessor callback; protected boolean accumulateLNs=false; public SortedLSNTreeWalker( DatabaseImpl dbImpl, boolean removeINsFromINList, boolean setDbState, long rootLsn, TreeNodeProcessor callback) throws DatabaseException { this.dbImpl=dbImpl; this.envImpl=dbImpl.getDbEnvironment(); if (envImpl == null) { throw new DatabaseException("environmentImpl is null for target db " + dbImpl.getDebugName()); } this.dups=dbImpl.getSortedDuplicates(); this.removeINsFromINList=removeINsFromINList; this.setDbState=setDbState; this.rootLsn=rootLsn; this.callback=callback; currentLSNs=new long[0]; currentLSNIdx=0; } private boolean extractINsForDb( INList inList) throws DatabaseException { return new SortedLSNTreeWalker_extractINsForDb(this,inList).execute(); } /** * Find all non-resident nodes, and execute the callback. The root IN's LSN * is not returned to the callback. */ public void walk() throws DatabaseException { walkInternal(); } protected void walkInternal() throws DatabaseException { INList inList=envImpl.getInMemoryINs(); IN root=null; if (!extractINsForDb(inList)) { if (rootLsn == DbLsn.NULL_LSN) { return; } root=getRootIN(rootLsn); accumulateLSNs(root); releaseRootIN(root); } this.hook359(); while (true) { maybeGetMoreINs(); if (currentLSNs != null && currentLSNIdx < currentLSNs.length) { fetchAndProcessLSN(currentLSNs[currentLSNIdx++]); } else { break; } } } private void maybeGetMoreINs(){ if ((currentLSNs != null && currentLSNIdx >= currentLSNs.length)) { if (accumulatedLSNFileNumbers == null || accumulatedLSNFileNumbers.size() == 0) { currentLSNs=null; currentLSNIdx=Integer.MAX_VALUE; return; } long[] tempFileNumbers=accumulatedLSNFileNumbers.toArray(); long[] tempFileOffsets=accumulatedLSNFileOffsets.toArray(); int nLSNs=tempFileNumbers.length; currentLSNIdx=0; currentLSNs=new long[nLSNs]; for (int i=0; i < nLSNs; i++) { currentLSNs[i]=DbLsn.makeLsn(tempFileNumbers[i],tempFileOffsets[i]); } Arrays.sort(currentLSNs); accumulatedLSNFileNumbers=null; accumulatedLSNFileOffsets=null; } } private void accumulateLSNs( IN in) throws DatabaseException { boolean accumulate=true; if (!accumulateLNs) { if ((!dups && (in instanceof BIN)) || (in instanceof DBIN)) { accumulate=false; } } for (int i=0; i < in.getNEntries(); i++) { if (in.isEntryPendingDeleted(i) || in.isEntryKnownDeleted(i)) { continue; } long lsn=in.getLsn(i); Node node=in.getTarget(i); if (accumulate && (node == null)) { if (accumulatedLSNFileNumbers == null) { accumulatedLSNFileNumbers=new OffsetList(); accumulatedLSNFileOffsets=new OffsetList(); } accumulatedLSNFileNumbers.add(DbLsn.getFileNumber(lsn),false); accumulatedLSNFileOffsets.add(DbLsn.getFileOffset(lsn),false); addToLsnINMap(new Long(lsn),in,i); } else { callback.processLSN(lsn,(node == null) ? LogEntryType.LOG_LN : node.getLogType()); } } if (in instanceof DIN) { if (in.isRoot()) { DIN din=(DIN)in; callback.processLSN(din.getDupCountLNRef().getLsn(),LogEntryType.LOG_DUPCOUNTLN); } } } private void fetchAndProcessLSN( long lsn) throws DatabaseException { Node node=fetchLSN(lsn); if (node != null) { callback.processLSN(lsn,node.getLogType()); if (node instanceof IN) { accumulateLSNs((IN)node); } } } /** * The default behavior fetches the rootIN from the log, but classes * extending this may fetch the root from the tree. */ protected IN getRootIN( long rootLsn) throws DatabaseException { return (IN)envImpl.getLogManager().get(rootLsn); } protected void releaseRootIN( IN ignore) throws DatabaseException { } protected void addToLsnINMap( Long lsn, IN in, int index){ } protected Node fetchLSN( long lsn) throws DatabaseException { return (Node)envImpl.getLogManager().get(lsn); } @MethodObject static class SortedLSNTreeWalker_extractINsForDb { SortedLSNTreeWalker_extractINsForDb( SortedLSNTreeWalker _this, INList inList){ this._this=_this; this.inList=inList; } boolean execute() throws DatabaseException { foundSome=false; foundSet=new HashSet(); this.hook360(); this.hook356(); try { this.hook357(); iter=inList.iterator(); while (iter.hasNext()) { thisIN=(IN)iter.next(); if (thisIN.getDatabase() == _this.dbImpl) { foundSome=true; if (_this.removeINsFromINList) { iter.remove(); this.hook361(); } foundSet.add(thisIN); } } } catch ( DatabaseException e) { this.hook362(); throw e; } finally { this.hook358(); } if (foundSome) { iter1=foundSet.iterator(); while (iter1.hasNext()) { thisIN1=(IN)iter1.next(); _this.accumulateLSNs(thisIN1); } } foundSet=null; return foundSome; } protected SortedLSNTreeWalker _this; protected INList inList; protected boolean foundSome; protected Set foundSet; protected long memoryChange; protected MemoryBudget mb; protected Iterator iter; protected IN thisIN; protected Iterator iter1; protected IN thisIN1; protected void hook356() throws DatabaseException { } protected void hook357() throws DatabaseException { } protected void hook358() throws DatabaseException { } protected void hook360() throws DatabaseException { } protected void hook361() throws DatabaseException { } protected void hook362() throws DatabaseException { } } protected void hook359() throws DatabaseException { } }