Skip to content

Commit 71b0f8a

Browse files
authored
feat(experimental): Handle BidiReadObjectRedirectedError for bidi reads (#1600)
* feat(experimental): Add read resumption strategy * add unit tests * feat(experimental): Handle BidiReadObjectRedirectedError for bidi reads * resolve comments
1 parent 5d5e895 commit 71b0f8a

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

google/cloud/storage/_experimental/asyncio/retry/reads_resumption_strategy.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from google.cloud.storage._experimental.asyncio.retry.base_strategy import (
66
_BaseResumptionStrategy,
77
)
8+
from google.cloud._storage_v2.types.storage import BidiReadObjectRedirectedError
89

910
class _DownloadState:
1011
"""A helper class to track the state of a single range download."""
@@ -65,7 +66,9 @@ def update_state_from_response(self, response: storage_v2.BidiReadObjectResponse
6566
raise DataCorruption(response, f"Byte count mismatch for read_id {read_id}")
6667

6768
async def recover_state_on_failure(self, error: Exception, state: Any) -> None:
68-
"""Handles BidiReadObjectRedirectError for reads."""
69+
"""Handles BidiReadObjectRedirectedError for reads."""
6970
# This would parse the gRPC error details, extract the routing_token,
7071
# and store it on the shared state object.
71-
pass
72+
cause = getattr(error, "cause", error)
73+
if isinstance(cause, BidiReadObjectRedirectedError):
74+
state['routing_token'] = cause.routing_token

tests/unit/asyncio/retry/test_reads_resumption_strategy.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
import unittest
1717
import pytest
1818
from google.cloud.storage.exceptions import DataCorruption
19+
from google.api_core import exceptions
1920

2021
from google.cloud import _storage_v2 as storage_v2
2122
from google.cloud.storage._experimental.asyncio.retry.reads_resumption_strategy import (
2223
_DownloadState,
2324
_ReadResumptionStrategy,
2425
)
26+
from google.cloud._storage_v2.types.storage import BidiReadObjectRedirectedError
2527

2628
_READ_ID = 1
2729

@@ -204,3 +206,21 @@ def test_update_state_from_response_completes_download_zero_length(self):
204206

205207
self.assertTrue(read_state.is_complete)
206208
self.assertEqual(read_state.bytes_written, len(data))
209+
210+
async def test_recover_state_on_failure_handles_redirect(self):
211+
"""Verify recover_state_on_failure correctly extracts routing_token."""
212+
strategy = _ReadResumptionStrategy()
213+
214+
state = {}
215+
self.assertIsNone(state.get("routing_token"))
216+
217+
dummy_token = "dummy-routing-token"
218+
redirect_error = BidiReadObjectRedirectedError(
219+
routing_token=dummy_token
220+
)
221+
222+
final_error = exceptions.RetryError("Retry failed", cause=redirect_error)
223+
224+
await strategy.recover_state_on_failure(final_error, state)
225+
226+
self.assertEqual(state.get("routing_token"), dummy_token)

0 commit comments

Comments
 (0)