Bound the Client monitor's memory and fix reader and writer registry cleanup
I maintainer di solito rispondono entro 1 giorno
@ffumero2003 ci sta già lavorando.
Dal 28/10/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Context
Every b2.Client records every HTTP call in four x/window counters, one of them "forever". The reducer appends each call to a slice, and a zero-size window keeps its value indefinitely, so memory grows by one record per request for the life of the client. This is always on: NewClient installs the recording transport whether or not the caller uses Status or ServeHTTP, and every request takes the client-wide slock.
The registries behind Status have related problems:
- readers are removed only by
Reader.Close; a reader read to EOF but not closed (io.ReadAllandio.Copydo not close) stays inClient.sReaderswith its chunk buffers (up toConcurrentDownloadsx 10 MB); - a writer closed without writing is never removed:
Closetakes the never-started branch, which registers the writer and returns before the deferredremoveWriter; - both maps are keyed by
bucket/name, so two concurrent transfers of one name overwrite each other's entry, and the first to finish removes the other's.
Current behavior
At a5e0d13:
- b2/b2.go:63-68:
newMethodCounter(0, 0), // forever. - b2/monitor.go:75-94: the reducer appends every record.
- x/window/window.go:53-67, :133-136 and the package doc at :43-44 (slice reducers grow linearly).
- b2/b2.go:204-229:
clientTransport.RoundTriprecords every call underslock; b2/baseline.go:200-206:authOptionsalways installs it, both when authorizing and when restoring saved auth data. - b2/monitor.go:153-195: registries keyed by
fmt.Sprintf("%s/%s", bucket, name). - b2/reader.go:280 and :104: added in
initFunc, removed only inClose. - b2/writer.go:242: added in
init; b2/writer.go:719-726: a writer that never started callsinitand returns before the deferredremoveWriterat :728. b2/sync/sync.go:393-399: withWithCompare(CompareSHA1)the sync engine passes anio.TeeReader, so an empty file is copied without anyWriteand takes this branch.
Reproduced locally: 50 empty writers closed leave 50 registry entries. The RPC growth follows from the code: 10 million requests retain 10 million records.
Expected behavior
The monitor uses bounded memory regardless of client lifetime, and Status lists each active transfer exactly once.
Acceptance criteria
- R1. One million recorded calls keep the monitor's retained size constant (benchmark or heap check).
- R2. A reader read to EOF without
Closeis no longer listed inStatus(). - R3. Two concurrent writers of the same name both appear, and finishing one leaves the other listed.
- R4. A writer closed without writing is not listed in
Status()afterClosereturns.
In scope
Proposed change
- Replace the all-time slice with per-method counters (count, total duration, status histogram), or drop the forever window; aggregate the bounded windows the same way.
- Make RPC recording opt-in (for example a
WithMonitoring()client option) for those who useServeHTTP. - Key registries by a unique per-transfer ID; remove readers on EOF or error as well as on
Close; remove a writer on everyClosepath, including the one for a writer that never wrote (or register it only once data is sent).
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 ./x/window
- Record the benchmark or heap check for one million calls and the retained-size result, as required above.
- Test reader EOF without Close, closing an empty writer, and two concurrent same-name transfers; assert the stated registry contents after each transition.
- 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
None
- 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 ·