Skip to content

Commit 18e691a

Browse files
[Storage] Prepare hotfix release (#34608)
1 parent 9979486 commit 18e691a

File tree

7 files changed

+90
-4
lines changed

7 files changed

+90
-4
lines changed

sdk/storage/azure-storage-blob/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release History
22

3+
## 12.19.1 (2024-03-04)
4+
5+
### Bugs Fixed
6+
- Fixed an issue where under rare circumstances, full downloads of sparse Page Blobs could result in the
7+
downloaded content containing up to one "chunk" of extra `\x00` at the end due to an optimization error.
8+
39
## 12.19.0 (2023-11-07)
410

511
### Features Added

sdk/storage/azure-storage-blob/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/storage/azure-storage-blob",
5-
"Tag": "python/storage/azure-storage-blob_12c8154ae2"
5+
"Tag": "python/storage/azure-storage-blob_1b66da54e8"
66
}

sdk/storage/azure-storage-blob/azure/storage/blob/_download.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ def _download_chunk(self, chunk_start, chunk_end):
186186
# No need to download the empty chunk from server if there's no data in the chunk to be downloaded.
187187
# Do optimize and create empty chunk locally if condition is met.
188188
if self._do_optimize(download_range[0], download_range[1]):
189-
chunk_data = b"\x00" * self.chunk_size
189+
data_size = download_range[1] - download_range[0] + 1
190+
chunk_data = b"\x00" * data_size
190191
else:
191192
range_header, range_validation = validate_and_format_range_headers(
192193
download_range[0],

sdk/storage/azure-storage-blob/azure/storage/blob/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
# license information.
55
# --------------------------------------------------------------------------
66

7-
VERSION = "12.19.0"
7+
VERSION = "12.19.1"

sdk/storage/azure-storage-blob/azure/storage/blob/aio/_download_async.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ async def _download_chunk(self, chunk_start, chunk_end):
9595
# No need to download the empty chunk from server if there's no data in the chunk to be downloaded.
9696
# Do optimize and create empty chunk locally if condition is met.
9797
if self._do_optimize(download_range[0], download_range[1]):
98-
chunk_data = b"\x00" * self.chunk_size
98+
data_size = download_range[1] - download_range[0] + 1
99+
chunk_data = b"\x00" * data_size
99100
else:
100101
range_header, range_validation = validate_and_format_range_headers(
101102
download_range[0],

sdk/storage/azure-storage-blob/tests/test_page_blob.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,45 @@ def test_download_sparse_page_blob_parallel(self, **kwargs):
22692269

22702270
content = blob_client.download_blob(max_concurrency=3).readall()
22712271

2272+
@BlobPreparer()
2273+
@recorded_by_proxy
2274+
def test_download_sparse_page_blob_uneven_chunks(self, **kwargs):
2275+
storage_account_name = kwargs.pop("storage_account_name")
2276+
storage_account_key = kwargs.pop("storage_account_key")
2277+
2278+
# Arrange
2279+
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), credential=storage_account_key)
2280+
self._setup(bsc)
2281+
2282+
# Choose an initial size, chunk size, and blob size, so the last chunk spills over end of blob
2283+
self.config.max_single_get_size = 4 * 1024
2284+
self.config.max_chunk_get_size = 4 * 1024
2285+
sparse_page_blob_size = 10 * 1024
2286+
2287+
blob_client = self._get_blob_reference(bsc)
2288+
blob_client.create_page_blob(sparse_page_blob_size)
2289+
2290+
data = b'12345678' * 128 # 1024 bytes
2291+
range_start = 2 * 1024 + 512
2292+
blob_client.upload_page(data, offset=range_start, length=len(data))
2293+
2294+
# Act
2295+
content = blob_client.download_blob().readall()
2296+
2297+
# Assert
2298+
assert sparse_page_blob_size == len(content)
2299+
start = end = 0
2300+
for r in blob_client.list_page_ranges():
2301+
if not r.cleared:
2302+
start = r.start
2303+
end = r.end
2304+
2305+
assert data == content[start: end + 1]
2306+
for byte in content[:start - 1]:
2307+
assert byte == 0
2308+
for byte in content[end + 1:]:
2309+
assert byte == 0
2310+
22722311
@BlobPreparer()
22732312
@recorded_by_proxy
22742313
def test_upload_progress_chunked_non_parallel(self, **kwargs):

sdk/storage/azure-storage-blob/tests/test_page_blob_async.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,6 +2238,45 @@ async def test_download_sparse_page_blob(self, storage_account_name, storage_acc
22382238
except:
22392239
assert byte == 0
22402240

2241+
@BlobPreparer()
2242+
@recorded_by_proxy_async
2243+
async def test_download_sparse_page_blob_uneven_chunks(self, **kwargs):
2244+
storage_account_name = kwargs.pop("storage_account_name")
2245+
storage_account_key = kwargs.pop("storage_account_key")
2246+
2247+
# Arrange
2248+
bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), credential=storage_account_key)
2249+
await self._setup(bsc)
2250+
2251+
# Choose an initial size, chunk size, and blob size, so the last chunk spills over end of blob
2252+
self.config.max_single_get_size = 4 * 1024
2253+
self.config.max_chunk_get_size = 4 * 1024
2254+
sparse_page_blob_size = 10 * 1024
2255+
2256+
blob_client = self._get_blob_reference(bsc)
2257+
await blob_client.create_page_blob(sparse_page_blob_size)
2258+
2259+
data = b'12345678' * 128 # 1024 bytes
2260+
range_start = 2 * 1024 + 512
2261+
await blob_client.upload_page(data, offset=range_start, length=len(data))
2262+
2263+
# Act
2264+
content = await (await blob_client.download_blob()).readall()
2265+
2266+
# Assert
2267+
assert sparse_page_blob_size == len(content)
2268+
start = end = 0
2269+
async for r in blob_client.list_page_ranges():
2270+
if not r.cleared:
2271+
start = r.start
2272+
end = r.end
2273+
2274+
assert data == content[start: end + 1]
2275+
for byte in content[:start - 1]:
2276+
assert byte == 0
2277+
for byte in content[end + 1:]:
2278+
assert byte == 0
2279+
22412280
@BlobPreparer()
22422281
@recorded_by_proxy_async
22432282
async def test_upload_progress_chunked_non_parallel(self, **kwargs):

0 commit comments

Comments
 (0)