fix: use streaming retry settings for ResumableStreamIterator (#49) · googleapis/java-spanner@63b33e9 (original) (raw)
`@@ -17,15 +17,19 @@
`
17
17
`package com.google.cloud.spanner;
`
18
18
``
19
19
`import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException;
`
``
20
`+
import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerExceptionForCancellation;
`
20
21
`import static com.google.common.base.Preconditions.checkArgument;
`
21
22
`import static com.google.common.base.Preconditions.checkNotNull;
`
22
23
`import static com.google.common.base.Preconditions.checkState;
`
23
24
``
24
25
`import com.google.api.client.util.BackOff;
`
``
26
`+
import com.google.api.client.util.ExponentialBackOff;
`
``
27
`+
import com.google.api.gax.retrying.RetrySettings;
`
25
28
`import com.google.cloud.ByteArray;
`
26
29
`import com.google.cloud.Date;
`
27
30
`import com.google.cloud.Timestamp;
`
28
31
`import com.google.cloud.spanner.spi.v1.SpannerRpc;
`
``
32
`+
import com.google.cloud.spanner.v1.stub.SpannerStubSettings;
`
29
33
`import com.google.common.annotations.VisibleForTesting;
`
30
34
`import com.google.common.base.Function;
`
31
35
`import com.google.common.collect.AbstractIterator;
`
`@@ -46,6 +50,7 @@
`
46
50
`import io.opencensus.trace.Span;
`
47
51
`import io.opencensus.trace.Tracer;
`
48
52
`import io.opencensus.trace.Tracing;
`
``
53
`+
import java.io.IOException;
`
49
54
`import java.io.Serializable;
`
50
55
`import java.util.AbstractList;
`
51
56
`import java.util.ArrayList;
`
`@@ -55,7 +60,10 @@
`
55
60
`import java.util.LinkedList;
`
56
61
`import java.util.List;
`
57
62
`import java.util.concurrent.BlockingQueue;
`
``
63
`+
import java.util.concurrent.CountDownLatch;
`
``
64
`+
import java.util.concurrent.Executor;
`
58
65
`import java.util.concurrent.LinkedBlockingQueue;
`
``
66
`+
import java.util.concurrent.TimeUnit;
`
59
67
`import java.util.logging.Level;
`
60
68
`import java.util.logging.Logger;
`
61
69
`import javax.annotation.Nullable;
`
`@@ -820,8 +828,10 @@ void setCall(SpannerRpc.StreamingCall call) {
`
820
828
`@VisibleForTesting
`
821
829
`abstract static class ResumableStreamIterator extends AbstractIterator
`
822
830
`implements CloseableIterator {
`
``
831
`+
private static final RetrySettings STREAMING_RETRY_SETTINGS =
`
``
832
`+
SpannerStubSettings.newBuilder().executeStreamingSqlSettings().getRetrySettings();
`
823
833
`private static final Logger logger = Logger.getLogger(ResumableStreamIterator.class.getName());
`
824
``
`-
private final BackOff backOff = SpannerImpl.newBackOff();
`
``
834
`+
private final BackOff backOff = newBackOff();
`
825
835
`private final LinkedList buffer = new LinkedList<>();
`
826
836
`private final int maxBufferSize;
`
827
837
`private final Span span;
`
`@@ -841,6 +851,70 @@ protected ResumableStreamIterator(int maxBufferSize, String streamName, Span par
`
841
851
`this.span = tracer.spanBuilderWithExplicitParent(streamName, parent).startSpan();
`
842
852
` }
`
843
853
``
``
854
`+
private static ExponentialBackOff newBackOff() {
`
``
855
`+
return new ExponentialBackOff.Builder()
`
``
856
`+
.setMultiplier(STREAMING_RETRY_SETTINGS.getRetryDelayMultiplier())
`
``
857
`+
.setInitialIntervalMillis(
`
``
858
`+
(int) STREAMING_RETRY_SETTINGS.getInitialRetryDelay().toMillis())
`
``
859
`+
.setMaxIntervalMillis((int) STREAMING_RETRY_SETTINGS.getMaxRetryDelay().toMillis())
`
``
860
`+
.setMaxElapsedTimeMillis(Integer.MAX_VALUE) // Prevent Backoff.STOP from getting returned.
`
``
861
`+
.build();
`
``
862
`+
}
`
``
863
+
``
864
`+
private static void backoffSleep(Context context, BackOff backoff) throws SpannerException {
`
``
865
`+
backoffSleep(context, nextBackOffMillis(backoff));
`
``
866
`+
}
`
``
867
+
``
868
`+
private static long nextBackOffMillis(BackOff backoff) throws SpannerException {
`
``
869
`+
try {
`
``
870
`+
return backoff.nextBackOffMillis();
`
``
871
`+
} catch (IOException e) {
`
``
872
`+
throw newSpannerException(ErrorCode.INTERNAL, e.getMessage(), e);
`
``
873
`+
}
`
``
874
`+
}
`
``
875
+
``
876
`+
private static void backoffSleep(Context context, long backoffMillis) throws SpannerException {
`
``
877
`+
tracer
`
``
878
`+
.getCurrentSpan()
`
``
879
`+
.addAnnotation(
`
``
880
`+
"Backing off",
`
``
881
`+
ImmutableMap.of("Delay", AttributeValue.longAttributeValue(backoffMillis)));
`
``
882
`+
final CountDownLatch latch = new CountDownLatch(1);
`
``
883
`+
final Context.CancellationListener listener =
`
``
884
`+
new Context.CancellationListener() {
`
``
885
`+
@Override
`
``
886
`+
public void cancelled(Context context) {
`
``
887
`+
// Wakeup on cancellation / DEADLINE_EXCEEDED.
`
``
888
`+
latch.countDown();
`
``
889
`+
}
`
``
890
`+
};
`
``
891
+
``
892
`+
context.addListener(listener, DirectExecutor.INSTANCE);
`
``
893
`+
try {
`
``
894
`+
if (backoffMillis == BackOff.STOP) {
`
``
895
`+
// Highly unlikely but we handle it just in case.
`
``
896
`+
backoffMillis = STREAMING_RETRY_SETTINGS.getMaxRetryDelay().toMillis();
`
``
897
`+
}
`
``
898
`+
if (latch.await(backoffMillis, TimeUnit.MILLISECONDS)) {
`
``
899
`+
// Woken by context cancellation.
`
``
900
`+
throw newSpannerExceptionForCancellation(context, null);
`
``
901
`+
}
`
``
902
`+
} catch (InterruptedException interruptExcept) {
`
``
903
`+
throw newSpannerExceptionForCancellation(context, interruptExcept);
`
``
904
`+
} finally {
`
``
905
`+
context.removeListener(listener);
`
``
906
`+
}
`
``
907
`+
}
`
``
908
+
``
909
`+
private enum DirectExecutor implements Executor {
`
``
910
`+
INSTANCE;
`
``
911
+
``
912
`+
@Override
`
``
913
`+
public void execute(Runnable command) {
`
``
914
`+
command.run();
`
``
915
`+
}
`
``
916
`+
}
`
``
917
+
844
918
`abstract CloseableIterator startStream(@Nullable ByteString resumeToken);
`
845
919
``
846
920
`@Override
`
`@@ -915,9 +989,9 @@ protected PartialResultSet computeNext() {
`
915
989
`try (Scope s = tracer.withSpan(span)) {
`
916
990
`long delay = e.getRetryDelayInMillis();
`
917
991
`if (delay != -1) {
`
918
``
`-
SpannerImpl.backoffSleep(context, delay);
`
``
992
`+
backoffSleep(context, delay);
`
919
993
` } else {
`
920
``
`-
SpannerImpl.backoffSleep(context, backOff);
`
``
994
`+
backoffSleep(context, backOff);
`
921
995
` }
`
922
996
` }
`
923
997
`continue;
`