Skip to content

Commit 9e0d3f7

Browse files
committed
refactor(gax): extract resumable upload coordinator and future helpers
1 parent 90be099 commit 9e0d3f7

4 files changed

Lines changed: 81 additions & 58 deletions

File tree

‎sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ResumableUploadChunkCoordinator.java‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ ApiFuture<ResponseT> getFuture() {
110110

111111
void start() {
112112
progressTracker.onStarted(uploadUrl, chunkSize);
113+
registerCancellationListener();
114+
try {
115+
buffer.fill();
116+
transmitChunk();
117+
} catch (Throwable t) {
118+
uploadResultFuture.setException(t);
119+
}
120+
}
121+
122+
private void registerCancellationListener() {
113123
uploadResultFuture.addListener(
114124
() -> {
115125
Future<?> inFlight = inFlightFuture;
@@ -118,12 +128,6 @@ void start() {
118128
}
119129
},
120130
MoreExecutors.directExecutor());
121-
try {
122-
buffer.fill();
123-
transmitChunk();
124-
} catch (Throwable t) {
125-
uploadResultFuture.setException(t);
126-
}
127131
}
128132

129133
private void transmitChunk() {

‎sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ResumableUploadFutureImpl.java‎

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ final class ResumableUploadFutureImpl<ResponseT> implements ResumableUploadFutur
8080
private final ApiCallContext callContext;
8181
private final ScheduledExecutorService executor;
8282
private final ExponentialRetryAlgorithm recoveryAlgorithm;
83-
private final ResumableUploadProgressTracker progressTracker =
84-
new ResumableUploadProgressTracker();
83+
private final ResumableUploadProgressTracker progressTracker;
8584
private final SettableApiFuture<ResponseT> resultFuture = SettableApiFuture.create();
8685

8786
private volatile @Nullable InputStream payload;
@@ -120,7 +119,8 @@ static <ResponseT> ResumableUploadFutureImpl<ResponseT> create(
120119
queryStatusCallable,
121120
options,
122121
clientContext,
123-
recoveryAlgorithm);
122+
recoveryAlgorithm,
123+
ResumableUploadProgressTracker.create());
124124
try {
125125
future.start(payloadSupplier);
126126
} catch (Throwable t) {
@@ -135,7 +135,8 @@ private ResumableUploadFutureImpl(
135135
UnaryCallable<QueryStatusRequest, QueryStatusResponse<ResponseT>> queryStatusCallable,
136136
ResumableUploadOptions options,
137137
ClientContext clientContext,
138-
ExponentialRetryAlgorithm recoveryAlgorithm) {
138+
ExponentialRetryAlgorithm recoveryAlgorithm,
139+
ResumableUploadProgressTracker progressTracker) {
139140
this.startFuture = checkNotNull(startFuture, "startFuture must not be null");
140141
this.uploadChunkCallable =
141142
checkNotNull(uploadChunkCallable, "uploadChunkCallable must not be null");
@@ -147,6 +148,7 @@ private ResumableUploadFutureImpl(
147148
this.callContext = clientContext.getDefaultCallContext();
148149
this.executor = checkNotNull(clientContext.getExecutor(), "executor must not be null");
149150
this.recoveryAlgorithm = checkNotNull(recoveryAlgorithm, "recoveryAlgorithm must not be null");
151+
this.progressTracker = checkNotNull(progressTracker, "progressTracker must not be null");
150152
this.inFlightFuture = startFuture;
151153
ResumableUploadProgressListener progressListener = options.getProgressListener();
152154
if (progressListener != null) {
@@ -155,10 +157,7 @@ private ResumableUploadFutureImpl(
155157
}
156158

157159
private void start(InputStreamSupplier payloadSupplier) throws IOException {
158-
Duration timeout = firstNonNull(options.getGlobalTimeout(), DEFAULT_GLOBAL_TIMEOUT);
159-
ScheduledFuture<?> timeoutFuture =
160-
executor.schedule(this::onTimeout, timeout.toMillis(), TimeUnit.MILLISECONDS);
161-
resultFuture.addListener(() -> timeoutFuture.cancel(false), MoreExecutors.directExecutor());
160+
setUpGlobalTimeout();
162161
this.payload = checkNotNull(payloadSupplier.get(), "payload supplier returned null");
163162
ApiFutures.addCallback(
164163
startFuture,
@@ -174,43 +173,10 @@ public void onSuccess(ResumableUploadSession session) {
174173
return;
175174
}
176175
ResumableUploadChunkCoordinator<ResponseT> coordinator =
177-
new ResumableUploadChunkCoordinator<>(
178-
uploadChunkCallable,
179-
queryStatusCallable,
180-
sessionUrl,
181-
payload,
182-
chunkSize,
183-
callContext,
184-
recoveryAlgorithm,
185-
executor,
186-
progressTracker);
187-
ApiFuture<ResponseT> uploadFuture = coordinator.getFuture();
188-
synchronized (lock) {
189-
if (inFlightFuture == null) {
190-
return;
191-
}
192-
effectiveChunkSize = chunkSize;
193-
uploadSessionUrl = sessionUrl;
194-
inFlightFuture = uploadFuture;
176+
createAndRegisterCoordinator(sessionUrl, chunkSize);
177+
if (coordinator != null) {
178+
coordinator.start();
195179
}
196-
ApiFutures.addCallback(
197-
uploadFuture,
198-
new ApiFutureCallback<ResponseT>() {
199-
@Override
200-
public void onSuccess(ResponseT response) {
201-
succeed(response);
202-
}
203-
204-
@Override
205-
public void onFailure(Throwable t) {
206-
if (t instanceof CancellationException) {
207-
return;
208-
}
209-
fail(t);
210-
}
211-
},
212-
MoreExecutors.directExecutor());
213-
coordinator.start();
214180
}
215181

216182
@Override
@@ -224,6 +190,55 @@ public void onFailure(Throwable t) {
224190
MoreExecutors.directExecutor());
225191
}
226192

193+
private void setUpGlobalTimeout() {
194+
Duration timeout = firstNonNull(options.getGlobalTimeout(), DEFAULT_GLOBAL_TIMEOUT);
195+
ScheduledFuture<?> timeoutFuture =
196+
executor.schedule(this::onTimeout, timeout.toMillis(), TimeUnit.MILLISECONDS);
197+
resultFuture.addListener(() -> timeoutFuture.cancel(false), MoreExecutors.directExecutor());
198+
}
199+
200+
private @Nullable ResumableUploadChunkCoordinator<ResponseT> createAndRegisterCoordinator(
201+
String sessionUrl, int chunkSize) {
202+
ResumableUploadChunkCoordinator<ResponseT> coordinator =
203+
new ResumableUploadChunkCoordinator<>(
204+
uploadChunkCallable,
205+
queryStatusCallable,
206+
sessionUrl,
207+
checkNotNull(payload),
208+
chunkSize,
209+
callContext,
210+
recoveryAlgorithm,
211+
executor,
212+
progressTracker);
213+
ApiFuture<ResponseT> uploadFuture = coordinator.getFuture();
214+
synchronized (lock) {
215+
if (inFlightFuture == null) {
216+
return null;
217+
}
218+
effectiveChunkSize = chunkSize;
219+
uploadSessionUrl = sessionUrl;
220+
inFlightFuture = uploadFuture;
221+
}
222+
ApiFutures.addCallback(
223+
uploadFuture,
224+
new ApiFutureCallback<ResponseT>() {
225+
@Override
226+
public void onSuccess(ResponseT response) {
227+
succeed(response);
228+
}
229+
230+
@Override
231+
public void onFailure(Throwable t) {
232+
if (t instanceof CancellationException) {
233+
return;
234+
}
235+
fail(t);
236+
}
237+
},
238+
MoreExecutors.directExecutor());
239+
return coordinator;
240+
}
241+
227242
private void onTimeout() {
228243
String message;
229244
if (getUploadSessionUrl() != null) {

‎sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ResumableUploadProgressTracker.java‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,12 @@ private void dispatchSafely(ResumableUploadProgress status) {
9393
@GuardedBy("lock")
9494
private ResumableUploadProgress currentStatus;
9595

96-
ResumableUploadProgressTracker() {
97-
this.currentStatus = ResumableUploadProgress.newBuilder().build();
96+
private ResumableUploadProgressTracker(ResumableUploadProgress initialStatus) {
97+
this.currentStatus = checkNotNull(initialStatus, "initialStatus must not be null");
98+
}
99+
100+
static ResumableUploadProgressTracker create() {
101+
return new ResumableUploadProgressTracker(ResumableUploadProgress.newBuilder().build());
98102
}
99103

100104
void addListener(ResumableUploadProgressListener listener, Executor executor) {

‎sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/ResumableUploadProgressTrackerTest.java‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ResumableUploadProgressTrackerTest {
4646

4747
@Test
4848
void testInitialSnapshotOnSubscribe() {
49-
ResumableUploadProgressTracker tracker = new ResumableUploadProgressTracker();
49+
ResumableUploadProgressTracker tracker = ResumableUploadProgressTracker.create();
5050
List<ResumableUploadProgress> statuses = new ArrayList<>();
5151
tracker.addListener(statuses::add, MoreExecutors.directExecutor());
5252

@@ -60,7 +60,7 @@ void testInitialSnapshotOnSubscribe() {
6060

6161
@Test
6262
void testProgressUpdates() {
63-
ResumableUploadProgressTracker tracker = new ResumableUploadProgressTracker();
63+
ResumableUploadProgressTracker tracker = ResumableUploadProgressTracker.create();
6464
tracker.onStarted("https://upload.url/test", 256);
6565

6666
List<Long> byteUpdates = new ArrayList<>();
@@ -79,7 +79,7 @@ void testProgressUpdates() {
7979

8080
@Test
8181
void testListenerExceptionSafety_doesNotDisruptSubsequentUpdates() {
82-
ResumableUploadProgressTracker tracker = new ResumableUploadProgressTracker();
82+
ResumableUploadProgressTracker tracker = ResumableUploadProgressTracker.create();
8383
tracker.onStarted("https://upload.url/test", 256);
8484

8585
AtomicInteger errorCount = new AtomicInteger(0);
@@ -111,7 +111,7 @@ void testListenerExceptionSafety_doesNotDisruptSubsequentUpdates() {
111111

112112
@Test
113113
void testTerminalState_noNotificationsAfterFinalized() {
114-
ResumableUploadProgressTracker tracker = new ResumableUploadProgressTracker();
114+
ResumableUploadProgressTracker tracker = ResumableUploadProgressTracker.create();
115115
tracker.onStarted("https://upload.url/test", 512);
116116

117117
List<String> received = new ArrayList<>();
@@ -135,7 +135,7 @@ void testTerminalState_noNotificationsAfterFinalized() {
135135

136136
@Test
137137
void testListenerReentrancy_canQueryStatusInsideCallback() {
138-
ResumableUploadProgressTracker tracker = new ResumableUploadProgressTracker();
138+
ResumableUploadProgressTracker tracker = ResumableUploadProgressTracker.create();
139139
AtomicReference<ResumableUploadProgress> observed = new AtomicReference<>();
140140

141141
tracker.addListener(
@@ -149,7 +149,7 @@ void testListenerReentrancy_canQueryStatusInsideCallback() {
149149

150150
@Test
151151
void testCustomExecutorDispatch() throws Exception {
152-
ResumableUploadProgressTracker tracker = new ResumableUploadProgressTracker();
152+
ResumableUploadProgressTracker tracker = ResumableUploadProgressTracker.create();
153153
ExecutorService executor = Executors.newSingleThreadExecutor();
154154
try {
155155
CountDownLatch latch = new CountDownLatch(2);

0 commit comments

Comments
 (0)