Copy Blob shares the source's page ranges and append-block list with the copy: writing to the copy changes the source, and writing to a restored blob changes its snapshot
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 78/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- typescript
Direzione di ricerca
Inizia da src/blob/persistence/LokiBlobMetadataStore.ts, nei percorsi startCopyFromURL e copyFromURL indicati nell’issue. Poi esamina createSnapshot per capire come copia l’intervallo di pagine e gli array di blocchi confermati. Riproduci i casi di page blob, ripristino dello snapshot e append blob dello script. Il lavoro è completato quando le scritture su un blob copiato non modificano più la sua origine né il suo snapshot e ogni blob restituisce i dati e il comportamento ETag previsti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Which service(blob, file, queue, table) does this issue concern?
Blob
Which version of the Azurite was used?
3.37.0 (also seen on 3.29.0)
Where do you get Azurite? (npm, DockerHub, NuGet, Visual Studio Code Extension)
Docker image mcr.microsoft.com/azure-storage/azurite:3.37.0 (same digest as latest)
What's the Node.js version?
v22.23.2 (the one in the image)
What problem was encountered?
After Copy Blob copies a page blob, the copy and the source share one list of page ranges. A Put
Page on either blob then changes the content of both. The other blob's ETag does not change, so a
client cannot see that its data changed. Append blobs share their list of
committed blocks in the same way.
Three cases (script below):
- Copy, then write the copy.
p1is a 1 KiB page blob that is never written. Copy Blob
p1 -> p2, then Put Page0-511withbonp2. Get Blob onp1now returns 512bbytes
followed by 512 zero bytes, with the same ETag as before. - Restore a snapshot, then write the blob.
p3holdsain0-511. Snapshot Blobp3,
restore the snapshot overp3with Copy Blob (x-ms-copy-source: .../p3?snapshot=<id>), then
Put Page0-511withconp3. The snapshot now readsc. The backup that the restore came
from has been overwritten by the next write to the restored blob. - Append blobs.
a1holdsx. Copy Bloba1 -> a2, Append Blockytoa2, then Append
Blockztoa1. Get Blob ona1returnsxy, notxz: the block appended to the copy is
now part of the source, and the source's ownzis not visible. Before the last append,
x-ms-blob-committed-block-countofa1was already2.
The documentation describes the copy as a new blob that receives the source's data, and a
snapshot as read-only. Copy Blob, remarks:
When you're copying from a page blob, Blob Storage creates a destination page blob of the source
blob's length. Initially, the page blob contains all zeroes. Then the source page ranges are
enumerated, and non-empty ranges are copied.
You can perform a copy operation to promote a snapshot over its base blob, as long as it's in an
online tier (hot, cool, or cold). In this way, you can restore an earlier version of a blob. The
snapshot remains, but its destination is overwritten with a copy that can be both read and
written.
Snapshots provide read-only versions of blobs. After you create a snapshot, you can read, copy,
or delete it, but you can't modify it.
Put Page: "The Put Page
operation writes a range of pages to a page blob." That is, to the blob in the request URL.
Append Block: "The
Append Block operation commits a new block of data to the end of an existing append blob."
Expected: in the first case p1 stays all zeroes; in the second the snapshot keeps a; in the
third a1 reads xz.
Steps to reproduce the issue?
docker run -d -p 10000:10000 mcr.microsoft.com/azure-storage/azurite:3.37.0 azurite-blob --blobHost 0.0.0.0
Then:
B=http://127.0.0.1:10000/devstoreaccount1
# Account SAS derived from Azurite's public default development key, not a cloud credential.
SAS='sv=2023-11-03&ss=b&srt=sco&sp=rwdl&se=2099-01-01&sig=gF53rwiyjUw0Grh2NB8D%2Bw%2FK1kP1kspe6Cj3FCMjg%2F0%3D'
V='x-ms-version: 2023-11-03'
req() { label=$1; shift; printf '%-42s %s\n' "$label" "$(curl -s -o /dev/null -w '%{http_code}' -X PUT -H "$V" -H 'Content-Length: 0' "$@")"; }
page() { printf '%-42s %s\n' "$1" "$(head -c 512 /dev/zero | tr '\0' "$2" | curl -s -o /dev/null -w '%{http_code}' -X PUT -H "$V" \
-H 'x-ms-page-write: update' -H 'x-ms-range: bytes=0-511' --data-binary @- "$3")"; }
first() { printf '%-42s%s\n' "$1" "$(curl -s -H "$V" "$2" | od -An -c -N4)"; }
req 'create container' "$B/pages?restype=container&$SAS"
# A. p1: a 1 KiB page blob that is never written. p2: a copy of p1. Write 'b' to p2.
req 'create page blob p1 (1024 bytes)' -H 'x-ms-blob-type: PageBlob' -H 'x-ms-blob-content-length: 1024' "$B/pages/p1?$SAS"
req 'Copy Blob p1 -> p2' -H "x-ms-copy-source: $B/pages/p1" "$B/pages/p2?$SAS"
page 'Put Page p2 bytes 0-511 = b' b "$B/pages/p2?comp=page&$SAS"
first 'first 4 bytes of p1:' "$B/pages/p1?$SAS"
# B. p3 holds 'a'. Snapshot it, restore the snapshot over p3 with Copy Blob, write 'c' to p3.
req 'create page blob p3 (1024 bytes)' -H 'x-ms-blob-type: PageBlob' -H 'x-ms-blob-content-length: 1024' "$B/pages/p3?$SAS"
page 'Put Page p3 bytes 0-511 = a' a "$B/pages/p3?comp=page&$SAS"
SNAP=$(curl -s -o /dev/null -D - -X PUT -H "$V" -H 'Content-Length: 0' "$B/pages/p3?comp=snapshot&$SAS" \
| tr -d '\r' | awk -F': ' 'tolower($1)=="x-ms-snapshot" {print $2}')
echo "Snapshot Blob p3: x-ms-snapshot $SNAP"
first 'first 4 bytes of the snapshot:' "$B/pages/p3?snapshot=$SNAP&$SAS"
req 'Copy Blob p3?snapshot -> p3 (restore)' -H "x-ms-copy-source: $B/pages/p3?snapshot=$SNAP" "$B/pages/p3?$SAS"
page 'Put Page p3 bytes 0-511 = c' c "$B/pages/p3?comp=page&$SAS"
first 'first 4 bytes of the snapshot:' "$B/pages/p3?snapshot=$SNAP&$SAS"
# C. Append blobs: a1 holds 'x'. Copy a1 -> a2, append 'y' to a2, then append 'z' to a1.
append() { printf '%-42s %s\n' "Append Block $2 = $1" "$(curl -s -o /dev/null -w '%{http_code}' -X PUT -H "$V" --data-binary "$1" "$B/pages/$2?comp=appendblock&$SAS")"; }
req 'create append blob a1' -H 'x-ms-blob-type: AppendBlob' "$B/pages/a1?$SAS"
append x a1
req 'Copy Blob a1 -> a2' -H "x-ms-copy-source: $B/pages/a1" "$B/pages/a2?$SAS"
append y a2
append z a1
echo "Get Blob a1: $(curl -s -H "$V" "$B/pages/a1?$SAS")"
Output on 3.37.0:
create container 201
create page blob p1 (1024 bytes) 201
Copy Blob p1 -> p2 202
Put Page p2 bytes 0-511 = b 201
first 4 bytes of p1: b b b b
create page blob p3 (1024 bytes) 201
Put Page p3 bytes 0-511 = a 201
Snapshot Blob p3: x-ms-snapshot 2026-11-01T18:24:13.0870000Z
first 4 bytes of the snapshot: a a a a
Copy Blob p3?snapshot -> p3 (restore) 202
Put Page p3 bytes 0-511 = c 201
first 4 bytes of the snapshot: c c c c
create append blob a1 201
Append Block a1 = x 201
Copy Blob a1 -> a2 202
Append Block a2 = y 201
Append Block a1 = z 201
Get Blob a1: xy
Expected: \0 \0 \0 \0 for p1, a a a a for the snapshot at the end, and xz for a1. In our runs the ETag
of p1 was the same before and after the write to p2 (for example "0x1DF07064A5C5EC0" both
times).
Have you found a mitigation/solution?
Mitigation: after copying a page or append blob, do not write to the copy or the source.
Potential cause. LokiBlobMetadataStore.startCopyFromURL builds the new blob under the comment
// Deep clone a copied blob but sets pageRangesInOrder: sourceBlob.pageRangesInOrder
(src/blob/persistence/LokiBlobMetadataStore.ts:2017, inserted at :2060); copyFromURL does the
same (:2208, :2253). The blobs collection is created without clone
(:181-183), so the source document and the copy hold the same array object.
uploadPages and clearRange then change that array in place through
PageBlobRangesManager.mergeRange / clearRange, which use splice
(src/blob/handlers/PageBlobRangesManager.ts:74, :113, :168; called from
LokiBlobMetadataStore.ts:2804 and :2869). The next lines of both copy paths do the same with
committedBlocksInOrder: sourceBlob.committedBlocksInOrder (:2026, :2217), and
appendBlock pushes onto that array (doc.committedBlocksInOrder.push(block), :2494-2495).
createSnapshot already copies both arrays (.slice(), :1145-1153), which is why a snapshot on
its own is safe until a copy is made from it.
Suggested fix. Copy both arrays in both copy paths, as createSnapshot does, for example
pageRangesInOrder: sourceBlob.pageRangesInOrder === undefined ? undefined : sourceBlob.pageRangesInOrder.slice()
and the same for committedBlocksInOrder. mergeRange and clearRange splice new range objects
into the array rather than changing the existing ones, so a shallow copy looks enough.
- Lingua principale
- TypeScript
- Stelle
- 2.3k
- Fork
- 400
- Merge medio
- 4g 9h
- PR unite (30g)
- 23
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di Azure/Azurite
-
Container-scoped Blob Batch runs subrequests on another container whose name starts with the scope container's nameForse già presa @compiler-segfault l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
Azure/Azurite#2872 · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
Azure/Azurite#2865 · 10 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
Get Blob with Range bytes=-1 returns 500Forse già presa @hippi345 l’ha presa 21 giorni fa. Aperta
Difficoltà 3/5 Mezza giornata Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
Acquiring a blob lease without a duration causes later requests to return 500Forse già presa @hippi345 l’ha presa 21 giorni fa. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 65/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di Azure/Azurite
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
area:ui bug effort:S priority:P2
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
Typo in WSL project description on Projects pageForse già presa @akhil9tiet l’ha presa oggi. Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
microsoft/opensource.microsoft.com#1066 · 1 commento ·
-
ArcGIS Maps SDK for JavaScript c-carousel calcite-design-tokens enhancement impact - p3 - not time sensitive needs triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
Esri/calcite-design-system#15565 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
linkwarden/linkwarden#1891 ·
I maintainer di solito rispondono entro 1 giorno