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

Commit a8940f8

Browse files
googlyrahmanchandra-sirigemini-code-assist[bot]release-please[bot]chalmerlowe
authored
feat(storage): sync async with main branch (#1754)
Sync async with main branch --------- Co-authored-by: Chandra Shekhar Sirimala <chandrasiri@google.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: Chalmer Lowe <chalmerlowe@google.com> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Anthonios Partheniou <partheniou@google.com> Co-authored-by: Pulkit Aggarwal <54775856+Pulkit0110@users.noreply.github.com> Co-authored-by: Victor Chudnovsky <vchudnov@google.com> Co-authored-by: gurusai-voleti <gvoleti@google.com>
1 parent 60d65d2 commit a8940f8

14 files changed

Lines changed: 125 additions & 150 deletions

File tree

.github/sync-repo-settings.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ branchProtectionRules:
1010
- 'Kokoro'
1111
- 'cla/google'
1212
- 'Kokoro system-3.14'
13-
- 'Kokoro system-3.9'
13+
- 'Kokoro system-3.10'
1414
- 'OwlBot Post Processor'

.kokoro/presubmit/system-3.10.cfg

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Format: //devtools/kokoro/config/proto/build.proto
2+
3+
# Only run this nox session.
4+
env_vars: {
5+
key: "NOX_SESSION"
6+
value: "system-3.10"
7+
}
8+
9+
# Credentials needed to test universe domain.
10+
env_vars: {
11+
key: "SECRET_MANAGER_KEYS"
12+
value: "client-library-test-universe-domain-credential"
13+
}

.librarian/generator-input/noxfile.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"]
2828

2929
DEFAULT_PYTHON_VERSION = "3.14"
30-
SYSTEM_TEST_PYTHON_VERSIONS = ["3.9", "3.14"]
31-
UNIT_TEST_PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
30+
SYSTEM_TEST_PYTHON_VERSIONS = ["3.10", "3.14"]
31+
UNIT_TEST_PYTHON_VERSIONS = ["3.10", "3.11", "3.12", "3.13", "3.14"]
3232
CONFORMANCE_TEST_PYTHON_VERSIONS = ["3.12"]
3333

3434
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
@@ -44,9 +44,6 @@
4444
"lint",
4545
"lint_setup_py",
4646
"system",
47-
# TODO(https://github.com/googleapis/python-storage/issues/1499):
48-
# Remove or restore testing for Python 3.7/3.8
49-
"unit-3.9",
5047
"unit-3.10",
5148
"unit-3.11",
5249
"unit-3.12",

.librarian/generator-input/setup.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,6 @@
8787
"License :: OSI Approved :: Apache Software License",
8888
"Programming Language :: Python",
8989
"Programming Language :: Python :: 3",
90-
"Programming Language :: Python :: 3.7",
91-
"Programming Language :: Python :: 3.8",
92-
"Programming Language :: Python :: 3.9",
9390
"Programming Language :: Python :: 3.10",
9491
"Programming Language :: Python :: 3.11",
9592
"Programming Language :: Python :: 3.12",

google/cloud/storage/asyncio/async_appendable_object_writer.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ def __init__(
211211
self.bytes_appended_since_last_flush = 0
212212
self._routing_token: Optional[str] = None
213213
self.object_resource: Optional[_storage_v2.Object] = None
214+
self._flush_count = 0
214215

215216
async def state_lookup(self) -> int:
216217
"""Returns the persisted_size
@@ -318,6 +319,8 @@ async def _do_open():
318319
self.write_handle = self.write_obj_stream.write_handle
319320
if self.write_obj_stream.persisted_size is not None:
320321
self.persisted_size = self.write_obj_stream.persisted_size
322+
# set offset while opening
323+
self.offset = self.persisted_size
321324

322325
self._is_stream_open = True
323326
self._routing_token = None
@@ -403,36 +406,40 @@ async def generator():
403406
write_state.user_buffer.seek(write_state.persisted_size)
404407
write_state.bytes_sent = write_state.persisted_size
405408
write_state.bytes_since_last_flush = 0
409+
self.bytes_appended_since_last_flush = 0
406410

407411
requests = strategy.generate_requests(state)
408412

409-
num_requests = len(requests)
410-
for i, chunk_req in enumerate(requests):
411-
if i == num_requests - 1:
412-
chunk_req.state_lookup = True
413-
chunk_req.flush = True
413+
for chunk_req in requests:
414414
await self.write_obj_stream.send(chunk_req)
415-
416-
resp = await self.write_obj_stream.recv()
417-
if resp:
418-
if resp.persisted_size is not None:
419-
self.persisted_size = resp.persisted_size
420-
state["write_state"].persisted_size = resp.persisted_size
421-
self.offset = self.persisted_size
422-
if resp.write_handle:
423-
self.write_handle = resp.write_handle
424-
state["write_state"].write_handle = resp.write_handle
425-
self.bytes_appended_since_last_flush = 0
426-
427-
yield resp
415+
if chunk_req.flush:
416+
self._flush_count += 1
417+
418+
resp = None
419+
if chunk_req.state_lookup:
420+
# TODO: if there's error, it'll raise error
421+
# and will be handled by `recover_state_on_failure`
422+
resp = await self.write_obj_stream.recv()
423+
424+
if resp:
425+
if resp.persisted_size is not None:
426+
self.persisted_size = resp.persisted_size
427+
state["write_state"].persisted_size = resp.persisted_size
428+
self.offset = self.persisted_size
429+
if resp.write_handle:
430+
self.write_handle = resp.write_handle
431+
state["write_state"].write_handle = resp.write_handle
432+
433+
yield resp
428434

429435
return generator()
430436

431437
# State initialization
432438
write_state = _WriteState(_MAX_CHUNK_SIZE_BYTES, buffer, self.flush_interval)
433439
write_state.write_handle = self.write_handle
434440
write_state.persisted_size = self.persisted_size
435-
write_state.bytes_sent = self.persisted_size
441+
# offset is set during `open()` call.
442+
write_state.bytes_sent = self.offset or 0
436443
write_state.bytes_since_last_flush = self.bytes_appended_since_last_flush
437444

438445
retry_manager = _BidiStreamRetryManager(
@@ -442,11 +449,8 @@ async def generator():
442449
await retry_manager.execute({"write_state": write_state}, retry_policy)
443450

444451
# Sync local markers
445-
self.write_obj_stream.persisted_size = write_state.persisted_size
446-
self.write_obj_stream.write_handle = write_state.write_handle
447452
self.bytes_appended_since_last_flush = write_state.bytes_since_last_flush
448-
self.persisted_size = write_state.persisted_size
449-
self.offset = write_state.persisted_size
453+
self.offset = write_state.bytes_sent
450454

451455
async def simple_flush(self) -> None:
452456
"""Flushes the data to the server.
@@ -516,7 +520,6 @@ async def close(self, finalize_on_close=False) -> Union[int, _storage_v2.Object]
516520
await self.write_obj_stream.close()
517521

518522
self._is_stream_open = False
519-
self.offset = None
520523
return self.persisted_size
521524

522525
async def finalize(self) -> _storage_v2.Object:

google/cloud/storage/asyncio/async_write_object_stream.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ async def requests_done(self):
190190
_utils.update_write_handle_if_exists(self, first_resp)
191191

192192
if first_resp != grpc.aio.EOF:
193-
self.persisted_size = first_resp.persisted_size
193+
# this persisted_size will not be upto date., also what if response
194+
# doesn't have persisted_size? , it'll throw error.
195+
if hasattr(first_resp, "persisted_size"):
196+
self.persisted_size = first_resp.persisted_size
194197
second_resp = await self.socket_like_rpc.recv()
195198
assert second_resp == grpc.aio.EOF
196199

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ def __init__(
4747
self.chunk_size = chunk_size
4848
self.user_buffer = user_buffer
4949
self.persisted_size: int = 0
50+
# Bytes sent to the server (it may be unpersisted),
51+
# i.e. latest object size = persisted_size + some more bytes.
52+
# Please note: these bytes are sent from client to server, server might have also received it.
53+
# but might not have persisted it yet (may be in memory buffer on server side).
54+
# This variable is same as `offset variable` in the instance of `AppendableObjectWriter`.
5055
self.bytes_sent: int = 0
5156
self.bytes_since_last_flush: int = 0
5257
self.flush_interval: int = flush_interval
@@ -91,7 +96,7 @@ def generate_requests(
9196

9297
if write_state.bytes_since_last_flush >= write_state.flush_interval:
9398
request.flush = True
94-
# reset counter after marking flush
99+
request.state_lookup = True
95100
write_state.bytes_since_last_flush = 0
96101

97102
requests.append(request)

noxfile.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from __future__ import absolute_import
1818
import os
1919
import pathlib
20-
import re
2120
import shutil
2221

2322
import nox
@@ -27,9 +26,8 @@
2726
BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"]
2827

2928
DEFAULT_PYTHON_VERSION = "3.14"
30-
SYSTEM_TEST_PYTHON_VERSIONS = ["3.9", "3.14"]
29+
SYSTEM_TEST_PYTHON_VERSIONS = ["3.10", "3.14"]
3130
UNIT_TEST_PYTHON_VERSIONS = [
32-
"3.9",
3331
"3.10",
3432
"3.11",
3533
"3.12",
@@ -51,9 +49,6 @@
5149
"lint",
5250
"lint_setup_py",
5351
"system",
54-
# TODO(https://github.com/googleapis/python-storage/issues/1499):
55-
# Remove or restore testing for Python 3.7/3.8
56-
"unit-3.9",
5752
"unit-3.10",
5853
"unit-3.11",
5954
"unit-3.12",

samples/snippets/zonal_buckets/storage_read_appendable_object_tail.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
AsyncMultiRangeDownloader,
2929
)
3030

31-
BYTES_TO_APPEND = b"fav_bytes."
31+
BYTES_TO_APPEND = b"fav_bytes." * 100 * 1024 * 1024
3232
NUM_BYTES_TO_APPEND_EVERY_SECOND = len(BYTES_TO_APPEND)
3333

3434

@@ -37,14 +37,16 @@ async def appender(writer: AsyncAppendableObjectWriter, duration: int):
3737
"""Appends 10 bytes to the object every second for a given duration."""
3838
print("Appender started.")
3939
bytes_appended = 0
40-
for i in range(duration):
40+
start_time = time.monotonic()
41+
# Run the appender for the specified duration.
42+
while time.monotonic() - start_time < duration:
4143
await writer.append(BYTES_TO_APPEND)
4244
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
4345
bytes_appended += NUM_BYTES_TO_APPEND_EVERY_SECOND
4446
print(
4547
f"[{now}] Appended {NUM_BYTES_TO_APPEND_EVERY_SECOND} new bytes. Total appended: {bytes_appended} bytes."
4648
)
47-
await asyncio.sleep(1)
49+
await asyncio.sleep(0.1)
4850
print("Appender finished.")
4951

5052

@@ -67,9 +69,7 @@ async def tailer(
6769
bytes_downloaded = output_buffer.getbuffer().nbytes
6870
if bytes_downloaded > 0:
6971
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
70-
print(
71-
f"[{now}] Tailer read {bytes_downloaded} new bytes: {output_buffer.getvalue()}"
72-
)
72+
print(f"[{now}] Tailer read {bytes_downloaded} new bytes: ")
7373
start_byte += bytes_downloaded
7474

7575
await asyncio.sleep(0.1) # Poll for new data every 0.1 seconds.

setup.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,6 @@
123123
"License :: OSI Approved :: Apache Software License",
124124
"Programming Language :: Python",
125125
"Programming Language :: Python :: 3",
126-
"Programming Language :: Python :: 3.7",
127-
"Programming Language :: Python :: 3.8",
128-
"Programming Language :: Python :: 3.9",
129126
"Programming Language :: Python :: 3.10",
130127
"Programming Language :: Python :: 3.11",
131128
"Programming Language :: Python :: 3.12",
@@ -138,7 +135,7 @@
138135
packages=packages,
139136
install_requires=dependencies,
140137
extras_require=extras,
141-
python_requires=">=3.7",
138+
python_requires=">=3.10",
142139
include_package_data=True,
143140
zip_safe=False,
144141
)

0 commit comments

Comments
 (0)