Skip to content

feat(gax): add RewindableStreamBuffer for chunk upload recovery - #14423

Draft
whowes wants to merge 1 commit into
whowes/resumable-upload-chunk-retryfrom
whowes/resumable-upload-buffer-window
Draft

whowes wants to merge 1 commit into
whowes/resumable-upload-chunk-retryfrom
whowes/resumable-upload-buffer-window

Conversation

@whowes

@whowes whowes commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Introduces RewindableStreamBuffer to manage a single-chunk buffer for the user-provided InputStream. This will be used in the next PR to "rewind" the stream as part of the query-command-based recovery process.

The length of upload request payloads is now included in the ChunkUploadRequest to allow for more efficient propagation of the content bytes from stream -> buffer -> wire.

@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from ba8840b to 6d89325 Compare September 17, 2026 22:08
gemini-code-assist[bot]

This comment was marked as outdated.

@whowes
whowes added this pull request to stack #14429 September 17, 2026 22:16
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 6d89325 to da1275c Compare September 18, 2026 02:23
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch 2 times, most recently from 589ef8e to f130ccc Compare September 18, 2026 03:21
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from f130ccc to 0e94df8 Compare September 18, 2026 15:05
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 0e94df8 to 00f7932 Compare September 19, 2026 01:36
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 00f7932 to c6c28b1 Compare September 19, 2026 21:12
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from c6c28b1 to 4a4b890 Compare September 19, 2026 22:57
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 4a4b890 to 14f6204 Compare September 19, 2026 23:17
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 14f6204 to 0a389c4 Compare September 20, 2026 00:19
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 0a389c4 to 7d192ff Compare September 20, 2026 05:23
@whowes whowes changed the title feat(gax): add rewindable stream buffer for chunk recovery feat(gax): add RewindableStreamBuffer for chunk upload recovery Sep 20, 2026
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 7d192ff to 33b9862 Compare September 20, 2026 06:51
@whowes
whowes removed this pull request from stack #14429 September 20, 2026 07:20
@whowes
whowes added this pull request to stack #14454 September 20, 2026 07:21
Introduce RewindableStreamBuffer managing a single-chunk buffer over an
InputStream, supporting forward compaction and topping up upon recovery
realignment without mark()/reset(). Enforces boundaries by throwing
FailedPreconditionException when a server offset is below the base
offset or beyond the current buffer window.

Use payloadLength in ChunkUploadRequest to avoid allocating temporary
byte arrays for full-sized chunks while reusing a single backing array.
@whowes
whowes force-pushed the whowes/resumable-upload-buffer-window branch from 33b9862 to 12f0b82 Compare September 20, 2026 07:45
@whowes

whowes commented Sep 20, 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 a new RewindableStreamBuffer class to manage a single-chunk buffer over an InputStream for resumable uploads, supporting forward compaction and topping up upon recovery realignment. It also updates ResumableUploadChunkCoordinator to use this new buffer, and modifies ChunkUploadRequest to track and validate payloadLength. However, a critical issue was identified in ResumableUploadChunkCoordinator where unconditionally calling buffer.fill(currentOffset) inside transmitChunk would overwrite the buffer even if it had already been populated and realigned during recovery, potentially causing data corruption. A conditional check should be added to only fill the buffer when necessary.

Comment on lines 127 to 132
try {
bytesRead = ByteStreams.read(payload, buffer, 0, chunkSize);
buffer.fill(currentOffset);
} catch (IOException e) {
result.setException(e);
return;
}

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.

high

Unconditionally calling buffer.fill(currentOffset) inside transmitChunk will overwrite the buffer even if it has already been correctly populated and realigned (e.g., via realignTo(committedOffset) during a recovery flow). This defeats the purpose of the realignTo method and would result in data corruption or unnecessary stream reads during recovery.

We should only call buffer.fill(currentOffset) if the buffer does not already contain the data for the requested currentOffset (i.e., when buffer.getBufferBaseOffset() != currentOffset or when the buffer is in its initial empty state).

Suggested change
try {
bytesRead = ByteStreams.read(payload, buffer, 0, chunkSize);
buffer.fill(currentOffset);
} catch (IOException e) {
result.setException(e);
return;
}
if (buffer.getBufferBaseOffset() != currentOffset || (buffer.isEmpty() && !buffer.isFinal())) {
try {
buffer.fill(currentOffset);
} catch (IOException e) {
result.setException(e);
return;
}
}

@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

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.

1 participant