Author: toad
Date: 2006-05-12 18:31:58 +0000 (Fri, 12 May 2006)
New Revision: 8683

Added:
   trunk/freenet/src/freenet/store/KeyCollisionException.java
Modified:
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/RequestSender.java
   trunk/freenet/src/freenet/node/SSKInsertHandler.java
   trunk/freenet/src/freenet/node/Version.java
   trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
   trunk/freenet/src/freenet/store/FreenetStore.java
Log:
703: Major bugfix to datastore.
Bug will have lead to occasionally losing slots in the datastore, potentially 
wasting large amounts of space.
RECOMMEND DATASTORE RESET, but please try to do so over a period of days, not 
all at once.

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2006-05-12 17:42:18 UTC (rev 
8682)
+++ trunk/freenet/src/freenet/node/Node.java    2006-05-12 18:31:58 UTC (rev 
8683)
@@ -89,6 +89,7 @@
 import freenet.pluginmanager.PluginManager;
 import freenet.store.BerkeleyDBFreenetStore;
 import freenet.store.FreenetStore;
+import freenet.store.KeyCollisionException;
 import freenet.support.Base64;
 import freenet.support.Bucket;
 import freenet.support.BucketFactory;
@@ -1618,10 +1619,12 @@
         synchronized(this) {
                if(cache) {
                        try {
-                               sskDatastore.put(block);
+                               sskDatastore.put(block, false);
                        } catch (IOException e) {
                                Logger.error(this, "Datastore failure: "+e, e);
-                       }
+                       } catch (KeyCollisionException e) {
+                               throw new 
LowLevelPutException(LowLevelPutException.COLLISION);
+                               }
                }
             is = makeInsertSender(block, 
                     MAX_HTL, uid, null, false, lm.getLocation().getValue(), 
cache);
@@ -1674,7 +1677,15 @@

         if(is.hasCollided()) {
                // Store it locally so it can be fetched immediately, and 
overwrites any locally inserted.
-               store(is.getBlock());
+               try {
+                       synchronized(this) {
+                               sskDatastore.put(is.getBlock(), true);
+                       }
+                       } catch (KeyCollisionException e) {
+                               // Impossible
+                       } catch (IOException e) {
+                       Logger.error(this, "Datastore failure: "+e, e);
+                       }
                throw new LowLevelPutException(LowLevelPutException.COLLISION);
         }

@@ -2034,9 +2045,9 @@
         }
     }

-    public synchronized void store(SSKBlock block) {
+    public synchronized void store(SSKBlock block) throws 
KeyCollisionException {
        try {
-               sskDatastore.put(block);
+               sskDatastore.put(block, false);
                cacheKey(((NodeSSK)block.getKey()).getPubKeyHash(), 
((NodeSSK)block.getKey()).getPubKey());
        } catch (IOException e) {
                Logger.error(this, "Cannot store data: "+e, e);

Modified: trunk/freenet/src/freenet/node/RequestSender.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestSender.java   2006-05-12 17:42:18 UTC 
(rev 8682)
+++ trunk/freenet/src/freenet/node/RequestSender.java   2006-05-12 18:31:58 UTC 
(rev 8683)
@@ -18,6 +18,7 @@
 import freenet.keys.NodeSSK;
 import freenet.keys.SSKBlock;
 import freenet.keys.SSKVerifyException;
+import freenet.store.KeyCollisionException;
 import freenet.support.Logger;
 import freenet.support.ShortBuffer;

@@ -398,6 +399,8 @@
                        Logger.error(this, "Failed to verify: "+e+" from 
"+next, e);
                        finish(VERIFY_FAILURE, next);
                        return;
+               } catch (KeyCollisionException e) {
+                       Logger.normal(this, "Collision on "+this);
                }
        }

@@ -427,7 +430,11 @@
                node.store(block);
        } else if (key instanceof NodeSSK) {
                SSKBlock block = new SSKBlock(data, headers, (NodeSSK)key, 
false);
-               node.store(block);
+               try {
+                               node.store(block);
+                       } catch (KeyCollisionException e) {
+                               Logger.normal(this, "Collision on "+this);
+                       }
        }
        }


Modified: trunk/freenet/src/freenet/node/SSKInsertHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/SSKInsertHandler.java        2006-05-12 
17:42:18 UTC (rev 8682)
+++ trunk/freenet/src/freenet/node/SSKInsertHandler.java        2006-05-12 
18:31:58 UTC (rev 8683)
@@ -12,6 +12,7 @@
 import freenet.keys.NodeSSK;
 import freenet.keys.SSKBlock;
 import freenet.keys.SSKVerifyException;
+import freenet.store.KeyCollisionException;
 import freenet.support.Logger;
 import freenet.support.ShortBuffer;

