From 3827b41d22bd17596a44aa42bacf74a2fadc2cf4 Mon Sep 17 00:00:00 2001 From: Chandra Sirimala Date: Wed, 31 Dec 2025 08:18:12 +0000 Subject: [PATCH 1/3] chore(tests): add test for reading unfinalized appendable objects with generation --- tests/system/test_zonal.py | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/system/test_zonal.py b/tests/system/test_zonal.py index d8d20ba36..19df3c7b6 100644 --- a/tests/system/test_zonal.py +++ b/tests/system/test_zonal.py @@ -255,3 +255,52 @@ async def test_mrd_open_with_read_handle(): del mrd del new_mrd gc.collect() + + +@pytest.mark.asyncio +async def test_read_unfinalized_appendable_object_with_generation( + storage_client, blobs_to_delete +): + object_name = f"read_unfinalized_appendable_object-{str(uuid.uuid4())[:4]}" + grpc_client = AsyncGrpcClient(attempt_direct_path=True).grpc_client + + # First write + writer = AsyncAppendableObjectWriter(grpc_client, _ZONAL_BUCKET, object_name) + await writer.open() + await writer.append(_BYTES_TO_UPLOAD) + await writer.flush() + generation = writer.generation + + # First read + mrd = AsyncMultiRangeDownloader(grpc_client, _ZONAL_BUCKET, object_name) + buffer = BytesIO() + await mrd.open() + assert mrd.persisted_size == len(_BYTES_TO_UPLOAD) + await mrd.download_ranges([(0, 0, buffer)]) + await mrd.close() + assert buffer.getvalue() == _BYTES_TO_UPLOAD + + # Second write, using generation from the first write. + writer_2 = AsyncAppendableObjectWriter( + grpc_client, _ZONAL_BUCKET, object_name, generation=generation + ) + await writer_2.open() + await writer_2.append(_BYTES_TO_UPLOAD) + await writer_2.flush() + + # Second read + mrd_2 = AsyncMultiRangeDownloader(grpc_client, _ZONAL_BUCKET, object_name) + buffer_2 = BytesIO() + await mrd_2.open() + assert mrd_2.persisted_size == 2 * len(_BYTES_TO_UPLOAD) + await mrd_2.download_ranges([(0, 0, buffer_2)]) + await mrd_2.close() + assert buffer_2.getvalue() == _BYTES_TO_UPLOAD + _BYTES_TO_UPLOAD + + # Clean up + blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name)) + del writer + del writer_2 + del mrd + del mrd_2 + gc.collect() From 69035be53fb2918212a085b4708defe8814487ee Mon Sep 17 00:00:00 2001 From: Chandra Sirimala Date: Wed, 31 Dec 2025 08:24:29 +0000 Subject: [PATCH 2/3] read using generation for during 2nd time. --- tests/system/test_zonal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/system/test_zonal.py b/tests/system/test_zonal.py index 19df3c7b6..401412a46 100644 --- a/tests/system/test_zonal.py +++ b/tests/system/test_zonal.py @@ -289,7 +289,9 @@ async def test_read_unfinalized_appendable_object_with_generation( await writer_2.flush() # Second read - mrd_2 = AsyncMultiRangeDownloader(grpc_client, _ZONAL_BUCKET, object_name) + mrd_2 = AsyncMultiRangeDownloader( + grpc_client, _ZONAL_BUCKET, object_name, generation_number=generation + ) buffer_2 = BytesIO() await mrd_2.open() assert mrd_2.persisted_size == 2 * len(_BYTES_TO_UPLOAD) From f4afe282334a2e2fbc6a0767e3d5363c76afc840 Mon Sep 17 00:00:00 2001 From: Chandra Sirimala Date: Wed, 31 Dec 2025 08:29:25 +0000 Subject: [PATCH 3/3] refactor read verification --- tests/system/test_zonal.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/system/test_zonal.py b/tests/system/test_zonal.py index 401412a46..8dc1c5af9 100644 --- a/tests/system/test_zonal.py +++ b/tests/system/test_zonal.py @@ -264,6 +264,21 @@ async def test_read_unfinalized_appendable_object_with_generation( object_name = f"read_unfinalized_appendable_object-{str(uuid.uuid4())[:4]}" grpc_client = AsyncGrpcClient(attempt_direct_path=True).grpc_client + async def _read_and_verify(expected_content, generation=None): + """Helper to read object content and verify against expected.""" + mrd = AsyncMultiRangeDownloader( + grpc_client, _ZONAL_BUCKET, object_name, generation + ) + buffer = BytesIO() + await mrd.open() + try: + assert mrd.persisted_size == len(expected_content) + await mrd.download_ranges([(0, 0, buffer)]) + assert buffer.getvalue() == expected_content + finally: + await mrd.close() + return mrd + # First write writer = AsyncAppendableObjectWriter(grpc_client, _ZONAL_BUCKET, object_name) await writer.open() @@ -272,13 +287,7 @@ async def test_read_unfinalized_appendable_object_with_generation( generation = writer.generation # First read - mrd = AsyncMultiRangeDownloader(grpc_client, _ZONAL_BUCKET, object_name) - buffer = BytesIO() - await mrd.open() - assert mrd.persisted_size == len(_BYTES_TO_UPLOAD) - await mrd.download_ranges([(0, 0, buffer)]) - await mrd.close() - assert buffer.getvalue() == _BYTES_TO_UPLOAD + mrd = await _read_and_verify(_BYTES_TO_UPLOAD) # Second write, using generation from the first write. writer_2 = AsyncAppendableObjectWriter( @@ -289,15 +298,7 @@ async def test_read_unfinalized_appendable_object_with_generation( await writer_2.flush() # Second read - mrd_2 = AsyncMultiRangeDownloader( - grpc_client, _ZONAL_BUCKET, object_name, generation_number=generation - ) - buffer_2 = BytesIO() - await mrd_2.open() - assert mrd_2.persisted_size == 2 * len(_BYTES_TO_UPLOAD) - await mrd_2.download_ranges([(0, 0, buffer_2)]) - await mrd_2.close() - assert buffer_2.getvalue() == _BYTES_TO_UPLOAD + _BYTES_TO_UPLOAD + mrd_2 = await _read_and_verify(_BYTES_TO_UPLOAD + _BYTES_TO_UPLOAD, generation) # Clean up blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name))