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 3413dc080..ac1566c01 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 @@ -471,6 +471,14 @@ public interface DatabaseComponent extends TransactionManager { Map getUnackedMessagesToSend(Transaction txn, ContactId c) throws DbException; + /** + * Reset the transmission count, expiry time and ETA of all messages that + * are eligible to be sent to the given contact. This includes messages that + * have already been sent and are not yet due for retransmission. + */ + void resetUnackedMessagesToSend(Transaction txn, ContactId c) + throws DbException; + /** * Returns the total length, including headers, of all messages that are * eligible to be sent to the given contact. This may include messages 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 ab2376a4b..a90019bc9 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 @@ -757,6 +757,13 @@ interface Database { */ void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException; + /** + * Resets the transmission count, expiry time and ETA of all messages that + * are eligible to be sent to the given contact. This includes messages that + * have already been sent and are not yet due for retransmission. + */ + void resetUnackedMessagesToSend(T txn, ContactId c) throws DbException; + /** * Sets the cleanup timer duration for the given message. This does not * start the message's cleanup timer. @@ -845,8 +852,8 @@ interface Database { * of the given message with respect to the given contact, using the latency * of the transport over which it was sent. */ - void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, long maxLatency) - throws DbException; + void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, + long maxLatency) throws DbException; /** * Stores the given transport keys, deleting any keys they have replaced. 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 4cbf4b865..b137fcfc3 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 @@ -750,6 +750,15 @@ class DatabaseComponentImpl implements DatabaseComponent { return db.getUnackedMessagesToSend(txn, c); } + @Override + public void resetUnackedMessagesToSend(Transaction transaction, ContactId c) + throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.resetUnackedMessagesToSend(txn, c); + } + @Override public long getUnackedMessageBytesToSend(Transaction transaction, ContactId c) throws DbException { 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 7f236eda2..f2b71dda9 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 @@ -3290,6 +3290,29 @@ abstract class JdbcDatabase implements Database { } } + @Override + public void resetUnackedMessagesToSend(Connection txn, ContactId c) + throws DbException { + PreparedStatement ps = null; + try { + String sql = "UPDATE statuses SET expiry = 0, txCount = 0, eta = 0" + + " WHERE contactId = ? AND state = ?" + + " AND groupShared = TRUE AND messageShared = TRUE" + + " AND deleted = FALSE AND seen = FALSE"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setInt(2, DELIVERED.getValue()); + int affected = ps.executeUpdate(); + if (affected < 0) { + throw new DbStateException(); + } + ps.close(); + } catch (SQLException e) { + tryToClose(ps, LOG, WARNING); + throw new DbException(e); + } + } + @Override public void setCleanupTimerDuration(Connection txn, MessageId m, long duration) throws DbException { 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 649b09369..ae344fa05 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 @@ -2197,6 +2197,55 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { db.close(); } + @Test + public void testResetRetransmissionTimes() throws Exception { + long now = System.currentTimeMillis(); + AtomicLong time = new AtomicLong(now); + Database db = + open(false, new TestMessageFactory(), new SettableClock(time)); + Connection txn = db.startTransaction(); + + // Add a contact, a shared group and a shared message + db.addIdentity(txn, identity); + assertEquals(contactId, + db.addContact(txn, author, localAuthor.getId(), null, true)); + db.addGroup(txn, group); + db.addGroupVisibility(txn, contactId, groupId, true); + db.addMessage(txn, message, DELIVERED, true, false, null); + + // Time: now + // Retrieve the message from the database + Collection ids = db.getMessagesToSend(txn, contactId, + ONE_MEGABYTE, MAX_LATENCY); + assertEquals(singletonList(messageId), ids); + + // Time: now + // Mark the message as sent + db.updateExpiryTimeAndEta(txn, contactId, messageId, MAX_LATENCY); + + // The message should expire after 2 * MAX_LATENCY + assertEquals(now + MAX_LATENCY * 2, db.getNextSendTime(txn, contactId)); + + // Time: now + MAX_LATENCY * 2 - 1 + // The message should not yet be sendable + time.set(now + MAX_LATENCY * 2 - 1); + ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); + assertTrue(ids.isEmpty()); + + // 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 + ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY); + assertFalse(ids.isEmpty()); + + db.commitTransaction(txn); + db.close(); + } + @Test public void testCompactionTime() throws Exception { MessageFactory messageFactory = new TestMessageFactory();