@@ -291,7 +292,11 @@
        Logger.minor(this, "Finishing");

        if(canCommit) {
-               node.store(block);
+               try {
+                               node.store(block);
+                       } catch (KeyCollisionException e) {
+                               Logger.normal(this, "Collision on "+this);
+                       }
        }
     }


Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-05-12 17:42:18 UTC (rev 
8682)
+++ trunk/freenet/src/freenet/node/Version.java 2006-05-12 18:31:58 UTC (rev 
8683)
@@ -18,7 +18,7 @@
        public static final String protocolVersion = "1.0";

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

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

Modified: trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2006-05-12 
17:42:18 UTC (rev 8682)
+++ trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2006-05-12 
18:31:58 UTC (rev 8683)
@@ -411,10 +411,84 @@
 //     return null;
     }

-    /**
+    public void put(CHKBlock b) throws IOException {
+               NodeCHK chk = (NodeCHK) b.getKey();
+               CHKBlock oldBlock = fetch(chk, false);
+               if(oldBlock != null)
+                       return;
+               innerPut(b);
+    }
+    
+    public void put(SSKBlock b, boolean overwrite) throws IOException, 
KeyCollisionException {
+               NodeSSK ssk = (NodeSSK) b.getKey();
+               SSKBlock oldBlock = fetch(ssk, false);
+               if(oldBlock != null) {
+                       if(!b.equals(oldBlock)) {
+                               if(!overwrite)
+                                       throw new KeyCollisionException();
+                               else {
+                                       if(overwrite(b)) {
+                                               fetch(ssk, false); // promote it
+                                               return;
+                                       }
+                               }
+                       }
+                       return;
+               }
+               innerPut(b);
+    }
+    
+    private boolean overwrite(SSKBlock b) throws IOException {
+       NodeSSK chk = (NodeSSK) b.getKey();
+       byte[] routingkey = chk.getRoutingKey();
+       DatabaseEntry routingkeyDBE = new DatabaseEntry(routingkey);
+       DatabaseEntry blockDBE = new DatabaseEntry();
+       Cursor c = null;
+       Transaction t = null;
+       try{
+               t = environment.beginTransaction(null,null);
+               c = chkDB.openCursor(t,null);
+               
+               if(c.getSearchKey(routingkeyDBE,blockDBE,LockMode.RMW)
+                               !=OperationStatus.SUCCESS) {
+                       c.close();
+                       t.abort();
+                       return false;
+               }
+
+               StoreBlock storeBlock = (StoreBlock) 
storeBlockTupleBinding.entryToObject(blockDBE);
+                               
+               byte[] header = b.getRawHeaders();
+               byte[] data = b.getRawData();
+               synchronized(chkStore) {
+                       
chkStore.seek(storeBlock.offset*(long)(dataBlockSize+headerBlockSize));
+                       
+                       chkStore.write(header);
+                       chkStore.write(data);
+               }
+                       
+           } catch(Throwable ex) {  // FIXME: ugly  
+               if(c!=null) {
+                       try{c.close();}catch(DatabaseException ex2){}
+               
+               }
+               if(t!=null) {
+                       try{t.abort();}catch(DatabaseException ex2){}
+               }
+       
+               checkSecondaryDatabaseError(ex);
+               Logger.error(this, "Caught "+ex, ex);
+               ex.printStackTrace();
+               throw new IOException(ex.getMessage());
+        }
+               
+       return true;
+       }
+
+       /**
      * Store a block.
      */
-    public void put(KeyBlock block) throws IOException
+    void innerPut(KeyBlock block) throws IOException
     {          
        if(closed)
                return;

Modified: trunk/freenet/src/freenet/store/FreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/FreenetStore.java   2006-05-12 17:42:18 UTC 
(rev 8682)
+++ trunk/freenet/src/freenet/store/FreenetStore.java   2006-05-12 18:31:58 UTC 
(rev 8683)
@@ -35,8 +35,15 @@

     /**
      * Store a block.
+     * @throws KeyCollisionException If the key already exists but has 
different contents.
+     * @param ignoreAndOverwrite If true, overwrite old content rather than 
throwing a KeyCollisionException. 
      */
-    public void put(KeyBlock block) throws IOException;
+    public void put(SSKBlock block, boolean ignoreAndOverwrite) throws 
IOException, KeyCollisionException;
+
+    /**
+     * Store a block.
+     */
+    public void put(CHKBlock block) throws IOException;

     /**
      * Store a public key.

Added: trunk/freenet/src/freenet/store/KeyCollisionException.java
===================================================================
--- trunk/freenet/src/freenet/store/KeyCollisionException.java  2006-05-12 
17:42:18 UTC (rev 8682)
+++ trunk/freenet/src/freenet/store/KeyCollisionException.java  2006-05-12 
18:31:58 UTC (rev 8683)
@@ -0,0 +1,5 @@
+package freenet.store;
+
+public class KeyCollisionException extends Exception {
+
+}


Reply via email to