Author: j16sdiz
Date: 2008-05-04 13:13:37 +0000 (Sun, 04 May 2008)
New Revision: 19738
Modified:
branches/saltedhashstore/freenet/src/freenet/store/SaltedHashFreenetStore.java
Log:
cleaner / estimate key count
Modified:
branches/saltedhashstore/freenet/src/freenet/store/SaltedHashFreenetStore.java
===================================================================
---
branches/saltedhashstore/freenet/src/freenet/store/SaltedHashFreenetStore.java
2008-05-04 13:13:16 UTC (rev 19737)
+++
branches/saltedhashstore/freenet/src/freenet/store/SaltedHashFreenetStore.java
2008-05-04 13:13:37 UTC (rev 19738)
@@ -23,6 +23,8 @@
import freenet.support.HexUtil;
import freenet.support.Logger;
import freenet.support.io.FileUtil;
+import freenet.support.math.RunningAverage;
+import freenet.support.math.SimpleRunningAverage;
/**
* Index-less data store based on salted hash
@@ -34,6 +36,7 @@
private static boolean logDEBUG;
private final File baseDir;
+ private final String name;
private final StoreCallback callback;
private final boolean collisionPossible;
private final int headerBlockLength;
@@ -48,12 +51,13 @@
return new SaltedHashFreenetStore(baseDir, name, callback,
random, maxKeys, shutdownHook);
}
- SaltedHashFreenetStore(File baseDir, String name, StoreCallback
callback, Random random, long maxKeys,
+ private SaltedHashFreenetStore(File baseDir, String name, StoreCallback
callback, Random random, long maxKeys,
SemiOrderedShutdownHook shutdownHook) throws IOException {
logMINOR = Logger.shouldLog(Logger.MINOR, this);
logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
this.baseDir = baseDir;
+ this.name = name;
this.callback = callback;
collisionPossible = callback.collisionPossible();
@@ -79,6 +83,9 @@
callback.setStore(this);
shutdownHook.addEarlyJob(new Thread(new ShutdownDB()));
+
+ cleanerThread = new Cleaner();
+ cleanerThread.start();
}
public StorableBlock fetch(byte[] routingKey, byte[] fullKey, boolean
dontPromote) throws IOException {
@@ -546,9 +553,9 @@
* +----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |0000| Salt |
* +----+---------------+---------------+
- * |0010| Store Size | prevStoreSize1|
+ * |0010| Store Size | prevStoreSize |
* +----+---------------+---------------+
- * |0010| prevStoreSize2| prevStoreSize3|
+ * |0010| Est Key Count | reserved |
* +----+---------------+---------------+
* </pre>
*/
@@ -572,11 +579,10 @@
salt = new byte[0x10];
raf.read(salt);
- // TODO store resize
storeSize = raf.readLong();
+ prevStoreSize = raf.readLong();
+ estimatedCount = new SimpleRunningAverage(3,
raf.readLong());
raf.readLong();
- raf.readLong();
- raf.readLong();
raf.close();
}
@@ -591,12 +597,11 @@
RandomAccessFile raf = new RandomAccessFile(tempConfig, "rw");
raf.seek(0);
raf.write(salt);
+
raf.writeLong(storeSize);
-
- // TODO store resize
+ raf.writeLong(prevStoreSize);
+ raf.writeLong(estimatedCount == null ? 0 : (long)
estimatedCount.currentValue());
raf.writeLong(0);
- raf.writeLong(0);
- raf.writeLong(0);
raf.close();
@@ -604,6 +609,87 @@
}
// ------------- Store resizing
+ private RunningAverage estimatedCount;
+ private long prevStoreSize = 0;
+ private Object cleanerLock = new Object();
+ private Cleaner cleanerThread;
+
+ private class Cleaner extends Thread {
+ public Cleaner() {
+ setName("Store-" + name + "-Cleaner");
+ setPriority(MIN_PRIORITY);
+ setDaemon(true);
+ }
+
+ public void run() {
+ while (!shutdown) {
+ if (prevStoreSize != 0)
+ moveOldEntries();
+ else
+ estimateStoreSize();
+
+ synchronized (cleanerLock) {
+ try {
+ cleanerLock.wait(10 * 60 *
1000); // 10 minutes
+ } catch (InterruptedException e) {
+ Logger.debug(this,
"interrupted", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Move old entries to new location
+ */
+ private void moveOldEntries() {
+ Logger.minor(this, "move old entries");
+ prevStoreSize = 0;
+ }
+
+ /**
+ * Sample to take at a time
+ */
+ private static final double SAMPLE_RATE = 0.05; // 5%
+
+ /**
+ * Last sample position
+ */
+ private long samplePos = 0;
+
+ /**
+ * Estimate store utilization
+ */
+ private void estimateStoreSize() {
+ Logger.minor(this, "start estimating key count");
+ long numSample = (long) (SAMPLE_RATE * storeSize);
+ long sampled = 0;
+ long occupied = 0;
+ while (sampled < numSample) {
+ try {
+ if (!isFree(samplePos)) {
+ occupied++;
+ }
+ sampled++;
+ } catch (IOException e) { // oh, why?
+ Logger.error(this, "estimating store
size", e);
+ }
+
+ samplePos = (samplePos + 1) % storeSize;
+
+ if (prevStoreSize != 0 || shutdown)
+ return; // oops! time to stop here
+ }
+
+ double newEstimatedCount = ((double) occupied *
storeSize) / sampled;
+ estimatedCount.report(newEstimatedCount);
+
+ if (logMINOR)
+ Logger.minor(this, "finish estimating key
count: sampled=" + sampled + ", occupied=" + occupied
+ + ", newEstimatedCount=" +
newEstimatedCount + ", runningAverage="
+ + estimatedCount.currentValue());
+ }
+ }
+
public void setMaxKeys(long maxStoreKeys, boolean shrinkNow) throws
IOException {
// TODO
// NO-OP now
@@ -704,7 +790,15 @@
public class ShutdownDB implements Runnable {
public void run() {
shutdown = true;
+
+ synchronized (cleanerLock) {
+ cleanerLock.notifyAll();
+ }
+
lockGlobal(10 * 1000); // 10 seconds
+
+ cleanerThread.interrupt();
+
flushAndClose();
try {
@@ -803,7 +897,7 @@
}
public long keyCount() {
- return 0;
+ return (long) estimatedCount.currentValue();
}
public long getMaxKeys() {