Author: toad
Date: 2006-07-25 23:46:05 +0000 (Tue, 25 Jul 2006)
New Revision: 9765

Modified:
   trunk/freenet/src/freenet/node/Version.java
   trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
Log:
901: Offline shrink of the datastore now preserves the most recently used 
content. Online store shrinking no longer supported unless only 10% or less. 
Online shrink does not preserve the most recently used content.

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-07-25 21:11:35 UTC (rev 
9764)
+++ trunk/freenet/src/freenet/node/Version.java 2006-07-25 23:46:05 UTC (rev 
9765)
@@ -18,7 +18,7 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       private static final int buildNumber = 900;
+       private static final int buildNumber = 901;

        /** Oldest build of Fred we will talk to */
        private static final int oldLastGoodBuild = 870;

Modified: trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2006-07-25 
21:11:35 UTC (rev 9764)
+++ trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2006-07-25 
23:46:05 UTC (rev 9765)
@@ -7,6 +7,7 @@
 import java.io.RandomAccessFile;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Vector;

 import com.sleepycat.bind.tuple.TupleBinding;
 import com.sleepycat.bind.tuple.TupleInput;
@@ -66,8 +67,8 @@
        private final Object chkBlocksInStoreLock = new Object();
        private long maxChkBlocks;
        private final Database chkDB;
-       private final Database chkDB_accessTime;
-       private final Database chkDB_blockNum;
+       private final SecondaryDatabase chkDB_accessTime;
+       private final SecondaryDatabase chkDB_blockNum;
        private final RandomAccessFile chkStore;
        private final SortedLongSet freeBlocks;

@@ -204,7 +205,7 @@
                Logger.minor(this, "Keys in store: "+chkBlocksInStore);
                System.out.println("Keys in store: "+chkBlocksInStore+" / 
"+maxChkBlocks);

-               maybeShrink(true);
+               maybeShrink(true, true);

 //              Add shutdownhook
                Runtime.getRuntime().addShutdownHook(new ShutdownHook());
@@ -232,7 +233,147 @@
                System.err.println("Checked database, found "+holes+" holes");
        }

