diff --git a/src/main/java/com/pubnub/api/PNConfiguration.java b/src/main/java/com/pubnub/api/PNConfiguration.java index e646c07d6..09cc16cd7 100644 --- a/src/main/java/com/pubnub/api/PNConfiguration.java +++ b/src/main/java/com/pubnub/api/PNConfiguration.java @@ -38,6 +38,7 @@ public class PNConfiguration { private static final int SUBSCRIBE_TIMEOUT = 310; private static final int CONNECT_TIMEOUT = 5; private static final int FILE_MESSAGE_PUBLISH_RETRY_LIMIT = 5; + private static final int MAXIMUM_RECONNECTION_RETRIES = 10; @Getter private SSLSocketFactory sslSocketFactory; @@ -201,7 +202,9 @@ public void setUserId(@NotNull UserId userId) { private PNReconnectionPolicy reconnectionPolicy; /** - * Set how many times the reconneciton manager will try to connect before giving app + * Set how many times the reconnection manager will try to connect before giving up. + * Max allowed value is 10. + * Default is 10. */ @Setter private int maximumReconnectionRetries; @@ -284,7 +287,7 @@ public PNConfiguration(@NotNull UserId userId) throws PubNubException { startSubscriberThread = true; - maximumReconnectionRetries = -1; + maximumReconnectionRetries = MAXIMUM_RECONNECTION_RETRIES; dedupOnSubscribe = false; suppressLeaveEvents = false; diff --git a/src/main/java/com/pubnub/api/managers/DelayedReconnectionManager.java b/src/main/java/com/pubnub/api/managers/DelayedReconnectionManager.java index 7a84b8599..63f6274ed 100644 --- a/src/main/java/com/pubnub/api/managers/DelayedReconnectionManager.java +++ b/src/main/java/com/pubnub/api/managers/DelayedReconnectionManager.java @@ -5,17 +5,21 @@ import com.pubnub.api.enums.PNReconnectionPolicy; import lombok.extern.slf4j.Slf4j; +import java.text.DecimalFormat; +import java.util.Random; import java.util.Timer; import java.util.TimerTask; @Slf4j public class DelayedReconnectionManager { private static final int DELAY_SECONDS = 3; + private static final int MAX_RANDOM = 3; private static final int MILLISECONDS = 1000; private final PNReconnectionPolicy pnReconnectionPolicy; private ReconnectionCallback callback; private PubNub pubnub; + private Random random = new Random(); /** * Timer for heartbeat operations. @@ -34,12 +38,13 @@ public void scheduleDelayedReconnection() { } timer = new Timer("Delayed Reconnection Manager timer", true); + int effectiveDelayInMilliSeconds = (int) ((DELAY_SECONDS + getRandomDelay()) * MILLISECONDS); timer.schedule(new TimerTask() { @Override public void run() { callTime(); } - }, DELAY_SECONDS * MILLISECONDS); + }, effectiveDelayInMilliSeconds); } public void setReconnectionListener(ReconnectionCallback reconnectionCallback) { @@ -61,8 +66,19 @@ private boolean isReconnectionPolicyUndefined() { return false; } + private double getRandomDelay() { + double randomDelay = MAX_RANDOM * random.nextDouble(); + randomDelay = roundTo3DecimalPlaces(randomDelay); + return randomDelay; + } + private void callTime() { stop(); callback.onReconnection(); } + + private double roundTo3DecimalPlaces(double value) { + DecimalFormat decimalFormat = new DecimalFormat("#.###"); + return Double.parseDouble(decimalFormat.format(value)); + } } diff --git a/src/main/java/com/pubnub/api/managers/ReconnectionManager.java b/src/main/java/com/pubnub/api/managers/ReconnectionManager.java index 7a1cc4858..6f866b4cf 100644 --- a/src/main/java/com/pubnub/api/managers/ReconnectionManager.java +++ b/src/main/java/com/pubnub/api/managers/ReconnectionManager.java @@ -9,7 +9,9 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import java.text.DecimalFormat; import java.util.Calendar; +import java.util.Random; import java.util.Timer; import java.util.TimerTask; @@ -18,10 +20,12 @@ public class ReconnectionManager { private static final int LINEAR_INTERVAL = 3; - private static final int MIN_EXPONENTIAL_BACKOFF = 1; + private static final int MAX_RANDOM = 3; + private static final int MIN_EXPONENTIAL_BACKOFF = 2; private static final int MAX_EXPONENTIAL_BACKOFF = 32; private static final int MILLISECONDS = 1000; + public static final int MAXIMUM_RECONNECTION_RETRIES_DEFAULT = 10; private ReconnectionCallback callback; private PubNub pubnub; @@ -31,6 +35,7 @@ public class ReconnectionManager { private PNReconnectionPolicy pnReconnectionPolicy; private int maxConnectionRetries; + private Random random = new Random(); /** * Timer for heartbeat operations. @@ -40,7 +45,16 @@ public class ReconnectionManager { public ReconnectionManager(PubNub pubnub) { this.pubnub = pubnub; this.pnReconnectionPolicy = pubnub.getConfiguration().getReconnectionPolicy(); - this.maxConnectionRetries = pubnub.getConfiguration().getMaximumReconnectionRetries(); + this.maxConnectionRetries = getMaximumReconnectionRetries(); + } + + private int getMaximumReconnectionRetries() { + int maximumReconnectionRetries = pubnub.getConfiguration().getMaximumReconnectionRetries(); + if (maximumReconnectionRetries < 0 || maximumReconnectionRetries > MAXIMUM_RECONNECTION_RETRIES_DEFAULT) { + maximumReconnectionRetries = MAXIMUM_RECONNECTION_RETRIES_DEFAULT; + } + log.debug("maximumReconnectionRetries is: " + maximumReconnectionRetries); + return maximumReconnectionRetries; } public void setReconnectionListener(ReconnectionCallback reconnectionCallback) { @@ -55,10 +69,10 @@ public void startPolling() { exponentialMultiplier = 1; failedCalls = 0; - registerHeartbeatTimer(); + registerRetryTimer(); } - private void registerHeartbeatTimer() { + private void registerRetryTimer() { // make sure only one timer is running at a time. stopHeartbeatTimer(); @@ -66,7 +80,7 @@ private void registerHeartbeatTimer() { return; } - if (maxConnectionRetries != -1 && failedCalls >= maxConnectionRetries) { // _what's -1? + if (failedCalls >= maxConnectionRetries) { callback.onMaxReconnectionExhaustion(); return; } @@ -78,10 +92,10 @@ private void registerHeartbeatTimer() { public void run() { callTime(); } - }, getNextInterval() * MILLISECONDS); + }, getNextIntervalInMilliSeconds()); } - int getNextInterval() { + int getNextIntervalInMilliSeconds() { int timerInterval = LINEAR_INTERVAL; failedCalls++; @@ -95,16 +109,26 @@ int getNextInterval() { } else if (timerInterval < 1) { timerInterval = MIN_EXPONENTIAL_BACKOFF; } - log.debug("timerInterval = " + timerInterval + " at: " + Calendar.getInstance().getTime().toString()); + timerInterval = (int) ((timerInterval + getRandomDelay()) * MILLISECONDS); + log.debug("timerInterval = " + timerInterval + "ms at: " + Calendar.getInstance().getTime().toString()); } if (pnReconnectionPolicy == PNReconnectionPolicy.LINEAR) { - timerInterval = LINEAR_INTERVAL; + timerInterval = (int) ((LINEAR_INTERVAL + getRandomDelay()) * MILLISECONDS); } - return timerInterval; } + private double getRandomDelay() { + double randomDelay = MAX_RANDOM * random.nextDouble(); + return roundTo3DecimalPlaces(randomDelay); + } + + private double roundTo3DecimalPlaces(double value) { + DecimalFormat decimalFormat = new DecimalFormat("#.###"); + return Double.parseDouble(decimalFormat.format(value)); + } + private void stopHeartbeatTimer() { if (timer != null) { timer.cancel(); @@ -121,7 +145,7 @@ public void onResponse(PNTimeResult result, @NotNull PNStatus status) { callback.onReconnection(); } else { log.debug("callTime() at: " + Calendar.getInstance().getTime().toString()); - registerHeartbeatTimer(); + registerRetryTimer(); } } }); diff --git a/src/test/java/com/pubnub/api/managers/ReconnectionManagerTest.java b/src/test/java/com/pubnub/api/managers/ReconnectionManagerTest.java index 647e14bbf..2738eed96 100644 --- a/src/test/java/com/pubnub/api/managers/ReconnectionManagerTest.java +++ b/src/test/java/com/pubnub/api/managers/ReconnectionManagerTest.java @@ -9,7 +9,6 @@ import java.util.UUID; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class ReconnectionManagerTest { @@ -20,10 +19,11 @@ public void reconnectionIntervalsEqualsForLinear() throws PubNubException { pnConfiguration.setReconnectionPolicy(PNReconnectionPolicy.LINEAR); final ReconnectionManager reconnectionManagerUnderTest = new ReconnectionManager(pubNub); - int firstInterval = reconnectionManagerUnderTest.getNextInterval(); - int secondInterval = reconnectionManagerUnderTest.getNextInterval(); + int firstInterval = reconnectionManagerUnderTest.getNextIntervalInMilliSeconds(); + int secondInterval = reconnectionManagerUnderTest.getNextIntervalInMilliSeconds(); - assertEquals(secondInterval, firstInterval); + assertTrue(firstInterval >= 3000 && firstInterval <= 6000); + assertTrue(secondInterval >= 3000 && secondInterval <= 6000); } @Test @@ -33,9 +33,9 @@ public void reconnectionIntervalsIncreaseForExponential() throws PubNubException PubNub pubNub = new PubNub(pnConfiguration); final ReconnectionManager reconnectionManagerUnderTest = new ReconnectionManager(pubNub); - int firstInterval = reconnectionManagerUnderTest.getNextInterval(); - int secondInterval = reconnectionManagerUnderTest.getNextInterval(); - int thirdInterval = reconnectionManagerUnderTest.getNextInterval(); + int firstInterval = reconnectionManagerUnderTest.getNextIntervalInMilliSeconds(); + int secondInterval = reconnectionManagerUnderTest.getNextIntervalInMilliSeconds(); + int thirdInterval = reconnectionManagerUnderTest.getNextIntervalInMilliSeconds(); assertTrue(firstInterval < secondInterval); assertTrue(secondInterval < thirdInterval);