Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit c0b4d44

Browse files
committed
address comments and refactor other files
1 parent c59db11 commit c0b4d44

15 files changed

Lines changed: 104 additions & 83 deletions

google/cloud/storage/_experimental/asyncio/_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def raise_if_no_fast_crc32c():
3434
"For more information, see https://github.com/googleapis/python-crc32c."
3535
)
3636

37+
3738
def update_write_handle_if_exists(obj, response):
3839
"""Update the write_handle attribute of an object if it exists in the response."""
3940
if hasattr(response, "write_handle") and response.write_handle is not None:

google/cloud/storage/_experimental/asyncio/async_appendable_object_writer.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ def _is_write_retryable(exc):
102102
if detail.type_url == _BIDI_WRITE_REDIRECTED_TYPE_URL:
103103
return True
104104
except Exception:
105-
logger.error("Error unpacking redirect details from gRPC error. Exception: ", {exc})
105+
logger.error(
106+
"Error unpacking redirect details from gRPC error. Exception: ",
107+
{exc},
108+
)
106109
return False
107110
return False
108111

@@ -296,7 +299,10 @@ async def _do_open():
296299
try:
297300
await self.write_obj_stream.close()
298301
except Exception as e:
299-
logger.warning("Error closing previous write stream during open retry. Got exception: ", {e})
302+
logger.warning(
303+
"Error closing previous write stream during open retry. Got exception: ",
304+
{e},
305+
)
300306
self.write_obj_stream = None
301307
self._is_stream_open = False
302308

@@ -567,7 +573,6 @@ async def finalize(self) -> _storage_v2.Object:
567573
def is_stream_open(self) -> bool:
568574
return self._is_stream_open
569575