-       private void maybeShrink(boolean dontCheck) throws DatabaseException, 
IOException {
+       private void maybeShrink(boolean dontCheck, boolean offline) throws 
DatabaseException, IOException {
+               if(chkBlocksInStore <= maxChkBlocks) return;
+               if(offline)
+                       maybeSlowShrink(dontCheck);
+               else {
+                       if(chkBlocksInStore * 0.9 > maxChkBlocks) {
+                               Logger.error(this, "Doing quick and 
indiscriminate online shrink. Offline shrinks will preserve the LRU, this 
doesn't.");
+                               maybeQuickShrink(dontCheck);
+                       } else {
+                               Logger.error(this, "Online shrink only 
supported for small deltas because online shrink does not preserve LRU order. 
Suggest you restart the node.");
+                       }
+               }
+       }
+       
+       private void maybeSlowShrink(boolean dontCheck) throws 
DatabaseException, IOException {
+               Vector wantedKeep = new Vector(); // keep; content is wanted, 
and is in the right place
+               Vector unwantedIgnore = new Vector(); // ignore; content is not 
wanted, and is not in the right place
+               Vector wantedMove = new Vector(); // content is wanted, but is 
in the wrong part of the store
+               Vector unwantedMove = new Vector(); // content is not wanted, 
but is in the wrong part of the store
+               
+       Cursor c = null;
+       Transaction t = null;
+
+       long newSize = maxChkBlocks;
+       if(chkBlocksInStore < maxChkBlocks) return;
+       System.err.println("Shrinking from "+chkBlocksInStore+" to 
"+maxChkBlocks);
+       
+       try {
+                       t = environment.beginTransaction(null,null);
+                       c = chkDB_accessTime.openCursor(t,null);
+                       
+                       DatabaseEntry keyDBE = new DatabaseEntry();
+                       DatabaseEntry blockDBE = new DatabaseEntry();
+                       OperationStatus opStat;
+                       opStat = c.getLast(keyDBE, blockDBE, LockMode.RMW);
+                       
+                       if(opStat == OperationStatus.NOTFOUND) {
+                               System.err.println("Database is empty.");
+                               c.close();
+                               c = null;
+                               t.abort();
+                               t = null;
+                               return;
+                       }
+
+                       //Logger.minor(this, "Found first key");
+                       int x = 0;
+                       while(true) {
+                       StoreBlock storeBlock = (StoreBlock) 
storeBlockTupleBinding.entryToObject(blockDBE);
+                               //Logger.minor(this, "Found another key 
("+(x++)+") ("+storeBlock.offset+")");
+                               long block = storeBlock.offset;
+                               Long blockNum = new Long(storeBlock.offset);
+                               Long seqNum = new Long(storeBlock.recentlyUsed);
+                               //System.out.println("#"+x+" seq "+seqNum+": 
block "+blockNum);
+                               if(x < newSize) {
+                                       // Wanted
+                                       if(block < newSize) {
+                                               //System.out.println("Keep 
where it is: block "+blockNum+" seq # "+x+" / "+newSize);
+                                               wantedKeep.add(blockNum);
+                                       } else {
+                                               //System.out.println("Move to 
where it should go: "+blockNum+" seq # "+x+" / "+newSize);
+                                               wantedMove.add(blockNum);
+                                       }
+                               } else {
+                                       // Unwanted
+                                       if(block < newSize) {
+                                               
//System.out.println("Overwrite: "+blockNum+" seq # "+x+" / "+newSize);
+                                               unwantedMove.add(blockNum);
+                                       } else {
+                                               //System.out.println("Ignore, 
will be wiped: block "+blockNum+" seq # "+x+" / "+newSize);
+                                               unwantedIgnore.add(blockNum);
+                                       }
+                               }
+                               
+                               opStat = c.getPrev(keyDBE, blockDBE, 
LockMode.RMW);
+                               if(opStat == OperationStatus.NOTFOUND) {
+                                       break;
+                               }
+                               x++;
+                       }
+                       
+       } finally {
+               if(c != null)
+                       c.close();
+               if(t != null)
+                       t.abort();
+       }
+       
+       System.err.println("Keys to keep where they are:     
"+wantedKeep.size());
+       System.err.println("Keys which will be wiped anyway: 
"+unwantedIgnore.size());
+       System.err.println("Keys to move:                    
"+wantedMove.size());
+       System.err.println("Keys to be moved over:           
"+unwantedMove.size());
+       
+       // Now move all the wantedMove blocks onto the corresponding 
unwantedMove's.
+       
+       byte[] buf = new byte[headerBlockSize + dataBlockSize];
+       t = null;
+       try {
+       t = environment.beginTransaction(null,null);
+       for(int i=0;i<wantedMove.size();i++) {
+               Long wantedBlock = (Long)wantedMove.get(i);
+               Long unwantedBlock = (Long)unwantedMove.get(i);
+               // Delete unwantedBlock from the store
+               DatabaseEntry wantedBlockEntry = new DatabaseEntry();
+               longTupleBinding.objectToEntry(wantedBlock, wantedBlockEntry);
+               DatabaseEntry unwantedBlockEntry = new DatabaseEntry();
+               longTupleBinding.objectToEntry(unwantedBlock, 
unwantedBlockEntry);
+               // Delete the old block from the database.
+               chkDB_blockNum.delete(t, unwantedBlockEntry);
+               long seekTo = wantedBlock.longValue() * (headerBlockSize + 
dataBlockSize);
+               chkStore.seek(seekTo);
+               chkStore.readFully(buf);
+               seekTo = unwantedBlock.longValue() * (headerBlockSize + 
dataBlockSize);
+               chkStore.seek(seekTo);
+               chkStore.write(buf);
+               DatabaseEntry routingKeyDBE = new DatabaseEntry();
+               DatabaseEntry blockDBE = new DatabaseEntry();
+               chkDB_blockNum.get(t, wantedBlockEntry, routingKeyDBE, 
blockDBE, LockMode.RMW);
+               StoreBlock block = (StoreBlock) 
storeBlockTupleBinding.entryToObject(blockDBE);
+               block.offset = unwantedBlock.longValue();
+               storeBlockTupleBinding.objectToEntry(block, blockDBE);
+               chkDB.put(t, routingKeyDBE, blockDBE);
+               if((i+1) % 2048 == 0) {
+                       t.commit();
+                       t = environment.beginTransaction(null,null);
+               }
+               System.err.println("Moved "+wantedBlock+" to "+unwantedBlock);
+       }
+       if(t != null) {
+               t.commit();
+               t = null;
+       }
+       } finally {
+               if(t != null)
+                       t.abort();
+       }
+       maybeQuickShrink(false);
+       
+       }
+       
+       private void maybeQuickShrink(boolean dontCheck) throws 
DatabaseException, IOException {
                Transaction t = null;
                try {
                        // long's are not atomic.
@@ -408,7 +549,7 @@
                chkBlocksInStore = countCHKBlocksFromFile();
                lastRecentlyUsed = getMaxRecentlyUsed();

-               maybeShrink(true);
+               maybeShrink(true, true);

 //              Add shutdownhook
                Runtime.getRuntime().addShutdownHook(new ShutdownHook());
@@ -1331,6 +1472,6 @@
                synchronized(this) {
                        maxChkBlocks = maxStoreKeys;
                }
-               maybeShrink(false);
+               maybeShrink(false, false);
        }
 }


Reply via email to