Connections now use two separate threads for writing and listening

- this should avoid dead locks, specifically when connecting to Jabit :/
- also, Java 8 features are now allowed in modules not needed by Android clients
This commit is contained in:
2015-10-14 18:37:43 +02:00
parent 117ac3ca73
commit 511b3c1754
9 changed files with 246 additions and 180 deletions

View File

@ -10,6 +10,8 @@ uploadArchives {
}
}
sourceCompatibility = 1.8
dependencies {
compile project(':domain')
compile 'org.flywaydb:flyway-core:3.2.1'

View File

@ -27,12 +27,17 @@ import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static ch.dissem.bitmessage.utils.UnixTime.now;
public class JdbcInventory extends JdbcHelper implements Inventory {
private static final Logger LOG = LoggerFactory.getLogger(JdbcInventory.class);
private final Map<Long, Map<InventoryVector, Long>> cache = new ConcurrentHashMap<>();
public JdbcInventory(JdbcConfig config) {
super(config);
}
@ -40,36 +45,43 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
@Override
public List<InventoryVector> getInventory(long... streams) {
List<InventoryVector> result = new LinkedList<>();
try (Connection connection = config.getConnection()) {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE expires > " + now() +
" AND stream IN (" + join(streams) + ")");
while (rs.next()) {
result.add(new InventoryVector(rs.getBytes("hash")));
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
for (long stream : streams) {
getCache(stream).entrySet().stream()
.filter(e -> e.getValue() > now())
.forEach(e -> result.add(e.getKey()));
}
return result;
}
private List<InventoryVector> getFullInventory(long... streams) {
List<InventoryVector> result = new LinkedList<>();
try (Connection connection = config.getConnection()) {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE stream IN (" + join(streams) + ")");
while (rs.next()) {
result.add(new InventoryVector(rs.getBytes("hash")));
private Map<InventoryVector, Long> getCache(long stream) {
Map<InventoryVector, Long> result = cache.get(stream);
if (result == null) {
synchronized (cache) {
if (cache.get(stream) == null) {
result = new ConcurrentHashMap<>();
cache.put(stream, result);
try (Connection connection = config.getConnection()) {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT hash, expires FROM Inventory WHERE expires > "
+ now(-5 * MINUTE) + " AND stream = " + stream);
while (rs.next()) {
result.put(new InventoryVector(rs.getBytes("hash")), rs.getLong("expires"));
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
}
}
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
}
return result;
}
@Override
public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) {
offer.removeAll(getFullInventory(streams));
for (long stream : streams) {
getCache(stream).forEach((iv, t) -> offer.remove(iv));
}
return offer;
}
@ -131,6 +143,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
ps.setLong(5, object.getType());
ps.setLong(6, object.getVersion());
ps.executeUpdate();
getCache(object.getStream()).put(iv, object.getExpiresTime());
} catch (SQLException e) {
LOG.debug("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e);
} catch (Exception e) {
@ -140,28 +153,19 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
@Override
public boolean contains(ObjectMessage object) {
try (Connection connection = config.getConnection()) {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT count(1) FROM Inventory WHERE hash = X'"
+ object.getInventoryVector() + "'");
if (rs.next()) {
return rs.getInt(1) > 0;
} else {
throw new RuntimeException("Couldn't query if inventory contains " + object.getInventoryVector());
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return getCache(object.getStream()).entrySet().stream()
.anyMatch(x -> x.getKey().equals(object.getInventoryVector()));
}
@Override
public void cleanup() {
try (Connection connection = config.getConnection()) {
// We delete only objects that expired 5 minutes ago or earlier, so we don't request objects we just deleted
connection.createStatement().executeUpdate("DELETE FROM Inventory WHERE expires < " + (now() - 300));
connection.createStatement().executeUpdate("DELETE FROM Inventory WHERE expires < " + now(-5 * MINUTE));
} catch (SQLException e) {
LOG.debug(e.getMessage(), e);
}
for (Map<InventoryVector, Long> c : cache.values()) {
c.entrySet().removeIf(e -> e.getValue() < now(-5 * MINUTE));
}
}
}