feat(gax): implement uploadChunkCallable for resumable uploads - #14140
Conversation
faf99d0 to
010c0aa
Compare
010c0aa to
baee7f0
Compare
|
/gemini review |
baee7f0 to
8a6d2fc
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces support for transmitting individual chunks in a resumable upload session by adding ChunkUploadRequest and ChunkUploadResponse classes, implementing uploadChunkCallable() in HttpJsonResumableUploadClient, and adding corresponding unit tests. Feedback was provided to change the HTTP method in UPLOAD_CHUNK_DESCRIPTOR from POST to PUT to comply with the Google Scotty resumable upload protocol.
8a6d2fc to
1c123a9
Compare
|
1c123a9 to
d1a4963
Compare
d1a4963 to
0dcb287
Compare
839a11f to
e6fd648
Compare
e6fd648 to
c851386
Compare
c851386 to
3e9ab99
Compare
3e9ab99 to
4937751
Compare
|
/gemini review |
d3abc11 to
f4acebc
Compare
f4acebc to
5268b3c
Compare
5268b3c to
7b0fff7
Compare
8e33b86 to
a6c4e6d
Compare
a6c4e6d to
f91782f
Compare
bbcdbb0 to
780a677
Compare
780a677 to
65db78a
Compare
65db78a to
356b549
Compare
356b549 to
2beec8b
Compare
| public abstract String getUploadUrl(); | ||
|
|
||
| /** The binary chunk payload to upload. */ | ||
| public abstract ByteString getPayload(); |
There was a problem hiding this comment.
Can we use a Java native type like byte[]?
There was a problem hiding this comment.
My main concern with the byte array is that it breaks the typical AutoValue immutability contract (unless using defensive copying which would be pretty expensive for multi-MB chunks). For Java native, ByteBuffer is the other option that comes to mind but it's stateful so not really suitable for use here.
Using ByteString seems to be within precedent when an immutable array/List of bytes is needed see e.g. ByteArray that uses it as backing storage even though the class itself isn't really protobuf-specific, wdyt?
There was a problem hiding this comment.
My main concern with the byte array is that it breaks the typical AutoValue immutability contract
I think this is a valid concern but it is acceptable compare to other concerns. My two concerns are:
- Unnecessary converting from
InputStreamtoByteStringand then tobyte[].ResumableUploadCallableaccepts anInputStream, so we would have to create aByteStringfrom it first in the upstream callable and getbyte[]from it in low level callable. - Protobuf types such as
ByteStringare already exposed on the surface but we don't want to make it worse. Protobuf could still make breaking changes so using Java native types are preferred in general.
There was a problem hiding this comment.
SGTM, switched to byte[]
|
|
||
| @Override | ||
| public String getPath(ChunkUploadRequest request) { | ||
| return request.getUploadUrl(); |
There was a problem hiding this comment.
I know we designed request.getUploadUrl to be a full url in previous PRs. But I'm leaning towards splitting the full url returned from server to endpoint and path to follow existing patterns now.
For example, split https://foo.com/upload?upload_id=xyz to https://foo.com as endpoint, upload as path, and upload_id=xyz as query parameters.
This is because we have some places like the getPathTemplate method below assuming the structure of the path and using a placeholder like PathTemplate.create("{+path}") could be misleading.
On the other hand, I know it might not be easy to split the full urls in a clean way either. Let me know what you think.
There was a problem hiding this comment.
I don't know that we'd ever be able to use anything more specific than the {+path} placeholder here though since that may vary across the various services that use resumable uploads? e.g. we can't assume that we can parse out any upload ID (that limitation in particular is called out in one of our internal requirements docs.) I don't know that we even can assume that "upload" will be in the path.
In practice for the upload protocol code using the URL as an opaque string is fine since we just make requests directly to the URL without needing to make additions or subtractions to it (so there wouldn't really be much benefit to the protocol code specifically where this would be helpful), are there assumptions made elsewhere in GAX about the URL structure where this is problematic?
I'm not completely opposed to parsing here, just trying to understand the benefits v. complexity it would add (and probably preferring to defer that refactor to a future PR).
There was a problem hiding this comment.
SGTM. Let's use PathTemplate.create("**") as a dummy placeholder for now.
There was a problem hiding this comment.
Done, switched to "**" here and other relevant places.
| return future; | ||
| } | ||
|
|
||
| static <ResponseT> UnaryCallable<ChunkUploadRequest, ChunkUploadResponse<ResponseT>> create( |
There was a problem hiding this comment.
Usually the creation of the callable is not done in the callable itself. For example, createOperationCallable in HttpJsonCallableFactory.
We probably need to do the same for the high level ResumableUploadCallable. For the low level callables, I think it is OK to do this for now in this PR, but we need to give it more thought later.
There was a problem hiding this comment.
Acknowledged, I agree that ResumableUploadCallable creation definitely belongs there, and we can consider consolidating the lower-level Callable factory methods there too in the future.
| public abstract String getUploadUrl(); | ||
|
|
||
| /** The binary chunk payload to upload. */ | ||
| public abstract ByteString getPayload(); |
There was a problem hiding this comment.
My main concern with the byte array is that it breaks the typical AutoValue immutability contract
I think this is a valid concern but it is acceptable compare to other concerns. My two concerns are:
- Unnecessary converting from
InputStreamtoByteStringand then tobyte[].ResumableUploadCallableaccepts anInputStream, so we would have to create aByteStringfrom it first in the upstream callable and getbyte[]from it in low level callable. - Protobuf types such as
ByteStringare already exposed on the surface but we don't want to make it worse. Protobuf could still make breaking changes so using Java native types are preferred in general.
|
|







This handles sending binary chunks and finalize commands over HTTP/JSON, extracting offset and status from response headers/codes.