/*
* Mojito Distributed Hash Table (Mojito DHT)
* Copyright (C) 2006-2007 LimeWire LLC
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.limewire.mojito.db;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import junit.framework.Test;
import org.limewire.mojito.KUID;
import org.limewire.mojito.MojitoDHT;
import org.limewire.mojito.MojitoFactory;
import org.limewire.mojito.MojitoTestCase;
import org.limewire.mojito.db.impl.DHTValueEntityBag;
import org.limewire.mojito.db.impl.DHTValueImpl;
import org.limewire.mojito.db.impl.DatabaseImpl;
import org.limewire.mojito.routing.Contact;
import org.limewire.mojito.routing.ContactFactory;
import org.limewire.mojito.routing.Vendor;
import org.limewire.mojito.routing.Version;
import org.limewire.mojito.settings.ContextSettings;
import org.limewire.mojito.settings.DatabaseSettings;
import org.limewire.util.PrivilegedAccessor;
import org.limewire.util.StringUtils;
public class DatabaseTest extends MojitoTestCase {
/*static {
System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");
}*/
public DatabaseTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(DatabaseTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
private static DHTValueEntity createLocalDHTValue(byte[] value) {
return createLocalDHTValue(KUID.createRandomID(),
KUID.createRandomID(), value);
}
private static DHTValueEntity createLocalDHTValue(KUID nodeId, KUID valueId, byte[] value) {
Contact node = ContactFactory.createLocalContact(Vendor.UNKNOWN, Version.ZERO, nodeId, 0, false);
return new DHTValueEntity(node, node, valueId,
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, value), true);
}
private static DHTValueEntity createDirectDHTValue(byte[] value) {
return createDirectDHTValue(KUID.createRandomID(),
KUID.createRandomID(), value);
}
private static DHTValueEntity createDirectDHTValue(KUID nodeId, KUID valueId, byte[] value) {
SocketAddress addr = new InetSocketAddress(6666);
Contact node = ContactFactory.createLiveContact(addr, Vendor.UNKNOWN, Version.ZERO,
nodeId, addr, 0, Contact.DEFAULT_FLAG);
return new DHTValueEntity(node, node, valueId,
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, value), false);
}
private static DHTValueEntity createIndirectDHTValue(byte[] value) {
return createIndirectDHTValue(KUID.createRandomID(),
KUID.createRandomID(), KUID.createRandomID(), value);
}
private static DHTValueEntity createIndirectDHTValue(KUID creatorId, KUID senderId, KUID valueId, byte[] value) {
SocketAddress addr = new InetSocketAddress(6666);
Contact creator = ContactFactory.createLiveContact(addr, Vendor.UNKNOWN, Version.ZERO,
creatorId, addr, 0, Contact.DEFAULT_FLAG);
Contact sender = ContactFactory.createLiveContact(addr, Vendor.UNKNOWN, Version.ZERO,
senderId, addr, 0, Contact.DEFAULT_FLAG);
return new DHTValueEntity(creator, sender, valueId,
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, value), false);
}
public void testLocalAdd() throws Exception {
Database database = new DatabaseImpl();
// Add a local value
DHTValueEntity value1 = createLocalDHTValue(StringUtils.toAsciiBytes("Hello World"));
database.store(value1);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value1.getPrimaryKey())
.get(value1.getSecondaryKey()).getValue().getValue()));
// Neither direct...
DHTValueEntity value2 = createDirectDHTValue(value1.getSecondaryKey(),
value1.getPrimaryKey(), StringUtils.toAsciiBytes("Mojito"));
database.store(value2);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value2.getPrimaryKey())
.get(value2.getSecondaryKey()).getValue().getValue()));
// ...nor indirect values can replace a local value
DHTValueEntity value3 = createIndirectDHTValue(value1.getSecondaryKey(),
KUID.createRandomID(), value1.getPrimaryKey(), StringUtils.toAsciiBytes("Mary"));
database.store(value3);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value3.getPrimaryKey())
.get(value3.getSecondaryKey()).getValue().getValue()));
// Only local values can replace local values
DHTValueEntity value4 = createLocalDHTValue(value1.getSecondaryKey(),
value1.getPrimaryKey(), StringUtils.toAsciiBytes("Tonic"));
database.store(value4);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Tonic"),
database.get(value4.getPrimaryKey())
.get(value4.getSecondaryKey()).getValue().getValue()));
// Add a new direct value
DHTValueEntity value5 = createDirectDHTValue(StringUtils.toAsciiBytes("Mojito"));
database.store(value5);
assertEquals(2, database.getKeyCount());
assertEquals(2, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mojito"),
database.get(value5.getPrimaryKey())
.get(value5.getSecondaryKey()).getValue().getValue()));
// local values replace direct values
DHTValueEntity value6 = createLocalDHTValue(value5.getSecondaryKey(),
value5.getPrimaryKey(), StringUtils.toAsciiBytes("Mary"));
database.store(value6);
assertEquals(2, database.getKeyCount());
assertEquals(2, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mary"),
database.get(value6.getPrimaryKey())
.get(value6.getSecondaryKey()).getValue().getValue()));
// Add an indirect value
DHTValueEntity value7 = createDirectDHTValue(StringUtils.toAsciiBytes("Bloody"));
database.store(value7);
assertEquals(3, database.getKeyCount());
assertEquals(3, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Bloody"),
database.get(value7.getPrimaryKey())
.get(value7.getSecondaryKey()).getValue().getValue()));
// local values replace indirect values
DHTValueEntity value8 = createLocalDHTValue(value7.getSecondaryKey(),
value7.getPrimaryKey(), StringUtils.toAsciiBytes("Lime"));
database.store(value8);
assertEquals(3, database.getKeyCount());
assertEquals(3, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Lime"),
database.get(value8.getPrimaryKey())
.get(value8.getSecondaryKey()).getValue().getValue()));
}
public void testDirectAdd() throws Exception {
Database database = new DatabaseImpl();
// Add a directly stored value
DHTValueEntity value1 = createDirectDHTValue(StringUtils.toAsciiBytes("Hello World"));
database.store(value1);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value1.getPrimaryKey())
.get(value1.getSecondaryKey()).getValue().getValue()));
// Shouldn't change
database.store(value1);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
// The originator is issuing a direct store request
DHTValueEntity value2 = createDirectDHTValue(value1.getSecondaryKey(),
value1.getPrimaryKey(), StringUtils.toAsciiBytes("Mojito"));
database.store(value2);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mojito"),
database.get(value2.getPrimaryKey())
.get(value2.getSecondaryKey()).getValue().getValue()));
// A directly stored value cannot be replaced by
// an indirect value
DHTValueEntity value3 = createIndirectDHTValue(value2.getSecondaryKey(),
KUID.createRandomID(), value2.getPrimaryKey(), StringUtils.toAsciiBytes("Tough"));
database.store(value3);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mojito"),
database.get(value3.getPrimaryKey())
.get(value3.getSecondaryKey()).getValue().getValue()));
}
public void testIndirectAdd() throws Exception {
Database database = new DatabaseImpl();
// Add an indirectly stored value
DHTValueEntity value1 = createIndirectDHTValue(StringUtils.toAsciiBytes("Hello World"));
database.store(value1);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value1.getPrimaryKey())
.get(value1.getSecondaryKey()).getValue().getValue()));
// Indirect replaces indirect
DHTValueEntity value2 = createIndirectDHTValue(value1.getSecondaryKey(),
KUID.createRandomID(), value1.getPrimaryKey(), StringUtils.toAsciiBytes("Mojito"));
database.store(value2);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mojito"),
database.get(value2.getPrimaryKey())
.get(value2.getSecondaryKey()).getValue().getValue()));
// Direct replaces indirect
DHTValueEntity value3 = createDirectDHTValue(value2.getSecondaryKey(),
value2.getPrimaryKey(), StringUtils.toAsciiBytes("Tonic"));
database.store(value3);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Tonic"),
database.get(value3.getPrimaryKey())
.get(value3.getSecondaryKey()).getValue().getValue()));
// Indirect shouldn't replace direct
DHTValueEntity value4 = createIndirectDHTValue(value3.getSecondaryKey(),
KUID.createRandomID(), value3.getPrimaryKey(), StringUtils.toAsciiBytes("Mary"));
database.store(value3);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Tonic"),
database.get(value4.getPrimaryKey())
.get(value4.getSecondaryKey()).getValue().getValue()));
}
public void testMultipleValues() throws Exception {
Database database = new DatabaseImpl();
// Add a value
DHTValueEntity value1 = createIndirectDHTValue(StringUtils.toAsciiBytes("Hello World"));
database.store(value1);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value1.getPrimaryKey())
.get(value1.getSecondaryKey()).getValue().getValue()));
// Same Key but different originator/sender
DHTValueEntity value2 = createIndirectDHTValue(KUID.createRandomID(),
KUID.createRandomID(), value1.getPrimaryKey(), StringUtils.toAsciiBytes("Tonic"));
database.store(value2);
assertEquals(1, database.getKeyCount());
assertEquals(2, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Tonic"),
database.get(value2.getPrimaryKey())
.get(value2.getSecondaryKey()).getValue().getValue()));
// Same Key but a different originator
DHTValueEntity value3 = createDirectDHTValue(KUID.createRandomID(),
value1.getPrimaryKey(), StringUtils.toAsciiBytes("Mary"));
database.store(value3);
assertEquals(1, database.getKeyCount());
assertEquals(3, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mary"),
database.get(value3.getPrimaryKey())
.get(value3.getSecondaryKey()).getValue().getValue()));
// Different Key
DHTValueEntity value4 = createDirectDHTValue(StringUtils.toAsciiBytes("Olga"));
database.store(value4);
assertEquals(2, database.getKeyCount());
assertEquals(4, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Olga"),
database.get(value4.getPrimaryKey())
.get(value4.getSecondaryKey()).getValue().getValue()));
}
public void testRemove() throws Exception {
Database database = new DatabaseImpl();
// Add a value
DHTValueEntity value1 = createIndirectDHTValue(StringUtils.toAsciiBytes("Hello World"));
database.store(value1);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value1.getPrimaryKey())
.get(value1.getSecondaryKey()).getValue().getValue()));
// It's not possible to remove a value indirectly
DHTValueEntity value2 = createIndirectDHTValue(value1.getSecondaryKey(),
KUID.createRandomID(), value1.getPrimaryKey(), new byte[0]);
database.store(value2);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Hello World"),
database.get(value2.getPrimaryKey())
.get(value2.getSecondaryKey()).getValue().getValue()));
// But we can remove values directly
DHTValueEntity value3 = createDirectDHTValue(value1.getSecondaryKey(),
value1.getPrimaryKey(), new byte[0]);
database.store(value3);
assertEquals(0, database.getKeyCount());
assertEquals(0, database.getValueCount());
// Add a new local value
DHTValueEntity value4 = createLocalDHTValue(StringUtils.toAsciiBytes("Mojito"));
database.store(value4);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mojito"),
database.get(value4.getPrimaryKey())
.get(value4.getSecondaryKey()).getValue().getValue()));
// A local value cannot be removed
DHTValueEntity value5 = createDirectDHTValue(value4.getSecondaryKey(),
value4.getPrimaryKey(), new byte[0]);
database.store(value5);
assertEquals(1, database.getKeyCount());
assertEquals(1, database.getValueCount());
assertTrue(Arrays.equals(StringUtils.toAsciiBytes("Mojito"),
database.get(value5.getPrimaryKey())
.get(value5.getSecondaryKey()).getValue().getValue()));
// But a local value can remove a local value
DHTValueEntity value6 = createLocalDHTValue(value4.getSecondaryKey(),
value4.getPrimaryKey(), new byte[0]);
database.store(value6);
assertEquals(0, database.getKeyCount());
assertEquals(0, database.getValueCount());
}
/**
* A Database is a Set of DHTValueBags. Each Bag is a Set of
* DHTValueEntities.
*
* If you iterate over the Bag and remove items from the Bag
* you leak Bag references because they're not removed from
* the Database
*
* If you iterate over the Bag and remove items from the Database
* you don't leak references but run into a ConcurrentModificationException.
*
* Solution: Get a copy of the Bag and remove the items from the Database
*
* Better solution: It'd be nice if the Bag is holding a reference to its
* Database and cleanup itself once it's empty. There are however all kinds
* of side effects if you start adding items straight to the Bag instead
* of using the Database interface and so on.
*
* This test represents the current implemenetation. If you implement the
* "better solution" this test will fail!
*/
public void testMultiRemove() {
MojitoDHT dht = MojitoFactory.createDHT();
DatabaseImpl database = (DatabaseImpl)dht.getDatabase();
KUID key = KUID.createRandomID();
Contact c1 = ContactFactory.createUnknownContact(ContextSettings.getVendor(),
ContextSettings.getVersion(), KUID.createRandomID(),
new InetSocketAddress("localhost", 1000));
Contact c2 = ContactFactory.createUnknownContact(ContextSettings.getVendor(),
ContextSettings.getVersion(), KUID.createRandomID(),
new InetSocketAddress("localhost", 2000));
DHTValue value = new DHTValueImpl(DHTValueType.BINARY, Version.ZERO, new byte[1]);
DHTValueEntity entity1 = new DHTValueEntity(
dht.getLocalNode(), dht.getLocalNode(), key, value, true);
DHTValueEntity entity2 = new DHTValueEntity(c1, c1, key, value, false);
DHTValueEntity entity3 = new DHTValueEntity(c2, c2, key, value, false);
database.store(entity1);
database.store(entity2);
database.store(entity3);
assertEquals(3, database.getValueCount());
Map<KUID, DHTValueEntity> bag = database.get(key);
assertNotNull(bag);
assertEquals(3, bag.size());
// Cannot Iterate over Bag and remove items from it
// TODO This operation is maybe OK in future (check
// the Database implementation).
try {
for (Iterator<?> it = bag.values().iterator(); it.hasNext(); ) {
it.remove();
}
fail("Should have failed");
} catch (UnsupportedOperationException expected) {
}
assertEquals(3, bag.size());
for (DHTValueEntity e : bag.values()) {
database.remove(e.getPrimaryKey(), e.getSecondaryKey());
}
assertEquals(0, database.getKeyCount());
assertEquals(0, database.getValueCount());
assertEquals(3, bag.size());
// The Bag should be gone
assertNull(database.getBag(key));
assertTrue(database.get(key).isEmpty());
}
public void testMaxValuesPerAddress() {
DatabaseSettings.LIMIT_VALUES_PER_ADDRESS.setValue(true);
DatabaseSettings.MAX_VALUES_PER_ADDRESS.setValue(5);
DatabaseSettings.LIMIT_VALUES_PER_NETWORK.setValue(false);
Database db = new DatabaseImpl();
// this should accept
Contact badHost = ContactFactory.createLiveContact(
new InetSocketAddress("192.168.1.1", 1111),
Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(),
new InetSocketAddress("192.168.1.1", 1111), 0, Contact.DEFAULT_FLAG);
Contact goodHost = ContactFactory.createLiveContact(
new InetSocketAddress("192.168.1.2", 1111),
Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(),
new InetSocketAddress("192.168.1.2", 1111), 0, Contact.DEFAULT_FLAG);
DHTValueEntity value1 = new DHTValueEntity(badHost, badHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
DHTValueEntity value2 = new DHTValueEntity(goodHost, goodHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
// Store both values
assertTrue(db.store(value1));
assertTrue(db.store(value2));
assertEquals(2, db.getValueCount());
// The bad host fills up all its free slots
int remaining = DatabaseSettings.MAX_VALUES_PER_ADDRESS.getValue() - 1;
for (int i = 0; i < remaining; i++) {
DHTValueEntity value3 = new DHTValueEntity(badHost, badHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertTrue(db.store(value3));
}
assertEquals(6, db.getValueCount());
// The bad host cannot store values anymore
DHTValueEntity value4 = new DHTValueEntity(badHost, badHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertFalse(db.store(value4));
assertEquals(6, db.getValueCount());
// The good host can
DHTValueEntity value5 = new DHTValueEntity(goodHost, goodHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertTrue(db.store(value5));
assertEquals(7, db.getValueCount());
// Store forward a value from bad host? Cannot store!
DHTValueEntity value6 = new DHTValueEntity(badHost, goodHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertFalse(db.store(value6));
assertEquals(7, db.getValueCount());
// Store forward from bad host? Should work!
DHTValueEntity value7 = new DHTValueEntity(goodHost, badHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertTrue(db.store(value7));
assertEquals(8, db.getValueCount());
// Bad host but is a local value? Should work!
DHTValueEntity value8 = new DHTValueEntity(badHost, badHost, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), true);
assertTrue(db.store(value8));
assertEquals(9, db.getValueCount());
}
public void testMaxValuesPerNetwork() {
DatabaseSettings.LIMIT_VALUES_PER_ADDRESS.setValue(false);
DatabaseSettings.LIMIT_VALUES_PER_NETWORK.setValue(true);
DatabaseSettings.MAX_VALUES_PER_NETWORK.setValue(5);
Database db = new DatabaseImpl();
// Fill up all slots for the Class C Network
for (int i = 0; i < DatabaseSettings.MAX_VALUES_PER_NETWORK.getValue(); i++) {
Contact node1 = ContactFactory.createLiveContact(
new InetSocketAddress("192.168.1." + i, 1111+i),
Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(),
new InetSocketAddress("192.168.1." + i, 1111+i), 0, Contact.DEFAULT_FLAG);
DHTValueEntity value1 = new DHTValueEntity(node1, node1, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertTrue(db.store(value1));
}
assertEquals(5, db.getValueCount());
// Shouldn't be able to store more from the Class C Network!
Contact node2 = ContactFactory.createLiveContact(
new InetSocketAddress("192.168.1.50", 2050),
Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(),
new InetSocketAddress("192.168.1.50", 2050), 0, Contact.DEFAULT_FLAG);
DHTValueEntity value2 = new DHTValueEntity(node2, node2, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertFalse(db.store(value2));
assertEquals(5, db.getValueCount());
// Different Class C Network, should work!
Contact node3 = ContactFactory.createLiveContact(
new InetSocketAddress("192.168.2.50", 2050),
Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(),
new InetSocketAddress("192.168.2.50", 2050), 0, Contact.DEFAULT_FLAG);
DHTValueEntity value3 = new DHTValueEntity(node3, node3, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), false);
assertTrue(db.store(value3));
assertEquals(6, db.getValueCount());
// Same Class C Network as Node #2 but the value is local. Should work!
DHTValueEntity value4 = new DHTValueEntity(node2, node2, KUID.createRandomID(),
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("test")), true);
assertTrue(db.store(value4));
assertEquals(7, db.getValueCount());
}
public void testIncrementRequestLoad() throws Exception{
DatabaseImpl database = new DatabaseImpl();
DHTValueEntity entity = createLocalDHTValue(StringUtils.toAsciiBytes("Hello World"));
database.store(entity);
KUID primaryKey = entity.getPrimaryKey();
assertEquals(0f, database.getRequestLoad(primaryKey, true));
//should start with larger than smoothing factor
Thread.sleep(500);
float load = database.getRequestLoad(primaryKey, true);
assertGreaterThan(
DatabaseSettings.VALUE_REQUEST_LOAD_SMOOTHING_FACTOR.getValue(), load);
Thread.sleep(500);
assertGreaterThan(load, database.getRequestLoad(primaryKey, true));
//test a 0 smoothing factor
DatabaseSettings.VALUE_REQUEST_LOAD_SMOOTHING_FACTOR.setValue(0);
database.clear();
database.store(entity);
Thread.sleep(500);
assertEquals(0F, database.getRequestLoad(primaryKey, true));
//try a delay larger than nulling time
database.clear();
database.store(entity);
DHTValueEntityBag bag = database.getBag(entity.getPrimaryKey());
long now = System.currentTimeMillis();
PrivilegedAccessor.setValue(bag, "lastRequestTime",
now - DatabaseSettings.VALUE_REQUEST_LOAD_NULLING_DELAY.getValue()*1000L);
Thread.sleep(500);
//load should be 0
assertEquals(0, (int)bag.incrementRequestLoad());
// try a very very large delay
database.clear();
database.store(entity);
bag = database.getBag(entity.getPrimaryKey());
PrivilegedAccessor.setValue(bag, "lastRequestTime", 1L);
load = bag.incrementRequestLoad();
//load should never get < 0
assertGreaterThanOrEquals(0f, load);
//now try a very very small delay
bag.incrementRequestLoad();
Thread.sleep(10);
bag.incrementRequestLoad();
Thread.sleep(10);
bag.incrementRequestLoad();
Thread.sleep(10);
load = bag.incrementRequestLoad();
Thread.sleep(10);
load = bag.incrementRequestLoad();
Thread.sleep(10);
load = bag.incrementRequestLoad();
//should now have increased (a lot!)
assertGreaterThan(1f, load);
//but never be larger than 1/0.01
assertLessThan(1f/0.01f, load);
}
public void testReplaceFirewalledValue() throws InterruptedException {
DatabaseSettings.LIMIT_VALUES_PER_ADDRESS.setValue(false);
DatabaseSettings.LIMIT_VALUES_PER_NETWORK.setValue(false);
final int MAX_VALUES_PER_KEY = 5;
DatabaseSettings.MAX_VALUES_PER_KEY.setValue(MAX_VALUES_PER_KEY);
DatabaseImpl database = new DatabaseImpl();
List<DHTValueEntity> values = new ArrayList<DHTValueEntity>();
KUID primaryKey = KUID.createRandomID();
// Create a bunch of firewalled values and add them to the Database
for (int i = 0; i < 2*MAX_VALUES_PER_KEY; i++) {
SocketAddress addr = new InetSocketAddress(6666);
Contact node = ContactFactory.createLiveContact(
addr,
Vendor.UNKNOWN,
Version.ZERO,
KUID.createRandomID(),
addr, 0,
Contact.FIREWALLED_FLAG);
assertTrue(node.isFirewalled());
DHTValueEntity entity = new DHTValueEntity(node, node, primaryKey,
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("Hello World")), false);
database.store(entity);
values.add(entity);
// Make sure the creation times are different
Thread.sleep(100);
}
// Check initial State
assertEquals(1, database.getKeyCount());
assertEquals(MAX_VALUES_PER_KEY, database.getValueCount());
// The first five values should be in the Database
for (DHTValueEntity entity : values.subList(0, MAX_VALUES_PER_KEY)) {
assertTrue(database.contains(entity.getPrimaryKey(), entity.getSecondaryKey()));
}
// And the last five shouldn't!
for (DHTValueEntity entity : values.subList(MAX_VALUES_PER_KEY, values.size())) {
assertFalse(database.contains(entity.getPrimaryKey(), entity.getSecondaryKey()));
}
// Create a non-firewalled value
SocketAddress addr = new InetSocketAddress(6666);
Contact node = ContactFactory.createLiveContact(
addr,
Vendor.UNKNOWN,
Version.ZERO,
KUID.createRandomID(),
addr, 0,
Contact.DEFAULT_FLAG);
assertFalse(node.isFirewalled());
DHTValueEntity notFirewalled = new DHTValueEntity(node, node, primaryKey,
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("Hello World")), false);
// Store it in the Database
database.store(notFirewalled);
values.add(notFirewalled);
// It should be there
assertTrue(database.contains(notFirewalled.getPrimaryKey(), notFirewalled.getSecondaryKey()));
// The oldest firewalled value should be done
DHTValueEntity oldest = values.get(0);
assertFalse(database.contains(oldest.getPrimaryKey(), oldest.getSecondaryKey()));
// And the remaining four firewalled values should be still there
for (DHTValueEntity firewalled : values.subList(1, MAX_VALUES_PER_KEY)) {
assertTrue(database.contains(firewalled.getPrimaryKey(), firewalled.getSecondaryKey()));
}
// Create more non-firewalled values
for (int i = 0; i < 2*MAX_VALUES_PER_KEY; i++) {
addr = new InetSocketAddress(6666);
node = ContactFactory.createLiveContact(
addr,
Vendor.UNKNOWN,
Version.ZERO,
KUID.createRandomID(),
addr, 0,
Contact.DEFAULT_FLAG);
assertFalse(node.isFirewalled());
DHTValueEntity entity = new DHTValueEntity(node, node, primaryKey,
new DHTValueImpl(DHTValueType.TEST, Version.ZERO, StringUtils.toAsciiBytes("Hello World")), false);
database.store(entity);
values.add(entity);
// Make sure the creation times are different
Thread.sleep(100);
}
// Check state
assertEquals(1, database.getKeyCount());
assertEquals(MAX_VALUES_PER_KEY, database.getValueCount());
// The firewalled values should be all gone
int fromIndex = 0;
int toIndex = values.size() - (2*MAX_VALUES_PER_KEY);
for (DHTValueEntity entity : values.subList(fromIndex, toIndex-1)) {
assertTrue(entity.getCreator().isFirewalled());
assertFalse(database.contains(entity.getPrimaryKey(), entity.getSecondaryKey()));
}
// The first five non-firewalled values should be there
fromIndex = toIndex;
toIndex = fromIndex + MAX_VALUES_PER_KEY;
for (DHTValueEntity entity : values.subList(fromIndex, toIndex-1)) {
assertFalse(entity.getCreator().isFirewalled());
assertTrue(database.contains(entity.getPrimaryKey(), entity.getSecondaryKey()));
}
// The last five non-firewalled shouldn't be there
fromIndex = toIndex;
toIndex = values.size();
for (DHTValueEntity entity : values.subList(fromIndex, toIndex)) {
assertFalse(entity.getCreator().isFirewalled());
assertFalse(database.contains(entity.getPrimaryKey(), entity.getSecondaryKey()));
}
}
}