Created an improved JdbcNodeRegistry and removed MemoryNodeRegistry, as it doesn't properly work with the way nodes are handled and disseminated in the new PyBitmessage client. The new one should work a lot more stable.
This commit is contained in:
@ -0,0 +1,167 @@
|
||||
package ch.dissem.bitmessage.repository;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||
import ch.dissem.bitmessage.ports.NodeRegistry;
|
||||
import ch.dissem.bitmessage.utils.Collections;
|
||||
import ch.dissem.bitmessage.utils.SqlStrings;
|
||||
import ch.dissem.bitmessage.utils.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static ch.dissem.bitmessage.ports.NodeRegistryHelper.loadStableNodes;
|
||||
import static ch.dissem.bitmessage.utils.UnixTime.*;
|
||||
|
||||
public class JdbcNodeRegistry extends JdbcHelper implements NodeRegistry {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JdbcNodeRegistry.class);
|
||||
private Map<Long, Set<NetworkAddress>> stableNodes;
|
||||
|
||||
public JdbcNodeRegistry(JdbcConfig config) {
|
||||
super(config);
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
try (
|
||||
Connection connection = config.getConnection();
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM Node WHERE time<?")
|
||||
) {
|
||||
ps.setLong(1, now(-28 * DAY));
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkAddress loadExisting(NetworkAddress node) {
|
||||
String query =
|
||||
"SELECT stream, address, port, services, time" +
|
||||
" FROM Node" +
|
||||
" WHERE stream = " + node.getStream() +
|
||||
" AND address = X'" + Strings.hex(node.getIPv6()) + "'" +
|
||||
" AND port = " + node.getPort();
|
||||
try (
|
||||
Connection connection = config.getConnection();
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(query)
|
||||
) {
|
||||
if (rs.next()) {
|
||||
return new NetworkAddress.Builder()
|
||||
.stream(rs.getLong("stream"))
|
||||
.ipv6(rs.getBytes("address"))
|
||||
.port(rs.getInt("port"))
|
||||
.services(rs.getLong("services"))
|
||||
.time(rs.getLong("time"))
|
||||
.build();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new ApplicationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NetworkAddress> getKnownAddresses(int limit, long... streams) {
|
||||
List<NetworkAddress> result = new LinkedList<>();
|
||||
String query =
|
||||
"SELECT stream, address, port, services, time" +
|
||||
" FROM Node WHERE stream IN (" + SqlStrings.join(streams) + ")" +
|
||||
" ORDER BY TIME DESC" +
|
||||
" LIMIT " + limit;
|
||||
try (
|
||||
Connection connection = config.getConnection();
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(query)
|
||||
) {
|
||||
while (rs.next()) {
|
||||
result.add(
|
||||
new NetworkAddress.Builder()
|
||||
.stream(rs.getLong("stream"))
|
||||
.ipv6(rs.getBytes("address"))
|
||||
.port(rs.getInt("port"))
|
||||
.services(rs.getLong("services"))
|
||||
.time(rs.getLong("time"))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new ApplicationException(e);
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
synchronized (this) {
|
||||
if (stableNodes == null) {
|
||||
stableNodes = loadStableNodes();
|
||||
}
|
||||
}
|
||||
for (long stream : streams) {
|
||||
Set<NetworkAddress> nodes = stableNodes.get(stream);
|
||||
if (nodes != null) {
|
||||
result.add(Collections.selectRandom(nodes));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offerAddresses(List<NetworkAddress> nodes) {
|
||||
cleanUp();
|
||||
nodes.stream()
|
||||
.filter(node -> node.getTime() < now(+24 * HOUR) && node.getTime() > now(-28 * DAY))
|
||||
.forEach(node -> {
|
||||
synchronized (this) {
|
||||
NetworkAddress existing = loadExisting(node);
|
||||
if (existing == null) {
|
||||
insert(node);
|
||||
} else if (node.getTime() > existing.getTime()) {
|
||||
update(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void insert(NetworkAddress node) {
|
||||
try (
|
||||
Connection connection = config.getConnection();
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO Node (stream, address, port, services, time) " +
|
||||
"VALUES (?, ?, ?, ?, ?)")
|
||||
) {
|
||||
ps.setLong(1, node.getStream());
|
||||
ps.setBytes(2, node.getIPv6());
|
||||
ps.setInt(3, node.getPort());
|
||||
ps.setLong(4, node.getServices());
|
||||
ps.setLong(5, node.getTime());
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void update(NetworkAddress node) {
|
||||
try (
|
||||
Connection connection = config.getConnection();
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE Node SET services=?, time=? WHERE stream=? AND address=? AND port=?")
|
||||
) {
|
||||
ps.setLong(1, node.getServices());
|
||||
ps.setLong(2, node.getTime());
|
||||
ps.setLong(3, node.getStream());
|
||||
ps.setBytes(4, node.getIPv6());
|
||||
ps.setInt(5, node.getPort());
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
CREATE TABLE Node (
|
||||
stream BIGINT NOT NULL,
|
||||
address BINARY(32) NOT NULL,
|
||||
port INT NOT NULL,
|
||||
services BIGINT NOT NULL,
|
||||
time BIGINT NOT NULL,
|
||||
PRIMARY KEY (stream, address, port)
|
||||
);
|
||||
CREATE INDEX idx_time on Node(time);
|
@ -17,8 +17,8 @@
|
||||
package ch.dissem.bitmessage.repository;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
||||
import ch.dissem.bitmessage.ports.NodeRegistry;
|
||||
import ch.dissem.bitmessage.utils.UnixTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -27,8 +27,15 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static ch.dissem.bitmessage.utils.UnixTime.now;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Please note that some tests fail if there is no internet connection,
|
||||
* as the initial nodes' IP addresses are determined by DNS lookup.
|
||||
*/
|
||||
public class JdbcNodeRegistryTest extends TestBase {
|
||||
private TestJdbcConfig config;
|
||||
private NodeRegistry registry;
|
||||
@ -37,21 +44,26 @@ public class JdbcNodeRegistryTest extends TestBase {
|
||||
public void setUp() throws Exception {
|
||||
config = new TestJdbcConfig();
|
||||
config.reset();
|
||||
registry = new MemoryNodeRegistry();
|
||||
registry = new JdbcNodeRegistry(config);
|
||||
|
||||
registry.offerAddresses(Arrays.asList(
|
||||
createAddress(1, 8444, 1, now()),
|
||||
createAddress(2, 8444, 1, now()),
|
||||
createAddress(3, 8444, 1, now()),
|
||||
createAddress(4, 8444, 2, now())
|
||||
createAddress(1, 8444, 1, now()),
|
||||
createAddress(2, 8444, 1, now()),
|
||||
createAddress(3, 8444, 1, now()),
|
||||
createAddress(4, 8444, 2, now())
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitNodes() throws Exception {
|
||||
public void ensureGetKnownNodesWithoutStreamsYieldsEmpty() {
|
||||
assertThat(registry.getKnownAddresses(10), empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensurePredefinedNodeIsReturnedWhenDatabaseIsEmpty() throws Exception {
|
||||
config.reset();
|
||||
List<NetworkAddress> knownAddresses = registry.getKnownAddresses(2, 1);
|
||||
assertEquals(2, knownAddresses.size());
|
||||
assertEquals(1, knownAddresses.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -66,16 +78,16 @@ public class JdbcNodeRegistryTest extends TestBase {
|
||||
@Test
|
||||
public void testOfferAddresses() throws Exception {
|
||||
registry.offerAddresses(Arrays.asList(
|
||||
createAddress(1, 8444, 1, now()),
|
||||
createAddress(10, 8444, 1, now()),
|
||||
createAddress(11, 8444, 1, now())
|
||||
createAddress(1, 8444, 1, now()),
|
||||
createAddress(10, 8444, 1, now()),
|
||||
createAddress(11, 8444, 1, now())
|
||||
));
|
||||
|
||||
List<NetworkAddress> knownAddresses = registry.getKnownAddresses(1000, 1);
|
||||
assertEquals(5, knownAddresses.size());
|
||||
|
||||
registry.offerAddresses(Collections.singletonList(
|
||||
createAddress(1, 8445, 1, now())
|
||||
createAddress(1, 8445, 1, now())
|
||||
));
|
||||
|
||||
knownAddresses = registry.getKnownAddresses(1000, 1);
|
||||
@ -84,10 +96,10 @@ public class JdbcNodeRegistryTest extends TestBase {
|
||||
|
||||
private NetworkAddress createAddress(int lastByte, int port, long stream, long time) {
|
||||
return new NetworkAddress.Builder()
|
||||
.ipv6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, lastByte)
|
||||
.port(port)
|
||||
.stream(stream)
|
||||
.time(time)
|
||||
.build();
|
||||
.ipv6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, lastByte)
|
||||
.port(port)
|
||||
.stream(stream)
|
||||
.time(time)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user