Author: toad
Date: 2006-06-07 14:25:22 +0000 (Wed, 07 Jun 2006)
New Revision: 9070
Modified:
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/node/PacketSender.java
trunk/freenet/src/freenet/node/Version.java
trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
trunk/freenet/src/freenet/support/DoublyLinkedListImpl.java
Log:
783:
Datastore fixes:
- Check whether a pubkey exists in the pubkey store before adding it. This was
causing dupes in the pubkey store.
- Add a unique index on block number to the datastore. Migrate automatically on
startup, deleting any dupes.
- Get the next block number more accurately; again, avoid making dupes on the
same block number.
Don't start the watchdog until we start the PacketSender thread it watches;
long startup will no longer cause a restart.
Fix an exception in DLList.remove() that was happening when fetching stuff.
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2006-06-07 11:42:20 UTC (rev
9069)
+++ trunk/freenet/src/freenet/node/Node.java 2006-06-07 14:25:22 UTC (rev
9070)
@@ -598,8 +598,12 @@
// USK inserter.
private final MyARKInserter arkPutter;
+ // The node starter
private static NodeStarter nodeStarter;
+ // Debugging stuff
+ private static final boolean USE_RAM_PUBKEYS_CACHE = true;
+
/**
* Read all storable settings (identity etc) from the node file.
* @param filename The name of the file to read from.
@@ -854,9 +858,8 @@
}
}
- Node(Config config, RandomSource random, LoggingConfigHandler lc,
NodeStarter ns) throws NodeInitException{
- this(config, random, lc);
- nodeStarter=ns;
+ Node(Config config, RandomSource random, LoggingConfigHandler lc) throws
NodeInitException{
+ this(config, random, lc, null);
}
public boolean isUsingWrapper(){
@@ -879,11 +882,11 @@
* @param the loggingHandler
* @throws NodeInitException If the node initialization fails.
*/
- Node(Config config, RandomSource random, LoggingConfigHandler lc) throws
NodeInitException {
+ Node(Config config, RandomSource random, LoggingConfigHandler lc,
NodeStarter ns) throws NodeInitException {
// Easy stuff
- nodeStarter=null;
+ nodeStarter=ns;
if(logConfigHandler != lc)
- this.logConfigHandler=lc;
+ logConfigHandler=lc;
arkPutter = new MyARKInserter();
startupTime = System.currentTimeMillis();
throttleWindow = new ThrottleWindowManager(2.0);
@@ -2624,12 +2627,14 @@
public DSAPublicKey getKey(byte[] hash) {
ImmutableByteArrayWrapper w = new
ImmutableByteArrayWrapper(hash);
Logger.minor(this, "Getting pubkey: "+HexUtil.bytesToHex(hash));
- synchronized(cachedPubKeys) {
- DSAPublicKey key = (DSAPublicKey) cachedPubKeys.get(w);
- if(key != null) {
- cachedPubKeys.push(w, key);
- Logger.minor(this, "Got
"+HexUtil.bytesToHex(hash)+" from cache");
- return key;
+ if(USE_RAM_PUBKEYS_CACHE) {
+ synchronized(cachedPubKeys) {
+ DSAPublicKey key = (DSAPublicKey)
cachedPubKeys.get(w);
+ if(key != null) {
+ cachedPubKeys.push(w, key);
+ Logger.minor(this, "Got
"+HexUtil.bytesToHex(hash)+" from cache");
+ return key;
+ }
}
}
try {
@@ -2650,6 +2655,7 @@
* Cache a public key
*/
public void cacheKey(byte[] hash, DSAPublicKey key) {
+ Logger.minor(this, "Cache key: "+HexUtil.bytesToHex(hash)+" :
"+key);
ImmutableByteArrayWrapper w = new
ImmutableByteArrayWrapper(hash);
synchronized(cachedPubKeys) {
DSAPublicKey key2 = (DSAPublicKey) cachedPubKeys.get(w);
@@ -2684,6 +2690,7 @@
}
try {
pubKeyDatastore.put(hash, key);
+ pubKeyDatastore.fetchPubKey(hash, true);
} catch (IOException e) {
// FIXME deal with disk full, access perms etc; tell
user about it.
Logger.error(this, "Error accessing pubkey store: "+e,
e);
Modified: trunk/freenet/src/freenet/node/PacketSender.java
===================================================================
--- trunk/freenet/src/freenet/node/PacketSender.java 2006-06-07 11:42:20 UTC
(rev 9069)
+++ trunk/freenet/src/freenet/node/PacketSender.java 2006-06-07 14:25:22 UTC
(rev 9070)
@@ -39,10 +39,6 @@
myThread = new Thread(this, "PacketSender thread for
"+node.portNumber);
myThread.setDaemon(true);
lastTimeInSeconds = (int) (System.currentTimeMillis() / 1000);
- // Necessary because of sun JVM bugs when NPTL is enabled. Write once,
debug everywhere!
- Thread t1 = new Thread(new Watchdog(), "PacketSender watchdog");
- t1.setDaemon(true);
- t1.start();
}
/**
@@ -76,6 +72,9 @@
WrapperManager.requestThreadDump();
WrapperManager.restart();
}else{
+
if(!Node.logConfigHandler.getFileLoggerHook().hasRedirectedStdOutErrNoLock())
+
System.err.println("Exiting on deadlock, but not running in the wrapper! Please
restart the node manually.");
+
// No wrapper : we
don't want to let it harm the network!
node.exit();
}
@@ -95,6 +94,10 @@
void start() {
Logger.normal(this,"Starting the PacketSender thread");
+ // Necessary because of sun JVM bugs when NPTL is enabled. Write once,
debug everywhere!
+ Thread t1 = new Thread(new Watchdog(), "PacketSender watchdog");
+ t1.setDaemon(true);
+ t1.start();
myThread.start();
}
Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-06-07 11:42:20 UTC (rev
9069)
+++ trunk/freenet/src/freenet/node/Version.java 2006-06-07 14:25:22 UTC (rev
9070)
@@ -18,7 +18,7 @@
public static final String protocolVersion = "1.0";
/** The build number of the current revision */
- private static final int buildNumber = 782;
+ private static final int buildNumber = 783;
/** Oldest build of Fred we will talk to */
private static final int lastGoodBuild = 765;
Modified: trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2006-06-07
11:42:20 UTC (rev 9069)
+++ trunk/freenet/src/freenet/store/BerkeleyDBFreenetStore.java 2006-06-07
14:25:22 UTC (rev 9070)
@@ -5,16 +5,17 @@
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
+import java.util.HashSet;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
-import com.sleepycat.je.BtreeStats;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.DatabaseNotFoundException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
@@ -58,6 +59,7 @@
private long maxChkBlocks;
private final Database chkDB;
private final Database chkDB_accessTime;
+ private final Database chkDB_blockNum;
private final RandomAccessFile chkStore;
private long lastRecentlyUsed;
@@ -129,6 +131,38 @@
chkDB_accessTime = environment.openSecondaryDatabase
(null,
"CHK_accessTime", chkDB, secDbConfig);
+ // Initialize other secondary database sorted on block number
+// try {
+// environment.removeDatabase(null, "CHK_blockNum");
+// } catch (DatabaseNotFoundException e) { };
+ SecondaryConfig blockNoDbConfig = new SecondaryConfig();
+ blockNoDbConfig.setAllowCreate(false);
+ blockNoDbConfig.setSortedDuplicates(false);
+ blockNoDbConfig.setAllowPopulate(true);
+ blockNoDbConfig.setTransactional(true);
+
+ BlockNumberKeyCreator bnkc =
+ new BlockNumberKeyCreator(storeBlockTupleBinding);
+ blockNoDbConfig.setKeyCreator(bnkc);
+ SecondaryDatabase blockNums;
+ try {
+ System.err.println("Opening block db index");
+ blockNums = environment.openSecondaryDatabase
+ (null, "CHK_blockNum", chkDB, blockNoDbConfig);
+ } catch (DatabaseNotFoundException e) {
+ System.err.println("Migrating block db index");
+ // De-dupe on keys and block numbers.
+ migrate(storeDir);
+ System.err.println("De-duped, creating new index...");
+ blockNoDbConfig.setSortedDuplicates(false);
+ blockNoDbConfig.setAllowCreate(true);
+ blockNoDbConfig.setAllowPopulate(true);
+ blockNums = environment.openSecondaryDatabase
+ (null, "CHK_blockNum", chkDB, blockNoDbConfig);
+ }
+
+ chkDB_blockNum = blockNums;
+
// Initialize the store file
File storeFile = new File(dir,"store");
if(!storeFile.exists())
@@ -143,6 +177,60 @@
}
/**
+ * Migrate from a store which didn't have a unique index on blockNum,
to one which does.
+ * How do we do this? We scan through all entries (slow), we fetch each
key, delete all data's
+ * under it, and then insert the one we are looking at.
+ *
+ * FIXME: Create a list of reusable block numbers?
+ */
+ private void migrate(String storeDir) throws DatabaseException {
+
+ System.err.println("Migrating database "+storeDir+": Creating
unique index on block number");
+ HashSet s = new HashSet();
+
+ Cursor c = null;
+ Transaction t = null;
+ t = environment.beginTransaction(null,null);
+ c = chkDB.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.");
+ return;
+ }
+ Logger.minor(this, "Found first key");
+ try {
+ int x = 0;
+ while(true) {
+ StoreBlock storeBlock = (StoreBlock)
storeBlockTupleBinding.entryToObject(blockDBE);
+ Logger.minor(this, "Found another key
("+(x++)+") ("+storeBlock.offset+")");
+ Long l = new Long(storeBlock.offset);
+ if(s.contains(l)) {
+ Logger.minor(this, "Deleting (block
number conflict).");
+ chkDB.delete(t, keyDBE);
+ }
+ s.add(l);
+ opStat = c.getPrev(keyDBE, blockDBE,
LockMode.RMW);
+ if(opStat == OperationStatus.NOTFOUND) {
+ return;
+ }
+ }
+ } catch (DatabaseException e) {
+ System.err.println("Caught: "+e);
+ e.printStackTrace();
+ Logger.error(this, "Caught "+e, e);
+ t.abort();
+ t = null;
+ } finally {
+ c.close();
+ if(t != null)
+ t.commit();
+ }
+ }
+
+ /**
* Retrieve a block.
* @param dontPromote If true, don't promote data if fetched.
* @return null if there is no such block stored, otherwise the block.
@@ -218,7 +306,7 @@
Logger.minor(this, "Data: "+data.length+" bytes, hash
"+data);
}catch(CHKVerifyException ex){
- Logger.normal(this, "CHKBlock: Does not verify
("+ex+"), setting accessTime to 0 for : "+chk);
+ Logger.error(this, "CHKBlock: Does not verify ("+ex+"),
setting accessTime to 0 for : "+chk);
storeBlock.setRecentlyUsedToZero();
DatabaseEntry updateDBE = new DatabaseEntry();
storeBlockTupleBinding.objectToEntry(storeBlock,
updateDBE);
@@ -302,7 +390,7 @@
Logger.minor(this, "Data: "+data.length+" bytes, hash
"+data);
}catch(SSKVerifyException ex){
- Logger.normal(this, "SSHBlock: Does not verify
("+ex+"), setting accessTime to 0 for : "+chk, ex);
+ Logger.normal(this, "SSKBlock: Does not verify
("+ex+"), setting accessTime to 0 for : "+chk, ex);
storeBlock.setRecentlyUsedToZero();
DatabaseEntry updateDBE = new DatabaseEntry();
storeBlockTupleBinding.objectToEntry(storeBlock,
updateDBE);
@@ -377,7 +465,7 @@
}
if(!Arrays.equals(block.asBytesHash(), hash)) {
- Logger.normal(this, "DSAPublicKey: Does not
verify (unequal hashes), setting accessTime to 0 for :
"+HexUtil.bytesToHex(hash));
+ Logger.error(this, "DSAPublicKey: Does not
verify (unequal hashes), setting accessTime to 0 for :
"+HexUtil.bytesToHex(hash));
storeBlock.setRecentlyUsedToZero();
DatabaseEntry updateDBE = new DatabaseEntry();
storeBlockTupleBinding.objectToEntry(storeBlock, updateDBE);
@@ -592,10 +680,19 @@
}
}
+ /**
+ * Store a pubkey.
+ */
+ public void put(byte[] hash, DSAPublicKey key) throws IOException {
+ DSAPublicKey k = fetchPubKey(hash, true);
+ if(k == null)
+ innerPut(hash, key);
+ }
+
/**
* Store a block.
*/
- public void put(byte[] hash, DSAPublicKey key) throws IOException
+ public void innerPut(byte[] hash, DSAPublicKey key) throws IOException
{
if(closed)
return;
@@ -603,6 +700,11 @@
byte[] routingkey = hash;
byte[] data = key.asPaddedBytes();
+ if(!(Arrays.equals(hash, key.asBytesHash()))) {
+ Logger.error(this, "Invalid hash!: "+HexUtil.bytesToHex(hash)+"
: "+key.asBytesHash());
+ }
+
+
if(data.length!=dataBlockSize) {
Logger.error(this, "This data is "+data.length+" bytes. Should
be "+dataBlockSize);
return;
@@ -741,6 +843,26 @@
}
}
+ private class BlockNumberKeyCreator implements SecondaryKeyCreator {
+ private TupleBinding theBinding;
+
+ public BlockNumberKeyCreator(TupleBinding theBinding1) {
+ theBinding = theBinding1;
+ }
+
+ public boolean createSecondaryKey(SecondaryDatabase secDb,
+ DatabaseEntry keyEntry,
+ DatabaseEntry dataEntry,
+ DatabaseEntry resultEntry) {
+
+ StoreBlock storeblock = (StoreBlock)
theBinding.entryToObject(dataEntry);
+ Long blockNo = new Long(storeblock.offset);
+ longTupleBinding.objectToEntry(blockNo, resultEntry);
+ return true;
+ }
+
+ }
+
private class ShutdownHook extends Thread
{
public void run() {
@@ -760,6 +882,7 @@
Thread.sleep(5000);
chkStore.close();
chkDB_accessTime.close();
+ chkDB_blockNum.close();
chkDB.close();
environment.close();
Logger.minor(this, "Closing database finished.");
@@ -769,17 +892,30 @@
}
}
- private long countCHKBlocks() {
- long count =0;
+ private long countCHKBlocks() throws IOException {
+ long count = 0;
+
try{
- Logger.minor(this, "Started counting items in database");
-
- count = ((BtreeStats)chkDB.getStats(null)).getLeafNodeCount();
-
- Logger.minor(this, "Counted "+count+" items in database");
- }catch(DatabaseException ex){
- Logger.minor(this, "Exception while counting items in
database",ex);
+ Cursor c = chkDB_blockNum.openCursor(null,null);
+ DatabaseEntry keyDBE = new DatabaseEntry();
+ DatabaseEntry dataDBE = new DatabaseEntry();
+
if(c.getLast(keyDBE,dataDBE,null)==OperationStatus.SUCCESS) {
+ StoreBlock storeBlock = (StoreBlock)
storeBlockTupleBinding.entryToObject(dataDBE);
+ count = storeBlock.offset;
+ }
+ c.close();
+ }catch(DatabaseException ex){ex.printStackTrace();}
+
+ count++;
+ System.out.println("Count from database: "+count);
+
+ long oCount = chkStore.length() / (dataBlockSize + headerBlockSize);
+
+ if(oCount > count) {
+ System.err.println("Count from file length: "+oCount);
+ return oCount;
}
+
return count;
}
Modified: trunk/freenet/src/freenet/support/DoublyLinkedListImpl.java
===================================================================
--- trunk/freenet/src/freenet/support/DoublyLinkedListImpl.java 2006-06-07
11:42:20 UTC (rev 9069)
+++ trunk/freenet/src/freenet/support/DoublyLinkedListImpl.java 2006-06-07
14:25:22 UTC (rev 9070)
@@ -238,11 +238,11 @@
* @return this item, or null if the item was not in the list
*/
public DoublyLinkedList.Item remove(DoublyLinkedList.Item i) {
+ if (i.getParent() == null) return null; // not in list
if(isEmpty()) {
Logger.error(this, "Illegal ERROR: Removing from an empty
list!!");
throw new IllegalStateException("Illegal ERROR: Removing from
an empty list!!");
}
- if (i.getParent() == null) return null; // not in list
if (i.getParent() != this)
throw new PromiscuousItemException(i, i.getParent());
DoublyLinkedList.Item next = i.getNext(), prev = i.getPrev();