Fix Writer Write-after-Close, ReadFrom position and pipe handling, and unreleased resources
I maintainer di solito rispondono entro 1 giorno
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Context
b2.Writer's io.Writer, io.ReaderFrom and io.Closer behavior has several defects:
- Write after Close.
closedis set only on the large-file success path. After a small file'sClose,Writedereferences the released pooled buffer and panics. AfterCloseof a never-written writer,Writesucceeds and, pastChunkSize, starts a large file nothing will finish. io.Copyfrom a pipe uploads an empty object.ReadFromtreats anyio.ReadSeekeras seekable.*os.Filealways hasSeek, soio.Copy(w, os.Stdin)with piped stdin fails with "illegal seek" instead of buffering, andClosethen writes a zero-byte object.ReadFromignores the reader's position. WithoutResume, it uploads from offset 0 and returns the full size even when the source was already advanced.- Unreleased resources. A writer closed without any
Writestays inClient.sWriterswith its buffer open (an empty temp file withUseFileBuffer); two worker error paths skipcnk.buf.Close();NewWriter's context is never cancelled on success;ReadFrom's buffered paths usecopyContextwithout a closer, so the copy goroutine keeps reading afterReadFromreturns on cancellation.
Current behavior
At a5e0d13:
- b2/writer.go:282-287 and :769:
closedset only after a large-file finish. - b2/writer.go:723-727: the never-written path returns before
removeWriterand bufferCloseare deferred (:728-736); b2/buffer.go:145-155 and :127-131. - b2/writer.go:649-715:
ReadFromchecks only forio.ReadSeeker(:650), seeks to the end (:671) and starts at 0 (:682). - b2/writer.go:201-204 and :214-217: worker returns without closing the chunk buffer (the upload-error path at :222-226 does close it).
- b2/b2.go:738 and b2/writer.go:152: the writer context is cancelled only on error.
- b2/writer.go:652 and :668:
copyContext(b2/reader.go:378-380) passes no closer tocopyContextWithCloser(b2/reader.go:384-405). The sync engine (Backblaze/blazer#135) reaches this path when it wraps the source in anio.TeeReader(b2/sync/sync.go:399-412).
Reproduced locally (test server, -race):
| Case | Result |
|---|---|
small Write + Close, then Write |
panic: nil pointer dereference |
Close without Write, then Write past ChunkSize |
no error, large file started |
io.Copy(w, pipe) |
seek: illegal seek; Close returns nil, empty object stored |
file at offset 8 of HEADER--payload, ReadFrom |
object starts with HEADER-- |
| 50 empty + 50 small writers closed | 50 entries left in Client.sWriters |
empty writer with UseFileBuffer |
1 temp file left |
Expected behavior
Write and ReadFrom return ErrClosed after Close; pipes upload their data; ReadFrom starts at the current position; every writer releases its resources.
Acceptance criteria
- R1.
WriteafterClosereturnsErrClosedon small, empty and large paths, with no panic and no new upload. - R2.
io.Copy(w, r)whereris an*os.Filepipe uploads the piped bytes. - R3.
ReadFromon a seeker at offset k uploads from k and returnssize - k. - R4. After closing N empty writers,
Client.Status().WritersandFileBufferDirare empty.
In scope
Proposed change
- Set
closed = trueat the start ofCloseon every path. - In
ReadFrom, start atSeek(0, io.SeekCurrent)and fall back to buffering when seeking fails; return bytes actually sent. - Defer
removeWriterand bufferClosebefore the never-written branch; close chunk buffers on every worker exit. - Call
w.cancel()at the end ofClose. - In
ReadFrom, usecopyContextWithCloserwith a closer when available, or wait for the copy goroutine, or document the behavior.
Out of scope
- Unrelated work outside the components and behavior described in In scope.
Verification
Run the repository regression checks with live credentials unset:
unset B2_APPLICATION_KEY_ID B2_APPLICATION_KEY B2_ACCOUNT_ID B2_ACCOUNT_KEY
go build ./...
go vet ./...
go test -race -count=1 ./b2/...
- Run the supplied small/empty/large Close cases, OS-pipe copy, positioned-reader case, and resource-cleanup checks. Assert errors, content, byte counts, registries, and temp files as specified.
- Demonstrate failing regressions before each fix and passing results afterward; include concurrent/error/cancellation paths in the race run.
- Expected result: the checks above succeed and the observable outcomes in Acceptance criteria are demonstrated. If a check cannot run, report
Could not run because: ...; if it starts but fails, reportCould not complete because: .... A skipped check is not a pass.
Links
Open Backblaze/blazer#54 is a different panic in Close. Merged Backblaze/blazer#131 added copyContextWithCloser for downloads. Closed Backblaze/blazer#56 and Backblaze/blazer#58 fixed earlier worker issues. Merged Backblaze/blazer#135 added a ReadFrom caller in b2/sync.
- Lingua principale
- Go
- Stelle
- 66
- Fork
- 18
- Merge medio
- 1g 10h
- PR unite (30g)
- 132
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la 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 Backblaze/blazer
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
Bucket.Attrs never returns ReplicationConfig: replication configuration is write-onlyForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
test: skip the fuzz non-vacuity guard when replaying a single seedForse già presa @ffumero2003 l’ha presa 5 giorni fa. Apertatesting
Backblaze/blazer#330 · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di Backblaze/blazer
Issue simili
-
needs-triage typo
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
llm-d-incubation/llm-d-scaling-manager#122 ·
I maintainer di solito rispondono entro 1 giorno
-
good first issue severity: low tech-debt
Difficoltà 2/5 Mezza giornata Idoneità per principianti 75/100
TMHSDigital/subenum#329 ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug] Backend summarize min() / max() panics when a field has no numeric valuesForse già presa @weng5e l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
grafana/grafana-infinity-datasource#1832 ·
I maintainer di solito rispondono entro 1 giorno
-
tech-debt
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
GillMacedo/godebugdiff#13 ·