You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
11 KiB
340 lines
11 KiB
// kelondroObjectCache.java
|
|
// ------------------------
|
|
// (C) by Michael Peter Christen; mc@anomic.de
|
|
// first published on http://www.anomic.de
|
|
// Frankfurt, Germany, 2006
|
|
//
|
|
// This is a part of the kelondro database, which is a part of YaCy
|
|
//
|
|
// $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
|
|
// $LastChangedRevision: 1986 $
|
|
// $LastChangedBy: orbiter $
|
|
//
|
|
//
|
|
// LICENSE
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
//
|
|
// A NOTE FROM THE AUTHOR TO THE USERS:
|
|
//
|
|
// Using this software in any meaning (reading, learning, copying, compiling,
|
|
// running) means that you agree that the Author(s) is (are) not responsible
|
|
// for cost, loss of data or any harm that may be caused directly or indirectly
|
|
// by usage of this softare or this documentation. The usage of this software
|
|
// is on your own risk. The installation and usage (starting/running) of this
|
|
// software may allow other people or application to access your computer and
|
|
// any attached devices and is highly dependent on the configuration of the
|
|
// software which must be done by the user of the software; the author(s) is
|
|
// (are) also not responsible for proper configuration and usage of the
|
|
// software, even if provoked by documentation provided together with
|
|
// the software.
|
|
//
|
|
//
|
|
// A NOTE FROM THE AUTHOR TO DEVELOPERS:
|
|
//
|
|
// Contributions and changes to the program code should be marked as such:
|
|
// Please enter your own (C) notice below; they must be compatible with the GPL.
|
|
// Please mark also all changes in the code; if you don't mark them then they
|
|
// can't be identified; thus all unmarked code belong to the copyright holder
|
|
// as mentioned above. A good documentation of code authorities will also help
|
|
// to maintain the code and the project.
|
|
// A re-distribution must contain the intact and unchanged copyright statement.
|
|
|
|
|
|
package de.anomic.kelondro;
|
|
|
|
import java.util.TreeMap;
|
|
|
|
import de.anomic.server.serverMemory;
|
|
|
|
public class kelondroObjectCache {
|
|
|
|
private final TreeMap cache;
|
|
private final kelondroMScoreCluster ages, hasnot;
|
|
private long startTime;
|
|
private int maxHitSize, maxMissSize;
|
|
private long maxAge;
|
|
private long minMem;
|
|
private int readHit, readMiss, writeUnique, writeDouble, cacheDelete, cacheFlush;
|
|
private int hasnotHit, hasnotMiss, hasnotUnique, hasnotDouble, hasnotDelete, hasnotFlush;
|
|
private String name;
|
|
|
|
public kelondroObjectCache(String name, int maxHitSize, int maxMissSize, long maxAge, long minMem) {
|
|
this.name = name;
|
|
this.cache = new TreeMap();
|
|
this.ages = new kelondroMScoreCluster();
|
|
this.hasnot = new kelondroMScoreCluster();
|
|
this.startTime = System.currentTimeMillis();
|
|
this.maxHitSize = Math.max(maxHitSize, 1);
|
|
this.maxMissSize = Math.max(maxMissSize, 1);
|
|
this.maxAge = Math.max(maxAge, 10000);
|
|
this.minMem = Math.max(minMem, 1024 * 1024);
|
|
this.readHit = 0;
|
|
this.readMiss = 0;
|
|
this.writeUnique = 0;
|
|
this.writeDouble = 0;
|
|
this.cacheDelete = 0;
|
|
this.cacheFlush = 0;
|
|
this.hasnotHit = 0;
|
|
this.hasnotMiss = 0;
|
|
this.hasnotUnique = 0;
|
|
this.hasnotDouble = 0;
|
|
this.hasnotDelete = 0;
|
|
this.hasnotFlush = 0;
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public void setMaxAge(long maxAge) {
|
|
this.maxAge = maxAge;
|
|
}
|
|
|
|
public void setMaxHitSize(int maxSize) {
|
|
this.maxHitSize = maxSize;
|
|
}
|
|
|
|
public void setMaxMissSize(int maxSize) {
|
|
this.maxMissSize = maxSize;
|
|
}
|
|
|
|
public int maxHitSize() {
|
|
return this.maxHitSize;
|
|
}
|
|
|
|
public int maxMissSize() {
|
|
return this.maxMissSize;
|
|
}
|
|
|
|
public void setMinMem(int minMem) {
|
|
this.minMem = minMem;
|
|
}
|
|
|
|
public long minAge() {
|
|
if (ages.size() == 0) return 0;
|
|
return System.currentTimeMillis() - longEmit(ages.getMaxScore());
|
|
}
|
|
|
|
public long maxAge() {
|
|
if (ages.size() == 0) return 0;
|
|
return System.currentTimeMillis() - longEmit(ages.getMinScore());
|
|
}
|
|
|
|
public int hitsize() {
|
|
return cache.size();
|
|
}
|
|
|
|
public int misssize() {
|
|
return hasnot.size();
|
|
}
|
|
|
|
public long[] status() {
|
|
return new long[]{
|
|
(long) maxHitSize(),
|
|
(long) maxMissSize(),
|
|
(long) hitsize(),
|
|
(long) misssize(),
|
|
this.maxAge,
|
|
minAge(),
|
|
maxAge(),
|
|
(long) readHit,
|
|
(long) readMiss,
|
|
(long) writeUnique,
|
|
(long) writeDouble,
|
|
(long) cacheDelete,
|
|
(long) cacheFlush,
|
|
(long) hasnotHit,
|
|
(long) hasnotMiss,
|
|
(long) hasnotUnique,
|
|
(long) hasnotDouble,
|
|
(long) hasnotDelete,
|
|
(long) hasnotFlush
|
|
};
|
|
}
|
|
|
|
private static long[] combinedStatus(long[] a, long[] b) {
|
|
return new long[]{
|
|
a[0] + b[0],
|
|
a[1] + b[1],
|
|
a[2] + b[2],
|
|
a[3] + b[3],
|
|
Math.max(a[4], b[4]),
|
|
Math.min(a[5], b[5]),
|
|
Math.max(a[6], b[6]),
|
|
a[7] + b[7],
|
|
a[8] + b[8],
|
|
a[9] + b[9],
|
|
a[10] + b[10],
|
|
a[11] + b[11],
|
|
a[12] + b[12],
|
|
a[13] + b[13],
|
|
a[14] + b[14],
|
|
a[15] + b[15],
|
|
a[16] + b[16],
|
|
a[17] + b[17],
|
|
a[18] + b[18]
|
|
};
|
|
}
|
|
|
|
public static long[] combinedStatus(long[][] a, int l) {
|
|
if ((a == null) || (a.length == 0) || (l == 0)) return null;
|
|
if ((a.length >= 1) && (l == 1)) return a[0];
|
|
if ((a.length >= 2) && (l == 2)) return combinedStatus(a[0], a[1]);
|
|
return combinedStatus(combinedStatus(a, l - 1), a[l - 1]);
|
|
}
|
|
|
|
private int intTime(long longTime) {
|
|
return (int) Math.max(0, ((longTime - startTime) / 1000));
|
|
}
|
|
|
|
private long longEmit(int intTime) {
|
|
return (((long) intTime) * (long) 1000) + startTime;
|
|
}
|
|
|
|
public void put(byte[] key, Object value) {
|
|
if (key != null) put(new String(key), value);
|
|
}
|
|
|
|
public void put(String key, Object value) {
|
|
if ((key == null) || (value == null)) return;
|
|
Object prev = null;
|
|
synchronized(cache) {
|
|
prev = cache.put(key, value);
|
|
ages.setScore(key, intTime(System.currentTimeMillis()));
|
|
if (hasnot.deleteScore(key) != 0) hasnotDelete++;
|
|
}
|
|
if (prev == null) this.writeUnique++; else this.writeDouble++;
|
|
flushc();
|
|
}
|
|
|
|
public Object get(byte[] key) {
|
|
return get(new String(key));
|
|
}
|
|
|
|
public Object get(String key) {
|
|
if (key == null) return null;
|
|
Object r = null;
|
|
synchronized(cache) {
|
|
r = cache.get(key);
|
|
if (r == null) {
|
|
this.readMiss++;
|
|
} else {
|
|
this.readHit++;
|
|
ages.setScore(key, intTime(System.currentTimeMillis())); // renew cache update time
|
|
}
|
|
}
|
|
flushc();
|
|
return r;
|
|
}
|
|
|
|
public void hasnot(byte[] key) {
|
|
hasnot(new String(key));
|
|
}
|
|
|
|
public void hasnot(String key) {
|
|
if (key == null) return;
|
|
int prev = 0;
|
|
synchronized(cache) {
|
|
if (cache.remove(key) != null) cacheDelete++;
|
|
ages.deleteScore(key);
|
|
prev = hasnot.getScore(key);
|
|
hasnot.setScore(key, intTime(System.currentTimeMillis()));
|
|
}
|
|
if (prev == 0) this.hasnotUnique++; else this.hasnotDouble++;
|
|
flushh();
|
|
}
|
|
|
|
public int has(byte[] key) {
|
|
return has(new String(key));
|
|
}
|
|
|
|
public int has(String key) {
|
|
// returns a 3-value boolean:
|
|
// 1 = key definitely exists
|
|
// -1 = key definitely does not exist
|
|
// 0 = unknown, if key exists
|
|
if (key == null) return 0;
|
|
synchronized(cache) {
|
|
if (hasnot.getScore(key) > 0) {
|
|
hasnot.setScore(key, intTime(System.currentTimeMillis())); // renew cache update time
|
|
this.hasnotHit++;
|
|
return -1;
|
|
}
|
|
this.hasnotMiss++;
|
|
if (cache.get(key) != null) return 1;
|
|
}
|
|
flushh();
|
|
return 0;
|
|
}
|
|
|
|
public void remove(byte[] key) {
|
|
remove(new String(key));
|
|
}
|
|
|
|
public void remove(String key) {
|
|
if (key == null) return;
|
|
synchronized(cache) {
|
|
if (cache.remove(key) != null) cacheDelete++;
|
|
ages.deleteScore(key);
|
|
hasnot.setScore(key, intTime(System.currentTimeMillis()));
|
|
}
|
|
flushh();
|
|
}
|
|
|
|
public void flushc() {
|
|
String k;
|
|
synchronized(cache) {
|
|
while ((ages.size() > 0) &&
|
|
((k = (String) ages.getMinObject()) != null) &&
|
|
((ages.size() > maxHitSize) ||
|
|
(((System.currentTimeMillis() - longEmit(ages.getScore(k))) > maxAge) &&
|
|
(serverMemory.available() < minMem)))
|
|
) {
|
|
cache.remove(k);
|
|
ages.deleteScore(k);
|
|
cacheFlush++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void flushh() {
|
|
String k;
|
|
synchronized(cache) {
|
|
while ((hasnot.size() > 0) &&
|
|
((k = (String) hasnot.getMinObject()) != null) &&
|
|
((hasnot.size() > maxMissSize) ||
|
|
(((System.currentTimeMillis() - longEmit(hasnot.getScore(k))) > maxAge) &&
|
|
(serverMemory.available() < minMem)))
|
|
) {
|
|
hasnot.deleteScore(k);
|
|
hasnotFlush++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
// test to measure memory usage of miss cache
|
|
kelondroMScoreCluster t = new kelondroMScoreCluster();
|
|
System.gc(); long s0 = Runtime.getRuntime().freeMemory();
|
|
int loop = 200000;
|
|
for (int i = 0; i < loop; i++) t.setScore((Integer.toString(i) + "000000000000").substring(0, 12), i);
|
|
System.gc(); long s1 = Runtime.getRuntime().freeMemory();
|
|
System.out.println((s1 - s0) / loop);
|
|
}
|
|
|
|
}
|