/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-04 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.storage.cache;
import org.apache.log4j.Logger;
import org.exist.util.hashtable.SequencedLongHashMap;
/**
* Keeps track of various cache parameters. Most important,
* this class is used to determine if a cache should be grown by
* computing the cache efficiency, which is expressed as the amount
* of trashing that occurs during a certain
* period of time. Trashing occurs if a page is replaced from the cache
* and is reloaded shortly after. For the B+-tree pages, we normally
* don't want any trashing at all.
*
* @author wolf
*
*/
public class Accounting {
private final static Logger LOG = Logger.getLogger(Accounting.class);
private final static Object DUMMY = new Object();
/** the period (in milliseconds) for which trashing is recorded. */
private int checkPeriod = 30000;
/** start of the last check period */
private long checkPeriodStart = System.currentTimeMillis();
/** max. entries to keep in the table of replaced pages */
private int maxEntries = 1000;
/** total cache hits during the lifetime of the cache*/
private int hits = 0;
/** total cache misses during the lifetime of the cache */
private int misses = 0;
/** the current size of the cache */
private int totalSize = 0;
/** the number of pages replaced and reloaded during the check period */
private int thrashing = 0;
/** determines the amount of allowed trashing before a cache resize will
* be requested. This is expressed as a fraction of the total cache size.
*/
private double thrashingFactor;
/** the map used to track replaced page numbers */
private SequencedLongHashMap map;
public Accounting(double thrashingFactor) {
map = new SequencedLongHashMap((maxEntries * 3) / 2);
this.thrashingFactor = thrashingFactor;
}
/**
* Set the current size of the cache. Should be called by the
* cache whenever it changes its size.
*
* @param totalSize
*/
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
/**
* Increment the number of total cache hits by one.
*/
public void hitIncrement() {
++hits;
}
/**
* Returns the number of total cache hits during the
* lifetime of the cache.
*
* @return number of total cache hits
*/
public int getHits() {
return hits;
}
/**
* Increment the number of total cache faults by one.
*/
public void missesIncrement() {
++misses;
}
/**
* Returns the number of total cache faults.
* @return number of total cache faults
*/
public int getMisses() {
return misses;
}
/**
* Called by the cache to signal that a page was replaced
* in order to store the Cacheable object passed.
*
* @param cacheable
*/
public void replacedPage(Cacheable cacheable) {
if (System.currentTimeMillis() - checkPeriodStart > checkPeriod) {
map.clear();
thrashing = 0;
checkPeriodStart = System.currentTimeMillis();
}
if (map.size() == maxEntries) {
map.removeFirst();
}
if (map.get(cacheable.getKey()) != null) {
++thrashing;
} else
map.put(cacheable.getKey(), DUMMY);
}
/**
* Return the current amount of trashing.
* @return current amount of trashing
*/
public int getThrashing() {
return thrashing;
}
/**
* Returns true if a cache resize would increase the
* cache efficiency.
*
* @return True if a cache resize would increase the
* cache efficiency
*/
public boolean resizeNeeded() {
return thrashing > totalSize * thrashingFactor;
}
public void reset() {
map.clear();
thrashing = 0;
checkPeriodStart = System.currentTimeMillis();
}
public void stats() {
LOG.debug("hits: " + hits
+ "; misses: " + misses
+ "; thrashing: " + getThrashing()
+ "; thrashing period: " + checkPeriod);
}
}