19
19
import static com .google .cloud .storage .ByteSizeConstants ._256KiB ;
20
20
import static java .util .Objects .requireNonNull ;
21
21
22
+ import com .google .api .core .ApiFuture ;
22
23
import com .google .api .core .ApiFutures ;
23
24
import com .google .api .core .BetaApi ;
24
25
import com .google .api .core .InternalApi ;
26
+ import com .google .api .core .SettableApiFuture ;
25
27
import com .google .api .gax .retrying .BasicResultRetryAlgorithm ;
26
28
import com .google .api .gax .rpc .AbortedException ;
27
29
import com .google .api .gax .rpc .ApiException ;
30
+ import com .google .cloud .storage .BidiUploadState .AppendableUploadState ;
31
+ import com .google .cloud .storage .BidiUploadState .TakeoverAppendableUploadState ;
28
32
import com .google .cloud .storage .BlobAppendableUpload .AppendableUploadWriteableByteChannel ;
29
33
import com .google .cloud .storage .BlobAppendableUploadImpl .AppendableObjectBufferedWritableByteChannel ;
30
34
import com .google .cloud .storage .Storage .BlobWriteOption ;
31
35
import com .google .cloud .storage .TransportCompatibility .Transport ;
32
36
import com .google .cloud .storage .UnifiedOpts .ObjectTargetOpt ;
33
37
import com .google .cloud .storage .UnifiedOpts .Opts ;
38
+ import com .google .common .base .MoreObjects ;
34
39
import com .google .storage .v2 .BidiWriteObjectRequest ;
35
40
import com .google .storage .v2 .BidiWriteObjectResponse ;
36
41
import com .google .storage .v2 .Object ;
42
+ import com .google .storage .v2 .ServiceConstants .Values ;
43
+ import java .util .function .BiFunction ;
37
44
import javax .annotation .concurrent .Immutable ;
38
45
39
46
/**
@@ -53,17 +60,20 @@ public final class BlobAppendableUploadConfig {
53
60
new BlobAppendableUploadConfig (
54
61
FlushPolicy .minFlushSize (_256KiB ),
55
62
Hasher .enabled (),
56
- CloseAction .CLOSE_WITHOUT_FINALIZING );
63
+ CloseAction .CLOSE_WITHOUT_FINALIZING ,
64
+ false );
57
65
58
66
private final FlushPolicy flushPolicy ;
59
67
private final Hasher hasher ;
60
68
private final CloseAction closeAction ;
69
+ private final boolean newImpl ;
61
70
62
71
private BlobAppendableUploadConfig (
63
- FlushPolicy flushPolicy , Hasher hasher , CloseAction closeAction ) {
72
+ FlushPolicy flushPolicy , Hasher hasher , CloseAction closeAction , boolean newImpl ) {
64
73
this .flushPolicy = flushPolicy ;
65
74
this .hasher = hasher ;
66
75
this .closeAction = closeAction ;
76
+ this .newImpl = newImpl ;
67
77
}
68
78
69
79
/**
@@ -94,7 +104,7 @@ public BlobAppendableUploadConfig withFlushPolicy(FlushPolicy flushPolicy) {
94
104
if (this .flushPolicy .equals (flushPolicy )) {
95
105
return this ;
96
106
}
97
- return new BlobAppendableUploadConfig (flushPolicy , hasher , closeAction );
107
+ return new BlobAppendableUploadConfig (flushPolicy , hasher , closeAction , newImpl );
98
108
}
99
109
100
110
/**
@@ -124,7 +134,7 @@ public BlobAppendableUploadConfig withCloseAction(CloseAction closeAction) {
124
134
if (this .closeAction == closeAction ) {
125
135
return this ;
126
136
}
127
- return new BlobAppendableUploadConfig (flushPolicy , hasher , closeAction );
137
+ return new BlobAppendableUploadConfig (flushPolicy , hasher , closeAction , newImpl );
128
138
}
129
139
130
140
/**
@@ -156,7 +166,7 @@ BlobAppendableUploadConfig withCrc32cValidationEnabled(boolean enabled) {
156
166
return this ;
157
167
}
158
168
return new BlobAppendableUploadConfig (
159
- flushPolicy , enabled ? Hasher .enabled () : Hasher .noop (), closeAction );
169
+ flushPolicy , enabled ? Hasher .enabled () : Hasher .noop (), closeAction , newImpl );
160
170
}
161
171
162
172
/** Never to be made public until {@link Hasher} is public */
@@ -165,6 +175,18 @@ Hasher getHasher() {
165
175
return hasher ;
166
176
}
167
177
178
+ BlobAppendableUploadConfig useNewImpl () {
179
+ return new BlobAppendableUploadConfig (flushPolicy , hasher , closeAction , true );
180
+ }
181
+
182
+ @ Override
183
+ public String toString () {
184
+ return MoreObjects .toStringHelper (this )
185
+ .add ("closeAction" , closeAction )
186
+ .add ("flushPolicy" , flushPolicy )
187
+ .toString ();
188
+ }
189
+
168
190
/**
169
191
* Default instance factory method.
170
192
*
@@ -217,55 +239,123 @@ public enum CloseAction {
217
239
}
218
240
219
241
BlobAppendableUpload create (GrpcStorageImpl storage , BlobInfo info , Opts <ObjectTargetOpt > opts ) {
220
- boolean takeOver = info .getGeneration () != null ;
221
- BidiWriteObjectRequest req =
222
- takeOver
223
- ? storage .getBidiWriteObjectRequestForTakeover (info , opts )
224
- : storage .getBidiWriteObjectRequest (info , opts );
225
-
226
- BidiAppendableWrite baw = new BidiAppendableWrite (req , takeOver );
242
+ if (newImpl ) {
243
+ // TODO: make configurable
244
+ int maxRedirectsAllowed = 3 ;
227
245
246
+ long maxPendingBytes = this .getFlushPolicy ().getMaxPendingBytes ();
247
+ AppendableUploadState state = storage .getAppendableState (info , opts , maxPendingBytes );
228
248
WritableByteChannelSession <AppendableObjectBufferedWritableByteChannel , BidiWriteObjectResponse >
229
249
build =
230
- ResumableMedia .gapic ()
231
- .write ()
232
- .bidiByteChannel (storage .storageClient .bidiWriteObjectCallable ())
233
- .setHasher (this .getHasher ())
234
- .setByteStringStrategy (ByteStringStrategy .copy ())
235
- .appendable ()
236
- .withRetryConfig (
237
- storage .retrier .withAlg (
238
- new BasicResultRetryAlgorithm <Object >() {
239
- @ Override
240
- public boolean shouldRetry (
241
- Throwable previousThrowable , Object previousResponse ) {
242
- // TODO: remove this later once the redirects are not handled by the
243
- // retry loop
244
- ApiException apiEx = null ;
245
- if (previousThrowable instanceof StorageException ) {
246
- StorageException se = (StorageException ) previousThrowable ;
247
- Throwable cause = se .getCause ();
248
- if (cause instanceof ApiException ) {
249
- apiEx = (ApiException ) cause ;
250
+ new AppendableSession (
251
+ ApiFutures .immediateFuture (state ),
252
+ (start , resultFuture ) -> {
253
+ BidiUploadStreamingStream stream =
254
+ new BidiUploadStreamingStream (
255
+ start ,
256
+ storage .storageDataClient .executor ,
257
+ storage .storageClient .bidiWriteObjectCallable (),
258
+ maxRedirectsAllowed ,
259
+ storage .storageDataClient .retryContextProvider .create ());
260
+ ChunkSegmenter chunkSegmenter =
261
+ new ChunkSegmenter (
262
+ Hasher .enabled (),
263
+ ByteStringStrategy .copy (),
264
+ Math .min (
265
+ Values .MAX_WRITE_CHUNK_BYTES_VALUE , Math .toIntExact (maxPendingBytes )),
266
+ /* blockSize= */ 1 );
267
+ BidiAppendableUnbufferedWritableByteChannel c ;
268
+ if (state instanceof TakeoverAppendableUploadState ) {
269
+ // start the takeover reconciliation
270
+ stream .awaitTakeoverStateReconciliation ();
271
+ c =
272
+ new BidiAppendableUnbufferedWritableByteChannel (
273
+ stream , chunkSegmenter , state .getConfirmedBytes ());
274
+ } else {
275
+ c = new BidiAppendableUnbufferedWritableByteChannel (stream , chunkSegmenter , 0 );
276
+ }
277
+ return new AppendableObjectBufferedWritableByteChannel (
278
+ flushPolicy .createBufferedChannel (c ),
279
+ c ,
280
+ this .closeAction == CloseAction .FINALIZE_WHEN_CLOSING ,
281
+ newImpl );
282
+ },
283
+ state .getResultFuture ());
284
+
285
+ return new BlobAppendableUploadImpl (
286
+ new DefaultBlobWriteSessionConfig .DecoratedWritableByteChannelSession <>(
287
+ build , BidiBlobWriteSessionConfig .Factory .WRITE_OBJECT_RESPONSE_BLOB_INFO_DECODER ));
288
+ } else {
289
+ boolean takeOver = info .getGeneration () != null ;
290
+ BidiWriteObjectRequest req =
291
+ takeOver
292
+ ? storage .getBidiWriteObjectRequestForTakeover (info , opts )
293
+ : storage .getBidiWriteObjectRequest (info , opts );
294
+
295
+ BidiAppendableWrite baw = new BidiAppendableWrite (req , takeOver );
296
+
297
+ WritableByteChannelSession <
298
+ AppendableObjectBufferedWritableByteChannel , BidiWriteObjectResponse >
299
+ build =
300
+ ResumableMedia .gapic ()
301
+ .write ()
302
+ .bidiByteChannel (storage .storageClient .bidiWriteObjectCallable ())
303
+ .setHasher (this .getHasher ())
304
+ .setByteStringStrategy (ByteStringStrategy .copy ())
305
+ .appendable ()
306
+ .withRetryConfig (
307
+ storage .retrier .withAlg (
308
+ new BasicResultRetryAlgorithm <Object >() {
309
+ @ Override
310
+ public boolean shouldRetry (
311
+ Throwable previousThrowable , Object previousResponse ) {
312
+ // TODO: remove this later once the redirects are not handled by the
313
+ // retry loop
314
+ ApiException apiEx = null ;
315
+ if (previousThrowable instanceof StorageException ) {
316
+ StorageException se = (StorageException ) previousThrowable ;
317
+ Throwable cause = se .getCause ();
318
+ if (cause instanceof ApiException ) {
319
+ apiEx = (ApiException ) cause ;
320
+ }
250
321
}
322
+ if (apiEx instanceof AbortedException ) {
323
+ return true ;
324
+ }
325
+ return storage
326
+ .retryAlgorithmManager
327
+ .idempotent ()
328
+ .shouldRetry (previousThrowable , null );
251
329
}
252
- if (apiEx instanceof AbortedException ) {
253
- return true ;
254
- }
255
- return storage
256
- .retryAlgorithmManager
257
- .idempotent ()
258
- .shouldRetry (previousThrowable , null );
259
- }
260
- }))
261
- .buffered (this .getFlushPolicy ())
262
- .setStartAsync (ApiFutures .immediateFuture (baw ))
263
- .setGetCallable (storage .storageClient .getObjectCallable ())
264
- .setFinalizeOnClose (this .closeAction == CloseAction .FINALIZE_WHEN_CLOSING )
265
- .build ();
330
+ }))
331
+ .buffered (this .getFlushPolicy ())
332
+ .setStartAsync (ApiFutures .immediateFuture (baw ))
333
+ .setGetCallable (storage .storageClient .getObjectCallable ())
334
+ .setFinalizeOnClose (this .closeAction == CloseAction .FINALIZE_WHEN_CLOSING )
335
+ .build ();
266
336
267
- return new BlobAppendableUploadImpl (
268
- new DefaultBlobWriteSessionConfig .DecoratedWritableByteChannelSession <>(
269
- build , BidiBlobWriteSessionConfig .Factory .WRITE_OBJECT_RESPONSE_BLOB_INFO_DECODER ));
337
+ return new BlobAppendableUploadImpl (
338
+ new DefaultBlobWriteSessionConfig .DecoratedWritableByteChannelSession <>(
339
+ build , BidiBlobWriteSessionConfig .Factory .WRITE_OBJECT_RESPONSE_BLOB_INFO_DECODER ));
340
+ }
341
+ }
342
+
343
+ private static final class AppendableSession
344
+ extends ChannelSession <
345
+ AppendableUploadState ,
346
+ BidiWriteObjectResponse ,
347
+ AppendableObjectBufferedWritableByteChannel >
348
+ implements WritableByteChannelSession <
349
+ AppendableObjectBufferedWritableByteChannel , BidiWriteObjectResponse > {
350
+ private AppendableSession (
351
+ ApiFuture <AppendableUploadState > startFuture ,
352
+ BiFunction <
353
+ AppendableUploadState ,
354
+ SettableApiFuture <BidiWriteObjectResponse >,
355
+ AppendableObjectBufferedWritableByteChannel >
356
+ f ,
357
+ SettableApiFuture <BidiWriteObjectResponse > resultFuture ) {
358
+ super (startFuture , f , resultFuture );
359
+ }
270
360
}
271
361
}
0 commit comments