Skip to content

Commit dff4ea3

Browse files
authored
pubsub: cap stream reconnect time (googleapis#2409)
Previously we exponentially grow stream reconnect backoff time without bound, so unlucky users will wait forever waiting for a connection. This commit caps the reconnect at 10s (the same as polling). Previously we only reset the polling time after the stream closes without error. This usually doesn't happen because the streams are long running and don't close until some error is encountered. This commit resets the polling time if we receive any response from the stream. Fixes googleapis#2406.
1 parent cd25927 commit dff4ea3

1 file changed

Lines changed: 11 additions & 4 deletions

File tree

‎google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/v1/StreamingSubscriberConnection.java‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.List;
3838
import java.util.concurrent.ScheduledExecutorService;
3939
import java.util.concurrent.TimeUnit;
40+
import java.util.concurrent.atomic.AtomicLong;
4041
import java.util.logging.Level;
4142
import java.util.logging.Logger;
4243
import javax.annotation.Nullable;
@@ -48,9 +49,11 @@ final class StreamingSubscriberConnection extends AbstractApiService implements
4849
Logger.getLogger(StreamingSubscriberConnection.class.getName());
4950

5051
private static final Duration INITIAL_CHANNEL_RECONNECT_BACKOFF = Duration.ofMillis(100);
52+
private static final Duration MAX_CHANNEL_RECONNECT_BACKOFF = Duration.ofSeconds(10);
5153
private static final int MAX_PER_REQUEST_CHANGES = 10000;
5254

53-
private Duration channelReconnectBackoff = INITIAL_CHANNEL_RECONNECT_BACKOFF;
55+
private final AtomicLong channelReconnectBackoffMillis =
56+
new AtomicLong(INITIAL_CHANNEL_RECONNECT_BACKOFF.toMillis());
5457

5558
private final SubscriberStub asyncStub;
5659

@@ -119,6 +122,7 @@ public void beforeStart(ClientCallStreamObserver<StreamingPullRequest> requestOb
119122

120123
@Override
121124
public void onNext(StreamingPullResponse response) {
125+
channelReconnectBackoffMillis.set(INITIAL_CHANNEL_RECONNECT_BACKOFF.toMillis());
122126
messageDispatcher.processReceivedMessages(
123127
response.getReceivedMessagesList(),
124128
new Runnable() {
@@ -170,7 +174,7 @@ public void onSuccess(@Nullable Void result) {
170174
if (!isAlive()) {
171175
return;
172176
}
173-
channelReconnectBackoff = INITIAL_CHANNEL_RECONNECT_BACKOFF;
177+
channelReconnectBackoffMillis.set(INITIAL_CHANNEL_RECONNECT_BACKOFF.toMillis());
174178
// The stream was closed. And any case we want to reopen it to continue receiving
175179
// messages.
176180
initialize();
@@ -185,8 +189,11 @@ public void onFailure(Throwable cause) {
185189
}
186190
logger.log(Level.WARNING, "Terminated streaming with exception", cause);
187191
if (StatusUtil.isRetryable(cause)) {
188-
long backoffMillis = channelReconnectBackoff.toMillis();
189-
channelReconnectBackoff = channelReconnectBackoff.plusMillis(backoffMillis);
192+
long backoffMillis = channelReconnectBackoffMillis.get();
193+
long newBackoffMillis =
194+
Math.min(backoffMillis * 2, MAX_CHANNEL_RECONNECT_BACKOFF.toMillis());
195+
channelReconnectBackoffMillis.set(newBackoffMillis);
196+
190197
executor.schedule(
191198
new Runnable() {
192199
@Override

0 commit comments

Comments
 (0)