Skip to content

Commit b7fa233

Browse files
committed
refactor(storage): clean up stream teardown, eliminate redundant state, and revert artificial diffs
1 parent 92c553a commit b7fa233

2 files changed

Lines changed: 31 additions & 59 deletions

File tree

‎packages/google-cloud-storage/google/cloud/storage/asyncio/async_multi_range_downloader.py‎

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ def record_completion(self, range_count: int, byte_count: int) -> None:
131131
self.pending_ranges = max(0, self.pending_ranges - range_count)
132132
self.pending_bytes = max(0, self.pending_bytes - byte_count)
133133

134+
async def close(self) -> None:
135+
"""Closes the underlying multiplexer and stream cleanly."""
136+
try:
137+
await self.multiplexer.close()
138+
except Exception:
139+
pass
140+
try:
141+
res = self.stream.close()
142+
if inspect.isawaitable(res):
143+
await res
144+
except Exception:
145+
pass
146+
134147

135148
class _StreamPool:
136149
"""Manages a pool of _ManagedStream instances with dynamic scaling and least-loaded dispatch."""
@@ -196,16 +209,7 @@ async def _scale_up(self) -> None:
196209
new_worker = await self._stream_factory()
197210
async with self._lock:
198211
if self._closed:
199-
try:
200-
await new_worker.multiplexer.close()
201-
except Exception:
202-
pass
203-
try:
204-
res = new_worker.stream.close()
205-
if inspect.isawaitable(res):
206-
await res
207-
except Exception:
208-
pass
212+
await new_worker.close()
209213
return
210214
self.workers.append(new_worker)
211215
except Exception as e:
@@ -228,16 +232,7 @@ async def close(self) -> None:
228232
for task in list(self._background_tasks):
229233
task.cancel()
230234
for w in workers:
231-
try:
232-
await w.multiplexer.close()
233-
except Exception:
234-
pass
235-
try:
236-
res = w.stream.close()
237-
if inspect.isawaitable(res):
238-
await res
239-
except Exception:
240-
pass
235+
await w.close()
241236

242237

