Skip to content

Conversation

@Pulkit0110
Copy link
Member

Adding writes resumption strategy which will be used for error handling of bidi writes operation.

@Pulkit0110 Pulkit0110 requested review from a team as code owners December 15, 2025 07:17
@product-auto-label product-auto-label bot added the size: xl Pull request size is extra large. label Dec 15, 2025
@product-auto-label product-auto-label bot added the api: storage Issues related to the googleapis/python-storage API. label Dec 15, 2025
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Pulkit0110, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the reliability of asynchronous bidirectional streaming operations by introducing a comprehensive write resumption strategy and a generic retry manager. This refactoring centralizes error handling and retry logic, making both read and write operations more resilient to transient failures and network interruptions. The changes ensure that data transfers can seamlessly resume from the last known good state, improving the overall robustness of the system.

Highlights

  • Write Resumption Strategy: Introduced a new _WriteResumptionStrategy to provide robust error handling and resumption capabilities for bidirectional streaming write operations.
  • Generic Bidi Stream Retry Manager: Implemented a generic _BidiStreamRetryManager to centralize retry logic for all bidirectional streaming operations, making it reusable for both reads and writes.
  • Refactored Read Resumption: The AsyncMultiRangeDownloader was refactored to utilize the new _BidiStreamRetryManager and an updated _ReadResumptionStrategy, moving checksum and offset validation into the strategy.
  • Retry Policy Integration: The open and download_ranges methods in AsyncMultiRangeDownloader now accept an AsyncRetry policy, allowing callers to customize retry behavior.
  • Improved Data Integrity Checks: Checksum and offset validation logic for read operations has been enhanced and moved into the _ReadResumptionStrategy, ensuring data integrity during stream resumption.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

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

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 generic retry manager for bidi streams and applies it to read operations, which is a great improvement for resiliency. It also adds a resumption strategy for write operations. The overall structure is well-designed and the new tests are comprehensive. I've found one critical issue that will cause a TypeError at runtime due to a method being called with an unexpected argument. I've also included a couple of medium-severity comments to improve documentation clarity and maintainability in the new strategy classes.

@product-auto-label product-auto-label bot added size: l Pull request size is large. and removed size: xl Pull request size is extra large. labels Dec 26, 2025
@Pulkit0110
Copy link
Member Author

/gemini review

Copy link
Contributor

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

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 write resumption strategy for handling bidi write operations, which is a valuable addition for improving error handling in experimental async features. The implementation is well-structured and accompanied by a comprehensive set of unit tests that cover various scenarios, including initial uploads, resumption, and failure recovery. My feedback focuses on enhancing type safety, improving code clarity by addressing a potentially unused flag, and increasing the robustness of the tests by using mocks instead of None.

@chandra-siri
Copy link
Collaborator

/gemini summary

@gemini-code-assist
Copy link
Contributor

Summary of Changes

This pull request introduces an experimental write resumption strategy for asynchronous bidirectional write operations within the Google Cloud Storage client library. This new strategy significantly enhances the reliability of data uploads by providing robust mechanisms to handle errors, track the state of ongoing writes, and seamlessly resume operations from the last successfully persisted byte, thereby improving data integrity and resilience against transient network issues or server redirects.

