package com.sleepycat.je.cleaner; import java.util.ArrayList; import java.util.List; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.dbi.EnvironmentImpl; import com.sleepycat.je.dbi.MemoryBudget; import com.sleepycat.je.log.LogEntryType; import com.sleepycat.je.utilint.DbLsn; import de.ovgu.cide.jakutil.*; /** * Tracks changes to the utilization profile since the last checkpoint. * <p> * All changes to this object occur must under the log write latch. It is * possible to read tracked info without holding the latch. This is done by the * cleaner when selecting a file and by the checkpointer when determining what * FileSummaryLNs need to be written. To read tracked info outside the log write * latch, call getTrackedFile or getTrackedFiles. activateCleaner can also be * called outside the latch. * </p> */ public class UtilizationTracker { private EnvironmentImpl env; private Cleaner cleaner; private List files; private long activeFile; private TrackedFileSummary[] snapshot; private long bytesSinceActivate; /** * Creates an empty tracker. The cleaner field of the environment object * must be initialized before using this constructor. */ public UtilizationTracker( EnvironmentImpl env) throws DatabaseException { this(env,env.getCleaner()); } /** * Constructor used by the cleaner constructor, prior to setting the cleaner * field of the environment. */ UtilizationTracker( EnvironmentImpl env, Cleaner cleaner) throws DatabaseException { assert cleaner != null; this.env=env; this.cleaner=cleaner; files=new ArrayList(); snapshot=new TrackedFileSummary[0]; activeFile=-1; } public EnvironmentImpl getEnvironment(){ return env; } /** * Wakeup the cleaner thread and reset the log byte counter. */ public void activateCleaner(){ env.getCleaner().wakeup(); bytesSinceActivate=0; } /** * Returns a snapshot of the files being tracked as of the last time a log * entry was added. The summary info returned is the delta since the last * checkpoint, not the grand totals, and is approximate since it is changing * in real time. This method may be called without holding the log write * latch. * <p> * If files are added or removed from the list of tracked files in real * time, the returned array will not be changed since it is a snapshot. But * the objects contained in the array are live and will be updated in real * time under the log write latch. The array and the objects in the array * should not be modified by the caller. * </p> */ public TrackedFileSummary[] getTrackedFiles(){ return snapshot; } /** * Returns one file from the snapshot of tracked files, or null if the given * file number is not in the snapshot array. * @see #getTrackedFiles */ public TrackedFileSummary getTrackedFile( long fileNum){ TrackedFileSummary[] a=snapshot; for (int i=0; i < a.length; i+=1) { if (a[i].getFileNumber() == fileNum) { return a[i]; } } return null; } /** * Counts the addition of all new log entries including LNs, and returns * whether the cleaner should be woken. * <p> * Must be called under the log write latch. * </p> */ public boolean countNewLogEntry( long lsn, LogEntryType type, int size){ TrackedFileSummary file=getFile(DbLsn.getFileNumber(lsn)); file.totalCount+=1; file.totalSize+=size; if (type.isNodeType()) { if (inArray(type,LogEntryType.IN_TYPES)) { file.totalINCount+=1; file.totalINSize+=size; } else { file.totalLNCount+=1; file.totalLNSize+=size; } } bytesSinceActivate+=size; return (bytesSinceActivate >= env.getCleaner().cleanerBytesInterval); } /** * Counts a node that has become obsolete and tracks the LSN offset to avoid * a lookup during cleaning. * <p> * This method should only be called for LNs and INs (i.e, only for nodes). * If type is null we assume it is an LN. * </p> * <p> * Must be called under the log write latch. * </p> */ public void countObsoleteNode( long lsn, LogEntryType type){ TrackedFileSummary file=getFile(DbLsn.getFileNumber(lsn)); countOneNode(file,type); file.trackObsolete(DbLsn.getFileOffset(lsn)); } /** * Counts as countObsoleteNode does, but since the LSN may be inexact, does * not track the obsolete LSN offset. * <p> * This method should only be called for LNs and INs (i.e, only for nodes). * If type is null we assume it is an LN. * </p> * <p> * Must be called under the log write latch. * </p> */ public void countObsoleteNodeInexact( long lsn, LogEntryType type){ TrackedFileSummary file=getFile(DbLsn.getFileNumber(lsn)); countOneNode(file,type); } /** * Counts a change in the obsolete status of an node, incrementing the * obsolete count if obsolete is true and decrementing it if obsolete is * false. */ private void countOneNode( TrackedFileSummary file, LogEntryType type){ if (type == null || type.isNodeType()) { if (type == null || !inArray(type,LogEntryType.IN_TYPES)) { file.obsoleteLNCount+=1; } else { file.obsoleteINCount+=1; } } } /** * Adds changes from a given TrackedFileSummary. * <p> * Must be called under the log write latch. * </p> */ public void addSummary( long fileNumber, TrackedFileSummary other){ TrackedFileSummary file=getFile(fileNumber); file.addTrackedSummary(other); } /** * Returns a tracked summary for the given file which will not be flushed. * Used for watching changes that occur while a file is being cleaned. */ public TrackedFileSummary getUnflushableTrackedSummary( long fileNum) throws DatabaseException { TrackedFileSummary file=getFile(fileNum); file.setAllowFlush(false); return file; } /** * Returns a tracked file for the given file number, adding an empty one if * the file is not already being tracked. * <p> * Must be called under the log write latch. * </p> */ private TrackedFileSummary getFile( long fileNum){ if (activeFile < fileNum) { activeFile=fileNum; } int size=files.size(); for (int i=0; i < size; i+=1) { TrackedFileSummary file=(TrackedFileSummary)files.get(i); if (file.getFileNumber() == fileNum) { return file; } } TrackedFileSummary file=new TrackedFileSummary(this,fileNum,cleaner.trackDetail); files.add(file); takeSnapshot(); return file; } /** * Called after the FileSummaryLN is written to the log during checkpoint. * <p> * We keep the active file summary in the tracked file list, but we remove * older files to prevent unbounded growth of the list. * </p> * <p> * Must be called under the log write latch. * </p> */ void resetFile( TrackedFileSummary file){ if (file.getFileNumber() < activeFile && file.getAllowFlush()) { files.remove(file); takeSnapshot(); } } /** * Takes a snapshot of the tracked file list. * <p> * Must be called under the log write latch. * </p> */ private void takeSnapshot(){ TrackedFileSummary[] a=new TrackedFileSummary[files.size()]; files.toArray(a); snapshot=a; } /** * Returns whether an object reference is in an array. */ private boolean inArray( Object o, Object[] a){ for (int i=0; i < a.length; i+=1) { if (a[i] == o) { return true; } } return false; } }