issue #707 ExponentialBackOff refactored to interface for custom implementation

This commit is contained in:
monkey 2021-04-24 20:20:54 +08:00 committed by Ruben Bermudez
parent f437fd885c
commit e38c3b0ba9
6 changed files with 133 additions and 24 deletions

17
pom.xml
View File

@ -178,6 +178,23 @@
</dependency> </dependency>
</dependencies> </dependencies>
<!-- <reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version>
<reportSets>
<reportSet>
<reports>
<report>checkstyle</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>-->
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -0,0 +1,19 @@
package org.telegram.telegrambots.meta.generics;
/**
* @author Calvin
* @version 1.0
* Interface for backoff retries
*/
public interface BackOff {
/**
* Should be able to reset to the starting
*/
void reset();
/**
* specify the next backoff interval in milliseconds
*/
long nextBackOffMillis();
}

View File

@ -5,7 +5,7 @@ import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.telegram.telegrambots.meta.ApiConstants; import org.telegram.telegrambots.meta.ApiConstants;
import org.telegram.telegrambots.meta.generics.BotOptions; import org.telegram.telegrambots.meta.generics.BotOptions;
import org.telegram.telegrambots.updatesreceivers.ExponentialBackOff; import org.telegram.telegrambots.meta.generics.BackOff;
import java.util.List; import java.util.List;
@ -19,7 +19,7 @@ public class DefaultBotOptions implements BotOptions {
private int maxThreads; ///< Max number of threads used for async methods executions (default 1) private int maxThreads; ///< Max number of threads used for async methods executions (default 1)
private RequestConfig requestConfig; private RequestConfig requestConfig;
private volatile HttpContext httpContext; private volatile HttpContext httpContext;
private ExponentialBackOff exponentialBackOff; private BackOff backOff;
private Integer maxWebhookConnections; private Integer maxWebhookConnections;
private String baseUrl; private String baseUrl;
private List<String> allowedUpdates; private List<String> allowedUpdates;
@ -91,23 +91,23 @@ public class DefaultBotOptions implements BotOptions {
} }
/** /**
* @implSpec Default implementation assumes no proxy is needed and sets a 75secs timoute
* @param requestConfig Request config to be used in all Http requests * @param requestConfig Request config to be used in all Http requests
* @implSpec Default implementation assumes no proxy is needed and sets a 75secs timoute
*/ */
public void setRequestConfig(RequestConfig requestConfig) { public void setRequestConfig(RequestConfig requestConfig) {
this.requestConfig = requestConfig; this.requestConfig = requestConfig;
} }
public ExponentialBackOff getExponentialBackOff() { public BackOff getBackOff() {
return exponentialBackOff; return backOff;
} }
/** /**
* @param BackOff backOff to be used when long polling fails
* @implSpec Default implementation assumes starting at 500ms and max time of 60 minutes * @implSpec Default implementation assumes starting at 500ms and max time of 60 minutes
* @param exponentialBackOff ExponentialBackOff to be used when long polling fails
*/ */
public void setExponentialBackOff(ExponentialBackOff exponentialBackOff) { public void setBackOff(BackOff BackOff) {
this.exponentialBackOff = exponentialBackOff; this.backOff = BackOff;
} }
public ProxyType getProxyType() { public ProxyType getProxyType() {

View File

@ -16,11 +16,7 @@ import org.telegram.telegrambots.facilities.TelegramHttpClientBuilder;
import org.telegram.telegrambots.meta.api.methods.updates.GetUpdates; import org.telegram.telegrambots.meta.api.methods.updates.GetUpdates;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
import org.telegram.telegrambots.meta.generics.BotOptions; import org.telegram.telegrambots.meta.generics.*;
import org.telegram.telegrambots.meta.generics.BotSession;
import org.telegram.telegrambots.meta.generics.LongPollingBot;
import org.telegram.telegrambots.meta.generics.UpdatesHandler;
import org.telegram.telegrambots.meta.generics.UpdatesReader;
import java.io.IOException; import java.io.IOException;
import java.io.InvalidObjectException; import java.io.InvalidObjectException;
@ -142,7 +138,7 @@ public class DefaultBotSession implements BotSession {
private final UpdatesSupplier updatesSupplier; private final UpdatesSupplier updatesSupplier;
private final Object lock; private final Object lock;
private CloseableHttpClient httpclient; private CloseableHttpClient httpclient;
private ExponentialBackOff exponentialBackOff; private BackOff backOff;
private RequestConfig requestConfig; private RequestConfig requestConfig;
public ReaderThread(UpdatesSupplier updatesSupplier, Object lock) { public ReaderThread(UpdatesSupplier updatesSupplier, Object lock) {
@ -154,10 +150,11 @@ public class DefaultBotSession implements BotSession {
public synchronized void start() { public synchronized void start() {
httpclient = TelegramHttpClientBuilder.build(options); httpclient = TelegramHttpClientBuilder.build(options);
requestConfig = options.getRequestConfig(); requestConfig = options.getRequestConfig();
exponentialBackOff = options.getExponentialBackOff(); backOff = options.getBackOff();
if (exponentialBackOff == null) { // fall back to default exponential backoff strategy if no backoff specified
exponentialBackOff = new ExponentialBackOff(); if (backOff == null) {
backOff = new ExponentialBackOff();
} }
if (requestConfig == null) { if (requestConfig == null) {
@ -215,7 +212,7 @@ public class DefaultBotSession implements BotSession {
log.error(global.getLocalizedMessage(), global); log.error(global.getLocalizedMessage(), global);
try { try {
synchronized (lock) { synchronized (lock) {
lock.wait(exponentialBackOff.nextBackOffMillis()); lock.wait(backOff.nextBackOffMillis());
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
if (!running.get()) { if (!running.get()) {
@ -261,7 +258,7 @@ public class DefaultBotSession implements BotSession {
} else { } else {
try { try {
List<Update> updates = request.deserializeResponse(responseContent); List<Update> updates = request.deserializeResponse(responseContent);
exponentialBackOff.reset(); backOff.reset();
return updates; return updates;
} catch (JSONException e) { } catch (JSONException e) {
log.error("Error deserializing update: " + responseContent, e); log.error("Error deserializing update: " + responseContent, e);

View File

@ -14,6 +14,7 @@
package org.telegram.telegrambots.updatesreceivers; package org.telegram.telegrambots.updatesreceivers;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.telegram.telegrambots.meta.generics.BackOff;
/** /**
* Implementation of BackOff that increases the back off period for each retry attempt using * Implementation of BackOff that increases the back off period for each retry attempt using
@ -68,7 +69,7 @@ import com.google.common.base.Preconditions;
* @since 1.15 * @since 1.15
* @author Ravi Mistry * @author Ravi Mistry
*/ */
public class ExponentialBackOff { public class ExponentialBackOff implements BackOff {
/** The default initial interval value in milliseconds (0.5 seconds). */ /** The default initial interval value in milliseconds (0.5 seconds). */
private static final int DEFAULT_INITIAL_INTERVAL_MILLIS = 500; private static final int DEFAULT_INITIAL_INTERVAL_MILLIS = 500;
@ -82,7 +83,7 @@ public class ExponentialBackOff {
private static final double DEFAULT_MULTIPLIER = 1.5; private static final double DEFAULT_MULTIPLIER = 1.5;
/** The default maximum back off time in milliseconds (15 minutes). */ /** The default maximum back off time in milliseconds (15 minutes). */
private static final int DEFAULT_MAX_INTERVAL_MILLIS = 30000; private static final int DEFAULT_MAX_INTERVAL_MILLIS = 900000;
/** The default maximum elapsed time in milliseconds (60 minutes). */ /** The default maximum elapsed time in milliseconds (60 minutes). */
private static final int DEFAULT_MAX_ELAPSED_TIME_MILLIS = 3600000; private static final int DEFAULT_MAX_ELAPSED_TIME_MILLIS = 3600000;
@ -161,7 +162,8 @@ public class ExponentialBackOff {
} }
/** Sets the interval back to the initial retry interval and restarts the timer. */ /** Sets the interval back to the initial retry interval and restarts the timer. */
final void reset() { @Override
public void reset() {
currentIntervalMillis = initialIntervalMillis; currentIntervalMillis = initialIntervalMillis;
startTimeNanos = nanoTime(); startTimeNanos = nanoTime();
} }
@ -178,7 +180,8 @@ public class ExponentialBackOff {
* Subclasses may override if a different algorithm is required. * Subclasses may override if a different algorithm is required.
* </p> * </p>
*/ */
long nextBackOffMillis() { @Override
public long nextBackOffMillis() {
// Make sure we have not gone over the maximum elapsed time. // Make sure we have not gone over the maximum elapsed time.
if (getElapsedTimeMillis() > maxElapsedTimeMillis) { if (getElapsedTimeMillis() > maxElapsedTimeMillis) {
return maxElapsedTimeMillis; return maxElapsedTimeMillis;
@ -274,7 +277,32 @@ public class ExponentialBackOff {
*/ */
int maxElapsedTimeMillis = DEFAULT_MAX_ELAPSED_TIME_MILLIS; int maxElapsedTimeMillis = DEFAULT_MAX_ELAPSED_TIME_MILLIS;
Builder() { public Builder() {
}
public Builder setInitialIntervalMillis(int initialIntervalMillis) {
this.initialIntervalMillis = initialIntervalMillis;
return this;
}
public Builder setRandomizationFactor(double randomizationFactor) {
this.randomizationFactor = randomizationFactor;
return this;
}
public Builder setMultiplier(double multiplier) {
this.multiplier = multiplier;
return this;
}
public Builder setMaxIntervalMillis(int maxIntervalMillis) {
this.maxIntervalMillis = maxIntervalMillis;
return this;
}
public Builder setMaxElapsedTimeMillis(int maxElapsedTimeMillis) {
this.maxElapsedTimeMillis = maxElapsedTimeMillis;
return this;
} }
/** Builds a new instance of {@link ExponentialBackOff}. */ /** Builds a new instance of {@link ExponentialBackOff}. */

View File

@ -15,9 +15,11 @@ import org.mockito.Mockito;
import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.generics.BackOff;
import org.telegram.telegrambots.meta.generics.LongPollingBot; import org.telegram.telegrambots.meta.generics.LongPollingBot;
import org.telegram.telegrambots.test.Fakes.FakeLongPollingBot; import org.telegram.telegrambots.test.Fakes.FakeLongPollingBot;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
import org.telegram.telegrambots.updatesreceivers.ExponentialBackOff;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -139,6 +141,30 @@ public class TestDefaultBotSession {
session.stop(); session.stop();
} }
@Test
public void testDefaultBotSessionWithCustomExponentialBackOff() {
ExponentialBackOff ex = new ExponentialBackOff.Builder()
.setInitialIntervalMillis(500)
.setRandomizationFactor(0.5)
.setMultiplier(1.5)
.setMaxIntervalMillis(900000)
.setMaxElapsedTimeMillis(3600000)
.build();
DefaultBotOptions options = new DefaultBotOptions();
options.setBackOff(ex);
DefaultBotSession session = new DefaultBotSession();
session.setOptions(options);
}
@Test
public void testDefaultBotSessionWithCustomConstantBackOff() {
DefaultBotOptions options = new DefaultBotOptions();
ConstantBackOff backOff = new ConstantBackOff(3000);
options.setBackOff(backOff);
DefaultBotSession session = new DefaultBotSession();
session.setOptions(options);
}
private Update[] createFakeUpdates(int count) { private Update[] createFakeUpdates(int count) {
return IntStream.range(0, count).mapToObj(x -> { return IntStream.range(0, count).mapToObj(x -> {
Update mock = Mockito.mock(Update.class); Update mock = Mockito.mock(Update.class);
@ -178,4 +204,26 @@ public class TestDefaultBotSession {
session.setOptions(new DefaultBotOptions()); session.setOptions(new DefaultBotOptions());
return session; return session;
} }
private static class ConstantBackOff implements BackOff {
private long backOffMillis;
ConstantBackOff() {
new ConstantBackOff(3000);
}
ConstantBackOff(long millis) {
this.backOffMillis = millis;
}
@Override
public void reset() {
}
@Override
public long nextBackOffMillis() {
return backOffMillis;
}
}
} }