243238
class AsyncMultiRangeDownloader:
@@ -392,12 +387,6 @@ def __init__(
392387
self.full_obj_server_crc32c: Optional[int] = None
393388

394389
self.stream_config = stream_config
395-
self.min_connections = stream_config.min_connections if stream_config else 1
396-
self.max_connections = stream_config.max_connections if stream_config else 1
397-
self.target_io_depth = stream_config.target_io_depth if stream_config else 8
398-
self.target_bytes = (
399-
stream_config.target_bytes if stream_config else 8 * 1024 * 1024
400-
)
401390
self._pool: Optional[_StreamPool] = None
402391
self._primary_worker: Optional[_ManagedStream] = None
403392
self._metadata: Optional[List[Tuple[str, str]]] = None
@@ -494,8 +483,6 @@ async def _do_open():
494483
self.generation = self.read_obj_str.generation_number
495484
if self.read_obj_str.read_handle:
496485
self.read_handle = self.read_obj_str.read_handle
497-
if getattr(self.read_obj_str, "routing_token", None):
498-
self._routing_token = self.read_obj_str.routing_token
499486
if self.read_obj_str.persisted_size is not None:
500487
self.persisted_size = self.read_obj_str.persisted_size
501488
self.is_finalized = self.read_obj_str.is_finalized
@@ -511,14 +498,14 @@ async def _do_open():
511498
if self.stream_config is not None and self.stream_config.max_connections > 1:
512499
self._pool = _StreamPool(
513500
stream_factory=self._create_new_stream_worker,
514-
min_connections=self.min_connections,
515-
max_connections=self.max_connections,
516-
target_io_depth=self.target_io_depth,
517-
target_bytes=self.target_bytes,
501+
min_connections=self.stream_config.min_connections,
502+
max_connections=self.stream_config.max_connections,
503+
target_io_depth=self.stream_config.target_io_depth,
504+
target_bytes=self.stream_config.target_bytes,
518505
)
519506
await self._pool.add_worker(self._primary_worker)
520507

521-
for _ in range(self.min_connections - 1):
508+
for _ in range(self.stream_config.min_connections - 1):
522509
worker = await self._create_new_stream_worker()
523510
await self._pool.add_worker(worker)
524511
else:
@@ -545,8 +532,6 @@ async def _create_new_stream_worker(self) -> _ManagedStream:
545532
self.generation = stream.generation_number
546533
if stream.read_handle:
547534
self.read_handle = stream.read_handle
548-
if getattr(stream, "routing_token", None):
549-
self._routing_token = stream.routing_token
550535

551536
mux = _StreamMultiplexer(stream)
552537
return _ManagedStream(stream, mux)
@@ -582,8 +567,6 @@ async def factory():
582567
self.generation = stream.generation_number
583568
if stream.read_handle:
584569
self.read_handle = stream.read_handle
585-
if getattr(stream, "routing_token", None):
586-
self._routing_token = stream.routing_token
587570
self.is_finalized = stream.is_finalized
588571
self.full_obj_server_crc32c = stream.full_obj_server_crc32c
589572

@@ -665,13 +648,10 @@ async def _download_ranges_on_worker(
665648
# Heuristic to detect full object reads:
666649
# - Implicit full object read: start offset is 0 and length is 0 (read all).
667650
# - Explicit full object read: start offset is 0 and length matches the exact persisted size.
668-
is_full_object_read = self.is_finalized and (
669-
(offset == 0 and length == 0)
670-
or (
671-
self.persisted_size is not None
672-
and offset == 0
673-
and length == self.persisted_size
674-
)
651+
is_full_object_read = (offset == 0 and length == 0) or (
652+
self.persisted_size is not None
653+
and offset == 0
654+
and length == self.persisted_size
675655
)
676656
download_states[read_id] = _DownloadState(
677657
initial_offset=offset,
@@ -685,9 +665,7 @@ async def _download_ranges_on_worker(
685665
"read_handle": self.read_handle,
686666
"routing_token": None,
687667
"enable_checksum": enable_checksum,
688-
"full_obj_server_crc32c": self.full_obj_server_crc32c
689-
if self.is_finalized
690-
else None,
668+
"full_obj_server_crc32c": self.full_obj_server_crc32c,
691669
}
692670

693671
read_ids = set(download_states.keys())
@@ -787,8 +765,6 @@ async def generator():
787765
if initial_state.get("read_handle"):
788766
self.read_handle = initial_state["read_handle"]
789767
finally:
790-
if self._multiplexer is not None:
791-
self._multiplexer.unregister(read_ids)
792768
if worker.multiplexer is not None:
793769
worker.multiplexer.unregister(read_ids)
794770

‎packages/google-cloud-storage/tests/unit/asyncio/test_async_multi_range_downloader.py‎

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,10 +1029,10 @@ async def test_create_mrd_with_stream_config(self, mock_cls_stream):
10291029
mock_client, "b", "o", stream_config=config
10301030
)
10311031

1032-
assert mrd.min_connections == 2
1033-
assert mrd.max_connections == 4
1034-
assert mrd.target_io_depth == 8
1035-
assert mrd.target_bytes == 2 * 1024 * 1024
1032+
assert mrd.stream_config.min_connections == 2
1033+
assert mrd.stream_config.max_connections == 4
1034+
assert mrd.stream_config.target_io_depth == 8
1035+
assert mrd.stream_config.target_bytes == 2 * 1024 * 1024
10361036
# Verified that 2 streams were opened initially for min_connections=2
10371037
assert len(mrd._pool.workers) == 2
10381038
await mrd.close()
@@ -1223,23 +1223,21 @@ async def test_routing_token_preservation_and_propagation(
12231223
s1 = mock.MagicMock()
12241224
s1.open = AsyncMock()
12251225
s1.generation_number = 100
1226-
s1.routing_token = "token-abc"
12271226
s1.read_handle = b"h1"
12281227
s1.persisted_size = 1000
12291228
s1.is_finalized = True
12301229
s1.full_obj_server_crc32c = 12345
12311230
mock_cls_async_read_object_stream.return_value = s1
12321231

12331232
mrd = await AsyncMultiRangeDownloader.create_mrd(mock_client, "b", "o")
1234-
# Ensure routing token from initial stream was recorded
1235-
assert mrd._routing_token == "token-abc"
1233+
# Simulate a redirect having set _routing_token
1234+
mrd._routing_token = "token-abc"
12361235

12371236
# Now when a new stream worker is opened via _create_new_stream_worker,
12381237
# it should include routing_token in the metadata
12391238
s2 = mock.MagicMock()
12401239
s2.open = AsyncMock()
12411240
s2.generation_number = 100
1242-
s2.routing_token = "token-xyz"
12431241
s2.read_handle = b"h2"
12441242
s2.persisted_size = 1000
12451243
mock_cls_async_read_object_stream.return_value = s2
@@ -1251,7 +1249,5 @@ async def test_routing_token_preservation_and_propagation(
12511249
assert ("x-goog-request-params", "routing_token=token-abc") in call_kwargs[
12521250
"metadata"
12531251
]
1254-
# And after s2 opened, the token should update to s2's routing_token
1255-
assert mrd._routing_token == "token-xyz"
12561252

12571253
await mrd.close()

0 commit comments

Comments
 (0)