Skip to content

feat(gax): align resumable upload chunk size to server granularity and surface effective chunk size - #14670

Open
whowes wants to merge 1 commit into
mainfrom
whowes/align-chunk-size
Open

whowes wants to merge 1 commit into
mainfrom
whowes/align-chunk-size

Conversation

@whowes

@whowes whowes commented Oct 16, 2026

Copy link
Copy Markdown
Contributor
  • Align the user-provided chunk size to the server-provided X-Goog-Upload-Chunk-Granularity in ResumableUploadFutureImpl once session initiation completes.
  • Surface the resolved chunk size via ResumableUploadFuture.getEffectiveChunkSize(), ResumableUploadProgress.getEffectiveChunkSize(), and in terminal ApiException messages alongside the upload URL.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the concept of an 'effective chunk size' to the resumable upload process, aligning the configured chunk size with the server's chunk granularity and exposing it via ResumableUploadFuture and ResumableUploadProgress. Feedback on these changes highlights two key concurrency and safety improvements in ResumableUploadFutureImpl.java: first, avoiding a potential NullPointerException by formatting the nullable effectiveChunkSize with %s instead of %d and localizing volatile reads; second, ordering volatile writes so that effectiveChunkSize is written before uploadSessionUrl to ensure consistent visibility across threads.

@whowes
whowes force-pushed the whowes/align-chunk-size branch from 0ad413f to 5616230 Compare October 16, 2026 18:17
@whowes
whowes added this pull request to stack #14678 October 16, 2026 23:06
@whowes
whowes removed this pull request from stack #14678 October 22, 2026 16:36
@whowes
whowes force-pushed the whowes/align-chunk-size branch from 5616230 to 4ee3c1d Compare October 22, 2026 16:37
@whowes
whowes added this pull request to stack #14732 October 22, 2026 16:41
@whowes

whowes commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces tracking and alignment of the effective chunk size based on the server's chunk granularity during resumable uploads. It exposes getEffectiveChunkSize() in both ResumableUploadFuture and ResumableUploadProgress, and updates exception messages to include this information. Feedback suggests making the chunk size alignment more robust by returning the configured chunk size directly if the server's chunk granularity is less than or equal to zero, avoiding potential failures on backends that do not specify granularity.

@whowes
whowes marked this pull request as ready for review October 22, 2026 17:30
@whowes
whowes requested a review from a team as a code owner October 22, 2026 17:30
@whowes
whowes requested a review from blakeli0 October 22, 2026 17:30
"Configured chunk size (%s bytes) must be at least the server chunk granularity (%s bytes)",
configuredChunkSize,
chunkGranularity);
int unalignedRemainder = (int) (configuredChunkSize % chunkGranularity);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(int) is a bit awkward, do we know if chunkGranularity from the server response is long or int? If we do, maybe we can serialize it to int?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Over the wire it's not typed as long or int, but pragmatically it seems pretty much impossible that the server would prescribe a granularity expressible only as a long. Switched the lower-level types to parse the response into int and bubbled up the type change.

checkArgument(configuredChunkSize > 0, "configuredChunkSize must be > 0");
checkArgument(chunkGranularity > 0, "chunkGranularity must be > 0");
checkArgument(
configuredChunkSize >= chunkGranularity,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this behavior a requirement? If not, maybe align the effective chunk size to be chunkGranularity if configuredChunkSize is smaller than it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a requirement AIUI and your suggestion makes more sense - done.

if (t instanceof ApiException && uploadSessionUrl == null) {
String sessionUrl;
Integer chunkSize;
synchronized (lock) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a synchronized block here and the getter below? volatile should be good enough since there should always be only one thread writing.

We don't have to always return them together as well, it's OK if one of them is null. It is also very unlikely that one of them is populated and the other one is null.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getter is needed because passing the chunk size is required for resuming, so we need to provide a way for the user to get it in case their provided value is changed for granularity reasons. From the requirements: "Since the upload resume does not start with the start command, there is no opportunity to receive the server-specified granularity and adjust the chunk size."

Unless you feel strongly, I prefer keeping the switch to synchronized. It was prompted by my investigating an earlier Gemini review finding - in general as I was digging into it the threading characteristics were IMO harder to reason about with multiple volatile fields mixed with others that legitimately require synchronization. I personally find it more readable/understandable to consolidate on synchronizing these even with a single writer (and I don't think this is a hot spot where synchronizing will cause performance issues).


/**
* Returns the effective chunk size in bytes aligned to the server's chunk granularity, or {@code
* null} if session initiation is in progress.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It can also be null if the session initiation failed. Same thing for getUploadSessionUrl above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, adjusted both docs.

@whowes
whowes force-pushed the whowes/align-chunk-size branch from 4ee3c1d to 43ec12a Compare October 22, 2026 21:58
@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed for 'gapic-generator-java-root'

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants