You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2605d28
Browse filesBrowse the repository at this point in the historyBrowse files
fix(storage): make StreamActivities cancel-safe at the source (#1737)
## Hazard
`storage.Manager.StreamActivities` walked the activity bucket inside a
BBolt read transaction and sent every matching record into a channel
buffered at 100, with no context or done channel. Any consumer that
stopped reading early — the 24h profile stats loop breaking on
`ctx.Err()`, an activity export whose client disconnected mid-stream —
left the producer blocked on the send for the life of the process,
holding the read transaction and the mmap read lock: `Stats().OpenTxN`
stayed at 1, later writes could not remap the file, and `db.Close` hung
on shutdown. #1718 worked around it in its own consumer with a drain
goroutine (`drainActivities`); the two older consumers still leaked.
## Fix
- `StreamActivities(ctx, filter)` at the source: the producer checks
`ctx.Err()` before every send and selects on `ctx.Done()` around it, so
cancellation returns from `db.View` promptly and closes the channel.
Cancellation is not logged as a stream error. The signature changed
everywhere (storage → runtime → server → httpapi controller interface
and its test doubles); no external callers.
- Consumers pass their request/loop context: the httpapi export cancels
a per-request context on its write-error return, summary and scoped
usage pass `r.Context()`, `Stats24h` and `Suggest` pass their `ctx`.
`Stats24h` no longer caches a truncated rollup after cancellation;
`Suggest` returns the context error after the loop instead of a partial
window (a pre-cancelled context now yields zero records, so the in-loop
check alone would have returned a clean empty result).
- `drainActivities` and its test are removed.
## Tests
- `internal/storage/activity_stream_cancel_test.go`: abandon the stream
after one record with 300 matching records, assert `OpenTxN` returns to
0 and `Close` returns; the no-cancel variant proves the transaction
really is pinned without cancellation (guards the first test against
passing vacuously); pre-cancelled context yields nothing; live context
streams all. The first test hung `Close` for the full timeout before the
fix.
- `TestProfilesService_Stats24hDoesNotCacheTruncatedRollup`,
`TestSuggest_CancelledContextReturnsWithRecordsLeft` (updated).
- Green under `-race`: `internal/storage`, `internal/httpapi`,
`internal/runtime`. `golangci-lint` (CI config, bare and `--build-tags
server`) reports only pre-existing findings in untouched lines.
0 commit comments