From 3af077a4d8422f75b82a7787f7ab398591cae1c4 Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Wed, 6 Feb 2013 23:51:23 +0000 Subject: [PATCH] Refactored exponential backoff code out of JdbcDatabase and added tests. --- .../net/sf/briar/db/ExponentialBackoff.java | 30 +++++++++ .../src/net/sf/briar/db/JdbcDatabase.java | 14 +--- briar-tests/build.xml | 1 + .../sf/briar/db/ExponentialBackoffTest.java | 64 +++++++++++++++++++ 4 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 briar-core/src/net/sf/briar/db/ExponentialBackoff.java create mode 100644 briar-tests/src/net/sf/briar/db/ExponentialBackoffTest.java diff --git a/briar-core/src/net/sf/briar/db/ExponentialBackoff.java b/briar-core/src/net/sf/briar/db/ExponentialBackoff.java new file mode 100644 index 0000000000..4fd5e1045d --- /dev/null +++ b/briar-core/src/net/sf/briar/db/ExponentialBackoff.java @@ -0,0 +1,30 @@ +package net.sf.briar.db; + +class ExponentialBackoff { + + /** + * Returns the expiry time of a packet transmitted at time <tt>now</tt> + * over a transport with maximum latency <tt>maxLatency</tt>, where the + * packet has previously been transmitted <tt>txCount</tt> times. All times + * are in milliseconds. The expiry time is + * <tt>now + maxLatency * 2 ^ (txCount + 1)</tt>, so the interval between + * transmissions increases exponentially. If the expiry time would + * be greater than Long.MAX_VALUE, Long.MAX_VALUE is returned. + */ + static long calculateExpiry(long now, long maxLatency, int txCount) { + if(now < 0) throw new IllegalArgumentException(); + if(maxLatency <= 0) throw new IllegalArgumentException(); + if(txCount < 0) throw new IllegalArgumentException(); + // The maximum round-trip time is twice the maximum latency + long roundTrip = maxLatency * 2; + if(roundTrip < 0) return Long.MAX_VALUE; + // The interval between transmissions is roundTrip * 2 ^ txCount + for(int i = 0; i < txCount; i++) { + roundTrip <<= 1; + if(roundTrip < 0) return Long.MAX_VALUE; + } + // The expiry time is the current time plus the interval + long expiry = now + roundTrip; + return expiry < 0 ? Long.MAX_VALUE : expiry; + } +} diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index 2725a22486..dfa50aef8f 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -5,6 +5,7 @@ import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static net.sf.briar.api.Rating.UNRATED; import static net.sf.briar.db.DatabaseConstants.RETENTION_MODULUS; +import static net.sf.briar.db.ExponentialBackoff.calculateExpiry; import java.io.File; import java.io.FileNotFoundException; @@ -2877,17 +2878,4 @@ abstract class JdbcDatabase implements Database<Connection> { throw new DbException(e); } } - - // FIXME: Refactor the exponential backoff logic into a separate class - private long calculateExpiry(long now, long maxLatency, int txCount) { - long roundTrip = maxLatency * 2; - if(roundTrip < 0) return Long.MAX_VALUE; - for(int i = 0; i < txCount; i++) { - roundTrip <<= 1; - if(roundTrip < 0) return Long.MAX_VALUE; - } - long expiry = now + roundTrip; - if(expiry < 0) return Long.MAX_VALUE; - return expiry; - } } diff --git a/briar-tests/build.xml b/briar-tests/build.xml index 62f7292bf1..45b174c2a8 100644 --- a/briar-tests/build.xml +++ b/briar-tests/build.xml @@ -77,6 +77,7 @@ <test name='net.sf.briar.db.BasicH2Test'/> <test name='net.sf.briar.db.DatabaseCleanerImplTest'/> <test name='net.sf.briar.db.DatabaseComponentImplTest'/> + <test name='net.sf.briar.db.ExponentialBackoffTest'/> <test name='net.sf.briar.lifecycle.ShutdownManagerImplTest'/> <test name='net.sf.briar.lifecycle.WindowsShutdownManagerImplTest'/> <test name='net.sf.briar.messaging.ConstantsTest'/> diff --git a/briar-tests/src/net/sf/briar/db/ExponentialBackoffTest.java b/briar-tests/src/net/sf/briar/db/ExponentialBackoffTest.java new file mode 100644 index 0000000000..f2067afe73 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/ExponentialBackoffTest.java @@ -0,0 +1,64 @@ +package net.sf.briar.db; + +import net.sf.briar.BriarTestCase; + +import org.junit.Test; + +public class ExponentialBackoffTest extends BriarTestCase { + + private static final int ONE_HOUR = 60 * 60 * 1000; + + @Test + public void testFirstIntervalEqualsRoundTripTime() { + long first = ExponentialBackoff.calculateExpiry(0, ONE_HOUR, 0); + assertEquals(2 * ONE_HOUR, first); + } + + @Test + public void testIntervalsIncreaseExponentially() { + long first = ExponentialBackoff.calculateExpiry(0, ONE_HOUR, 0); + long second = ExponentialBackoff.calculateExpiry(0, ONE_HOUR, 1); + long third = ExponentialBackoff.calculateExpiry(0, ONE_HOUR, 2); + long fourth = ExponentialBackoff.calculateExpiry(0, ONE_HOUR, 3); + assertEquals(third, fourth / 2); + assertEquals(second, third / 2); + assertEquals(first, second / 2); + } + + @Test + public void testIntervalIsAddedToCurrentTime() { + long now = System.currentTimeMillis(); + long fromZero = ExponentialBackoff.calculateExpiry(0, ONE_HOUR, 0); + long fromNow = ExponentialBackoff.calculateExpiry(now, ONE_HOUR, 0); + assertEquals(now, fromNow - fromZero); + } + + @Test + public void testRoundTripTimeOverflow() { + long maxLatency = Long.MAX_VALUE / 2 + 1; // RTT will overflow + long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); + assertEquals(Long.MAX_VALUE, expiry); // Overflow caught + } + + @Test + public void testTransmissionCountOverflow() { + long maxLatency = (Long.MAX_VALUE - 1) / 2; // RTT will not overflow + long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); + assertEquals(Long.MAX_VALUE - 1, expiry); // No overflow + expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 1); + assertEquals(Long.MAX_VALUE, expiry); // Overflow caught + expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 2); + assertEquals(Long.MAX_VALUE, expiry); // Overflow caught + } + + @Test + public void testCurrentTimeOverflow() { + long maxLatency = (Long.MAX_VALUE - 1) / 2; // RTT will not overflow + long expiry = ExponentialBackoff.calculateExpiry(0, maxLatency, 0); + assertEquals(Long.MAX_VALUE - 1, expiry); // No overflow + expiry = ExponentialBackoff.calculateExpiry(1, maxLatency, 0); + assertEquals(Long.MAX_VALUE, expiry); // No overflow + expiry = ExponentialBackoff.calculateExpiry(2, maxLatency, 0); + assertEquals(Long.MAX_VALUE, expiry); // Overflow caught + } +} -- GitLab