diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
index a934fa6c8..c28c07fe9 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
@@ -541,15 +541,18 @@ public interface DatabaseComponent extends TransactionManager {
*/
long getNextCleanupDeadline(Transaction txn) throws DbException;
- /*
+ /**
* Returns the next time (in milliseconds since the Unix epoch) when a
- * message is due to be sent to the given contact. The returned value may
- * be zero if a message is due to be sent immediately, or Long.MAX_VALUE if
- * no messages are scheduled to be sent.
+ * message is due to be sent to the given contact over a transport with
+ * the given latency.
+ *
+ * The returned value may be zero if a message is due to be sent
+ * immediately, or Long.MAX_VALUE if no messages are scheduled to be sent.
*
* Read-only.
*/
- long getNextSendTime(Transaction txn, ContactId c) throws DbException;
+ long getNextSendTime(Transaction txn, ContactId c, long maxLatency)
+ throws DbException;
/**
* Returns the pending contact with the given ID.
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
index 1e6f4ab31..1e53c1fe2 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
@@ -587,13 +587,16 @@ interface Database {
/**
* Returns the next time (in milliseconds since the Unix epoch) when a
- * message is due to be sent to the given contact. The returned value may
- * be zero if a message is due to be sent immediately, or Long.MAX_VALUE
- * if no messages are scheduled to be sent.
+ * message is due to be sent to the given contact over a transport with
+ * the given latency.
+ *
+ * The returned value may be zero if a message is due to be sent
+ * immediately, or Long.MAX_VALUE if no messages are scheduled to be sent.
*
* Read-only.
*/
- long getNextSendTime(T txn, ContactId c) throws DbException;
+ long getNextSendTime(T txn, ContactId c, long maxLatency)
+ throws DbException;
/**
* Returns the pending contact with the given ID.
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index 14b033b41..cfc20ecce 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -814,10 +814,10 @@ class DatabaseComponentImpl implements DatabaseComponent {
}
@Override
- public long getNextSendTime(Transaction transaction, ContactId c)
- throws DbException {
+ public long getNextSendTime(Transaction transaction, ContactId c,
+ long maxLatency) throws DbException {
T txn = unbox(transaction);
- return db.getNextSendTime(txn, c);
+ return db.getNextSendTime(txn, c, maxLatency);
}
@Override
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index bf23fd683..9dc143bfa 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -2490,12 +2490,28 @@ abstract class JdbcDatabase implements Database {
}
@Override
- public long getNextSendTime(Connection txn, ContactId c)
+ public long getNextSendTime(Connection txn, ContactId c, long maxLatency)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
- String sql = "SELECT expiry FROM statuses"
+ // Are any messages sendable immediately?
+ String sql = "SELECT NULL FROM statuses"
+ + " WHERE contactId = ? AND state = ?"
+ + " AND groupShared = TRUE AND messageShared = TRUE"
+ + " AND deleted = FALSE AND seen = FALSE"
+ + " AND (maxLatency IS NULL OR ? < maxLatency)";
+ ps = txn.prepareStatement(sql);
+ ps.setInt(1, c.getInt());
+ ps.setInt(2, DELIVERED.getValue());
+ ps.setLong(3, maxLatency);
+ rs = ps.executeQuery();
+ boolean found = rs.next();
+ rs.close();
+ ps.close();
+ if (found) return 0;
+ // When is the earliest expiry time (could be in the past)?
+ sql = "SELECT expiry FROM statuses"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE"
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java
index 4bfb17bb0..3dc74bd19 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java
@@ -313,7 +313,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
Collection batch =
db.generateRequestedBatch(txn, contactId,
BATCH_CAPACITY, maxLatency);
- setNextSendTime(db.getNextSendTime(txn, contactId));
+ setNextSendTime(db.getNextSendTime(txn, contactId,
+ maxLatency));
return batch;
});
if (LOG.isLoggable(INFO))
@@ -356,7 +357,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
Offer o = db.transactionWithNullableResult(false, txn -> {
Offer offer = db.generateOffer(txn, contactId,
MAX_MESSAGE_IDS, maxLatency);
- setNextSendTime(db.getNextSendTime(txn, contactId));
+ setNextSendTime(db.getNextSendTime(txn, contactId,
+ maxLatency));
return offer;
});
if (LOG.isLoggable(INFO))
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
index 54d19da0d..35e091df3 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
@@ -2020,37 +2020,51 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, UNKNOWN, false, false, null);
// There should be no messages to send
- assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
+ assertEquals(Long.MAX_VALUE,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Share the group with the contact - still no messages to send
db.addGroupVisibility(txn, contactId, groupId, true);
- assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
+ assertEquals(Long.MAX_VALUE,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Set the message's state to DELIVERED - still no messages to send
db.setMessageState(txn, messageId, DELIVERED);
- assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
+ assertEquals(Long.MAX_VALUE,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Share the message - now it should be sendable immediately
db.setMessageShared(txn, messageId, true);
- assertEquals(0, db.getNextSendTime(txn, contactId));
+ assertEquals(0, db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Mark the message as requested - it should still be sendable
db.raiseRequestedFlag(txn, contactId, messageId);
- assertEquals(0, db.getNextSendTime(txn, contactId));
+ assertEquals(0, db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Update the message's expiry time as though we sent it - now the
// message should be sendable after one round-trip
- db.updateRetransmissionData(txn, contactId, messageId, 1000);
- assertEquals(now + 2000, db.getNextSendTime(txn, contactId));
+ db.updateRetransmissionData(txn, contactId, messageId, MAX_LATENCY);
+ assertEquals(now + MAX_LATENCY * 2,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
+
+ // The message should be sendable immediately over a transport with
+ // lower latency
+ assertEquals(0L, db.getNextSendTime(txn, contactId, MAX_LATENCY - 1));
// Update the message's expiry time again - now it should be sendable
// after two round-trips
- db.updateRetransmissionData(txn, contactId, messageId, 1000);
- assertEquals(now + 4000, db.getNextSendTime(txn, contactId));
+ db.updateRetransmissionData(txn, contactId, messageId, MAX_LATENCY);
+ assertEquals(now + MAX_LATENCY * 4,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
+
+ // The message should be sendable immediately over a transport with
+ // lower latency
+ assertEquals(0L, db.getNextSendTime(txn, contactId, MAX_LATENCY - 1));
// Delete the message - there should be no messages to send
db.deleteMessage(txn, messageId);
- assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
+ assertEquals(Long.MAX_VALUE,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn);
db.close();
@@ -2115,7 +2129,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.updateRetransmissionData(txn, contactId, messageId, MAX_LATENCY);
// The message should expire after 2 * MAX_LATENCY
- assertEquals(now + MAX_LATENCY * 2, db.getNextSendTime(txn, contactId));
+ assertEquals(now + MAX_LATENCY * 2,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Time: now + MAX_LATENCY * 2 - 1
// The message should not yet be sendable
@@ -2158,7 +2173,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.updateRetransmissionData(txn, contactId, messageId, MAX_LATENCY);
// The message should expire after 2 * MAX_LATENCY
- assertEquals(now + MAX_LATENCY * 2, db.getNextSendTime(txn, contactId));
+ assertEquals(now + MAX_LATENCY * 2,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
// The message should not be sendable via the same transport
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
@@ -2205,7 +2221,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.updateRetransmissionData(txn, contactId, messageId, MAX_LATENCY);
// The message should expire after 2 * MAX_LATENCY
- assertEquals(now + MAX_LATENCY * 2, db.getNextSendTime(txn, contactId));
+ assertEquals(now + MAX_LATENCY * 2,
+ db.getNextSendTime(txn, contactId, MAX_LATENCY));
// Time: now + MAX_LATENCY * 2 - 1
// The message should not yet be sendable
@@ -2216,8 +2233,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Reset the retransmission times
db.resetUnackedMessagesToSend(txn, contactId);
- // The message should have infinitely short expiry
- assertEquals(0, db.getNextSendTime(txn, contactId));
+ // The message should be sendable immediately
+ assertEquals(0, db.getNextSendTime(txn, contactId, MAX_LATENCY));
// The message should be sendable
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);