Highlights

  • New Write Resumption Strategy: Introduced _WriteResumptionStrategy and _WriteState to provide robust error handling and resumption capabilities for asynchronous bidirectional write operations. This strategy manages the state of an upload, including write_handle, routing_token, persisted_size, and bytes_sent.
  • Request Generation and Checksumming: The strategy generates BidiWriteObjectRequests, incorporating AppendObjectSpec, state_lookup for resumption, and data chunks with CRC32C checksums for data integrity. It also handles the finish_write signal for completing uploads.
  • Error Recovery Mechanism: Includes logic to update the write state based on server responses and to recover from failures, specifically BidiWriteObjectRedirectedError. Upon failure, the user buffer is rewound to the last known persisted_size to facilitate seamless resumption.
  • Comprehensive Unit Tests: Added a new test file (test_writes_resumption_strategy.py) with extensive unit tests to validate the functionality of the _WriteResumptionStrategy, covering initial uploads, resumption scenarios, error recovery, and checksumming.
  • Minor Formatting Adjustments: Minor formatting changes were applied to an existing test file (test_reads_resumption_strategy.py) to align with code style guidelines.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • google/cloud/storage/_experimental/asyncio/retry/writes_resumption_strategy.py
    • Added a new module defining _WriteState to track the progress and state of an upload operation.
    • Implemented _WriteResumptionStrategy to manage the generation of write requests, processing of server responses, and recovery from failures for bidirectional writes.
  • tests/unit/asyncio/retry/test_reads_resumption_strategy.py
    • Applied minor formatting changes to improve code readability and consistency.
  • tests/unit/asyncio/retry/test_writes_resumption_strategy.py
    • Added a new test suite to thoroughly validate the functionality of the _WriteResumptionStrategy, including tests for initial uploads, empty files, resumption, error handling, and checksum verification.
Activity
  • gemini-code-assist[bot] provided an initial summary of the changes and several review comments, highlighting critical and medium priority suggestions.
  • Pulkit0110 (the author) addressed multiple review comments, including updating type hints, removing an unused first_request flag, and improving test robustness by using mock objects for _WriteState initialization.
  • chandra-siri requested a summary of the pull request.

class _WriteState:
"""A helper class to track the state of a single upload operation.
Attributes:
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: please keep doc strings similar to other files in this repo. example

Attributes:
spec (AppendObjectSpec): The specification for the object to write.
chunk_size (int): The size of chunks to read from the buffer.
Copy link
Collaborator

Choose a reason for hiding this comment

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

to read ?

write_state.write_handle = response.write_handle

if response.resource:
write_state.is_complete = True
Copy link
Collaborator

Choose a reason for hiding this comment

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

resource is obtained only when object is finalized. Finalization != closing stream (or finishing uploading for a particular session)

as per the doc string is_complete (bool): Whether the upload has finished .

What do you mean here , when you set is_complete to True?

if hasattr(cause, "write_handle") and cause.write_handle:
write_state.write_handle = cause.write_handle

# We must assume any data sent beyond 'persisted_size' was lost.
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is one of the important behavior of this method. Can you mention that in doc string. (it's understandable from rewinds state but providing more info would be beneficial to public.)

self.assertEqual(requests[3].checksummed_data.content, b"89")

self.assertEqual(requests[4].write_offset, 10)
self.assertTrue(requests[4].finish_write)
Copy link
Collaborator

Choose a reason for hiding this comment

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

it shouldn't always be true.

It should be true only when user explicitly provides finalize_on_close=True

same commment for all other tests.

def test_generate_requests_resumption(self):
"""
Verify request sequence when resuming an upload.
- First request is AppendObjectSpec with write_handle and state_lookup=True.
Copy link
Collaborator

Choose a reason for hiding this comment

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

why state_lookup=True ? . If it's to fetch persisted_size then AFAIR, when opening a bidi-stream to write with write_handle persisted_size is obtained in first BidiWriteObjectResponse message

write_state.is_complete = True
write_state.persisted_size = response.resource.size

async def recover_state_on_failure(
Copy link
Collaborator

Choose a reason for hiding this comment

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

(maybe little late to notice) - why async def ? there's no await anywhere here.

write_state = state["write_state"]
write_state.persisted_size = 2048

response = storage_type.BidiWriteObjectResponse(persisted_size=1024)
Copy link
Collaborator

Choose a reason for hiding this comment

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

when this scenario will happen ?

write_state.is_complete = True
yield storage_type.BidiWriteObjectRequest(
write_offset=write_state.bytes_sent,
finish_write=True,
Copy link
Collaborator

Choose a reason for hiding this comment

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

this way object is always finalized after doing writer.append , right ?

we should not finalize. We should keep the object in unfinalize state always unless users explicitly specifies.

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

Labels

api: storage Issues related to the googleapis/python-storage API. size: l Pull request size is large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants