/**
* 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 3 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 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, see <http://www.gnu.org/licenses/>.
*
* @author Gabriel Roldan (OpenGeo) 2010
*
*/
package org.geowebcache.diskquota.storage;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
/**
* Collects statistics about a tile page, including its frequency of use, the last access time,
* the amount of tiles present in the page expressed as a fill factor, and the total number of hits.
* <p>
* The information provided by this class drives the expiration policies of the disk quota module
*/
public class PageStats implements Serializable {
private static final long serialVersionUID = 719776699585233200L;
private long id;
private long pageId;
/**
* Approximate average frequency of use of this page per minute, computed each time page
* {@link #addHitsAndAccessTime hits } are added based on the previous frequency of use, the
* time elapsed since the last use ({@link #lastAccessTimeMinutes}), and the new number of hits
* added in that period of time.
*/
private float frequencyOfUse;
private int lastAccessTimeMinutes;
private float fillFactor;
private BigInteger numHits;
PageStats() {
//
}
public PageStats(long pageId) {
this.pageId = Long.valueOf(pageId);
// should be the same than the tile creation time as is used as a base to measure the
// frequency of use of this page
this.numHits = BigInteger.ZERO;
this.lastAccessTimeMinutes = SystemUtils.get().currentTimeMinutes();
}
PageStats(TilePage page) {
this(page.getId());
}
public void addHitsAndAccessTime(final long addedHits, int lastAccessTimeMinutes,
final int creationTimeMinutes) {
if (lastAccessTimeMinutes < creationTimeMinutes) {
lastAccessTimeMinutes = creationTimeMinutes;
}
if (fillFactor <= 0f) {
// we're in trouble, how could this happen? well because somehow the hits are being
// recorded before the quota increase? it's not that tragic
fillFactor = Float.MIN_VALUE;
}
this.numHits = this.numHits.add(BigInteger.valueOf(addedHits));
BigDecimal age = new BigDecimal(1 + lastAccessTimeMinutes - creationTimeMinutes);
this.frequencyOfUse = new BigDecimal(this.numHits).divide(age, 7, RoundingMode.CEILING)
.multiply(new BigDecimal(fillFactor)).floatValue();
this.lastAccessTimeMinutes = lastAccessTimeMinutes;
}
public void addTiles(long numTiles, BigInteger maxTiles) {
if (fillFactor == 1.0f && numTiles >= 0) {
return;
}
if (fillFactor == 0.0f && numTiles <= 0) {
return;
}
// trading some computational overhead by storage savings here...
BigDecimal currFillFactor = new BigDecimal(fillFactor);
BigDecimal addedTiles = new BigDecimal(numTiles);
BigDecimal addedFillFactor = addedTiles.divide(new BigDecimal(maxTiles), 7,
RoundingMode.CEILING);
currFillFactor = currFillFactor.add(addedFillFactor);
this.fillFactor = currFillFactor.floatValue();
if (fillFactor > 1f) {
fillFactor = 1f;
} else if (fillFactor < 0f) {
fillFactor = 0f;
}
}
public float getFillFactor() {
return fillFactor;
}
public void setFillFactor(float fillFactor) {
this.fillFactor = fillFactor;
}
public int getLastAccessTimeMinutes() {
return lastAccessTimeMinutes;
}
public void setLastAccessMinutes(int lastAccessMinutes) {
this.lastAccessTimeMinutes = lastAccessMinutes;
}
public long getPageId() {
return pageId;
}
public void setPageId(long pageId) {
this.pageId = pageId;
}
public float getFrequencyOfUsePerMinute() {
return frequencyOfUse;
}
public void setFrequencyOfUsePerMinute(float lfuHotnes) {
this.frequencyOfUse = lfuHotnes;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append("[page: ").append(pageId);
sb.append(", fillFactor: ").append(fillFactor);
sb.append(", frequencyOfUse: ").append(frequencyOfUse);
sb.append(", last access: ")
.append(SystemUtils.get().currentTimeMinutes() - lastAccessTimeMinutes)
.append("m ago]");
return sb.toString();
}
public void setNumHits(BigInteger numHits) {
this.numHits = numHits;
}
public BigInteger getNumHits() {
return numHits;
}
}