570-
571576
# helper methods.
572577
async def append_from_string(self, data: str):
573578
"""

google/cloud/storage/_experimental/asyncio/async_grpc_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class AsyncGrpcClient:
4242
(Optional) Whether to attempt to use DirectPath for gRPC connections.
4343
Defaults to ``True``.
4444
"""
45+
4546
def __init__(
4647
self,
4748
credentials=None,

google/cloud/storage/_experimental/asyncio/async_write_object_stream.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424
from typing import List, Optional, Tuple
2525
from google.cloud import _storage_v2
26+
from google.cloud.storage._experimental.asyncio import _utils
2627
from google.cloud.storage._experimental.asyncio.async_grpc_client import AsyncGrpcClient
2728
from google.cloud.storage._experimental.asyncio.async_abstract_object_stream import (
2829
_AsyncAbstractObjectStream,
@@ -59,7 +60,7 @@ class _AsyncWriteObjectStream(_AsyncAbstractObjectStream):
5960
same name already exists, it will be overwritten the moment
6061
`writer.open()` is called.
6162
62-
:type write_handle: bytes
63+
:type write_handle: _storage_v2.BidiWriteHandle
6364
:param write_handle: (Optional) An existing handle for writing the object.
6465
If provided, opening the bidi-gRPC connection will be faster.
6566
"""
@@ -70,7 +71,7 @@ def __init__(
7071
bucket_name: str,
7172
object_name: str,
7273
generation_number: Optional[int] = None, # None means new object
73-
write_handle: Optional[bytes] = None,
74+
write_handle: Optional[_storage_v2.BidiWriteHandle] = None,
7475
routing_token: Optional[str] = None,
7576
) -> None:
7677
if client is None:
@@ -86,7 +87,7 @@ def __init__(
8687
generation_number=generation_number,
8788
)
8889
self.client: AsyncGrpcClient.grpc_client = client
89-
self.write_handle: Optional[bytes] = write_handle
90+
self.write_handle: Optional[_storage_v2.BidiWriteHandle] = write_handle
9091
self.routing_token: Optional[str] = routing_token
9192

9293
self._full_bucket_name = f"projects/_/buckets/{self.bucket_name}"
@@ -117,8 +118,6 @@ async def open(self, metadata: Optional[List[Tuple[str, str]]] = None) -> None:
117118
if self._is_stream_open:
118119
raise ValueError("Stream is already open")
119120

120-
write_handle = self.write_handle if self.write_handle else None
121-
122121
# Create a new object or overwrite existing one if generation_number
123122
# is None. This makes it consistent with GCS JSON API behavior.
124123
# Created object type would be Appendable Object.
@@ -140,27 +139,26 @@ async def open(self, metadata: Optional[List[Tuple[str, str]]] = None) -> None:
140139
bucket=self._full_bucket_name,
141140
object=self.object_name,
142141
generation=self.generation_number,
143-
write_handle=write_handle,
142+
write_handle=self.write_handle if self.write_handle else None,
144143
routing_token=self.routing_token if self.routing_token else None,
145144
),
146145
)
147146

148-
request_params = [f"bucket={self._full_bucket_name}"]
149-
other_metadata = []
147+
request_param_values = [f"bucket={self._full_bucket_name}"]
148+
final_metadata = []
150149
if metadata:
151150
for key, value in metadata:
152151
if key == "x-goog-request-params":
153-
request_params.append(value)
152+
request_param_values.append(value)
154153
else:
155-
other_metadata.append((key, value))
154+
final_metadata.append((key, value))
156155

157-
current_metadata = other_metadata
158-
current_metadata.append(("x-goog-request-params", ",".join(request_params)))
156+
final_metadata.append(("x-goog-request-params", ",".join(request_param_values)))
159157

160158
self.socket_like_rpc = AsyncBidiRpc(
161159
self.rpc,
162160
initial_request=self.first_bidi_write_req,
163-
metadata=current_metadata,
161+
metadata=final_metadata,
164162
)
165163

166164
await self.socket_like_rpc.open() # this is actually 1 send
@@ -194,7 +192,7 @@ async def requests_done(self):
194192
"""Signals that all requests have been sent."""
195193

196194
await self.socket_like_rpc.send(None)
197-
await self.socket_like_rpc.recv()
195+
_utils.update_write_handle_if_exists(self, await self.socket_like_rpc.recv())
198196

199197
async def send(
200198
self, bidi_write_object_request: _storage_v2.BidiWriteObjectRequest
@@ -236,4 +234,3 @@ async def recv(self) -> _storage_v2.BidiWriteObjectResponse:
236234
@property
237235
def is_stream_open(self) -> bool:
238236
return self._is_stream_open
239-

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

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@
2525
)
2626

2727

28-
_BIDI_WRITE_REDIRECTED_TYPE_URL = (
29-
"type.googleapis.com/google.storage.v2.BidiWriteObjectRedirectedError"
30-
)
31-
32-
3328
class _WriteState:
3429
"""A helper class to track the state of a single upload operation.
3530
@@ -39,22 +34,22 @@ class _WriteState:
3934
:type user_buffer: IO[bytes]
4035
:param user_buffer: The data source.
4136
42-
:type flush_interval: Optional[int]
37+
:type flush_interval: int
4338
:param flush_interval: The flush interval at which the data is flushed.
4439
"""
4540

4641
def __init__(
4742
self,
4843
chunk_size: int,
4944
user_buffer: IO[bytes],
50-
flush_interval: Optional[int] = None,
45+
flush_interval: int,
5146
):
5247
self.chunk_size = chunk_size
5348
self.user_buffer = user_buffer
5449
self.persisted_size: int = 0
5550
self.bytes_sent: int = 0
5651
self.bytes_since_last_flush: int = 0
57-
self.flush_interval: Optional[int] = flush_interval
52+
self.flush_interval: int = flush_interval
5853
self.write_handle: Union[bytes, storage_type.BidiWriteHandle, None] = None
5954
self.routing_token: Optional[str] = None
6055
self.is_finalized: bool = False
@@ -94,10 +89,7 @@ def generate_requests(
9489
write_state.bytes_sent += chunk_len
9590
write_state.bytes_since_last_flush += chunk_len
9691

97-
if (
98-
write_state.flush_interval
99-
and write_state.bytes_since_last_flush >= write_state.flush_interval
100-
):
92+
if write_state.bytes_since_last_flush >= write_state.flush_interval:
10193
request.flush = True
10294
# reset counter after marking flush
10395
write_state.bytes_since_last_flush = 0

tests/perf/microbenchmarks/_utils.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def publish_benchmark_extra_info(
4343

4444
object_size = params.file_size_bytes
4545
num_files = params.num_files
46-
total_uploaded_mib = (object_size / (1024 * 1024) * num_files)
46+
total_uploaded_mib = object_size / (1024 * 1024) * num_files
4747
min_throughput = total_uploaded_mib / benchmark.stats["max"]
4848
max_throughput = total_uploaded_mib / benchmark.stats["min"]
4949
mean_throughput = total_uploaded_mib / benchmark.stats["mean"]
@@ -80,8 +80,8 @@ def publish_benchmark_extra_info(
8080

8181
# Get benchmark name, rounds, and iterations
8282
name = benchmark.name
83-
rounds = benchmark.stats['rounds']
84-
iterations = benchmark.stats['iterations']
83+
rounds = benchmark.stats["rounds"]
84+
iterations = benchmark.stats["iterations"]
8585

8686
# Header for throughput table
8787
header = "\n\n" + "-" * 125 + "\n"
@@ -98,13 +98,15 @@ def publish_benchmark_extra_info(
9898
print(row)
9999
print("-" * 125)
100100

101+
101102
class RandomBytesIO(io.RawIOBase):
102103
"""
103104
A file-like object that generates random bytes using os.urandom.
104105
It enforces a fixed size and an upper safety cap.
105106
"""
107+
106108
# 10 GiB default safety cap
107-
DEFAULT_CAP = 10 * 1024 * 1024 * 1024
109+
DEFAULT_CAP = 10 * 1024 * 1024 * 1024
108110

109111
def __init__(self, size, max_size=DEFAULT_CAP):
110112
"""
@@ -114,9 +116,11 @@ def __init__(self, size, max_size=DEFAULT_CAP):
114116
"""
115117
if size is None:
116118
raise ValueError("Size must be defined (cannot be infinite).")
117-
119+
118120
if size > max_size:
119-
raise ValueError(f"Requested size {size} exceeds the maximum limit of {max_size} bytes (10 GiB).")
121+
raise ValueError(
122+
f"Requested size {size} exceeds the maximum limit of {max_size} bytes (10 GiB)."
123+
)
120124

121125
self._size = size
122126
self._pos = 0
@@ -125,15 +129,15 @@ def read(self, n=-1):
125129
# 1. Handle "read all" (n=-1)
126130
if n is None or n < 0:
127131
n = self._size - self._pos
128-
132+
129133
# 2. Handle EOF (End of File)
130134
if self._pos >= self._size:
131135
return b""
132-
136+
133137
# 3. Clamp read amount to remaining size
134138
# This ensures we stop exactly at `size` bytes.
135139
n = min(n, self._size - self._pos)
136-
140+
137141
# 4. Generate data
138142
data = os.urandom(n)
139143
self._pos += len(data)

tests/perf/microbenchmarks/conftest.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def storage_client():
4949
with contextlib.closing(client):
5050
yield client
5151

52+
5253
@pytest.fixture
5354
def monitor():
5455
"""
@@ -57,6 +58,7 @@ def monitor():
5758
"""
5859
return ResourceMonitor
5960

61+
6062
def publish_resource_metrics(benchmark: Any, monitor: ResourceMonitor) -> None:
6163
"""
6264
Helper function to publish resource monitor results to the extra_info property.
@@ -76,7 +78,10 @@ async def upload_appendable_object(bucket_name, object_name, object_size, chunk_
7678
# this method is to write "appendable" objects which will be used for
7779
# benchmarking reads, hence not concerned performance of writes here.
7880
writer = AsyncAppendableObjectWriter(
79-
AsyncGrpcClient().grpc_client, bucket_name, object_name, writer_options={"FLUSH_INTERVAL_BYTES": 1026 * 1024 ** 2}
81+
AsyncGrpcClient().grpc_client,
82+
bucket_name,
83+
object_name,
84+
writer_options={"FLUSH_INTERVAL_BYTES": 1026 * 1024**2},
8085
)
8186
await writer.open()
8287
uploaded_bytes = 0
@@ -106,11 +111,15 @@ def _upload_worker(args):
106111
upload_appendable_object(bucket_name, object_name, object_size, chunk_size)
107112
)
108113
else:
109-
uploaded_bytes = upload_simple_object(bucket_name, object_name, object_size, chunk_size)
114+
uploaded_bytes = upload_simple_object(
115+
bucket_name, object_name, object_size, chunk_size
116+
)
110117
return object_name, uploaded_bytes
111118

112119

113-
def _create_files(num_files, bucket_name, bucket_type, object_size, chunk_size=1024 * 1024 * 1024):
120+
def _create_files(
121+
num_files, bucket_name, bucket_type, object_size, chunk_size=1024 * 1024 * 1024
122+
):
114123
"""
115124
Create/Upload objects for benchmarking and return a list of their names.
116125
"""

tests/perf/microbenchmarks/reads/config.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@ def _get_params() -> Dict[str, List[ReadParameters]]:
5454
rounds = common_params["rounds"]
5555

5656
bucket_map = {
57-
"zonal": os.environ.get("DEFAULT_RAPID_ZONAL_BUCKET", config['defaults']['DEFAULT_RAPID_ZONAL_BUCKET']),
58-
"regional": os.environ.get("DEFAULT_STANDARD_BUCKET", config['defaults']['DEFAULT_STANDARD_BUCKET'])
57+
"zonal": os.environ.get(
58+
"DEFAULT_RAPID_ZONAL_BUCKET",
59+
config["defaults"]["DEFAULT_RAPID_ZONAL_BUCKET"],
60+
),
61+
"regional": os.environ.get(
62+
"DEFAULT_STANDARD_BUCKET", config["defaults"]["DEFAULT_STANDARD_BUCKET"]
63+
),
5964
}
6065

6166
for workload in config["workload"]:

tests/perf/microbenchmarks/reads/test_reads.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ def download_files_using_mrd_multi_coro(loop, client, files, other_params, chunk
180180
Returns:
181181
float: The maximum latency (in seconds) among all coroutines.
182182
"""
183+
183184
async def main():
184185
if len(files) == 1:
185186
result = await download_chunks_using_mrd_async(
@@ -356,8 +357,7 @@ def download_files_mp_mc_wrapper(files_names, params, chunks, bucket_type):
356357

357358
@pytest.mark.parametrize(
358359
"workload_params",
359-
all_params["read_seq_multi_process"] +
360-
all_params["read_rand_multi_process"],
360+
all_params["read_seq_multi_process"] + all_params["read_rand_multi_process"],
361361
indirect=True,
362362
ids=lambda p: p.name,
363363
)
@@ -412,4 +412,4 @@ def target_wrapper(*args, **kwargs):
412412

413413
blobs_to_delete.extend(
414414
storage_client.bucket(params.bucket_name).blob(f) for f in files_names
415-
)
415+
)

tests/perf/microbenchmarks/resource_monitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,4 @@ def throughput_mb_s(self):
9696
"""Calculates combined network throughput."""
9797
if self.duration <= 0:
9898
return 0.0
99-
return (self.net_sent_mb + self.net_recv_mb) / self.duration
99+
return (self.net_sent_mb + self.net_recv_mb) / self.duration

0 commit comments

Comments
 (0)