Skip to content

fix(sink): stop progressBlockRate janitor in Stats.Close()#785

Merged
maoueh merged 1 commit into
streamingfast:developfrom
hugosjoberg:fix/stats-close-progress-block-rate-leak
May 21, 2026
Merged

fix(sink): stop progressBlockRate janitor in Stats.Close()#785
maoueh merged 1 commit into
streamingfast:developfrom
hugosjoberg:fix/stats-close-progress-block-rate-leak

Conversation

@hugosjoberg
Copy link
Copy Markdown
Contributor

@hugosjoberg hugosjoberg commented May 21, 2026

Fixes #786

Problem

`Stats.Close()` stops `dataMsgRate` and `undoMsgRate` but omits `progressBlockRate.Stop()`.

Each `dmetrics.AvgRate*` object spawns an internal janitor goroutine on construction. Without `Stop()`, that goroutine leaks for the process lifetime.

In code paths that repeatedly construct and tear down a `Sinker` — for example a continuous-mode loop that builds a fresh `Sinker` each collection interval — this produces one leaked goroutine per iteration, creating a linear goroutine growth visible in runtime metrics over hours/days.

Fix

Add the missing `s.progressBlockRate.Stop()` call, symmetric with the other two.

Test

`sink/stats_test.go` — `TestStatsCloseStopsAllRateJanitors`: creates and closes 50 `Stats` objects in a loop and asserts `runtime.NumGoroutine()` does not drift upward (delta ≤ 2). Pre-fix: delta = 50. Post-fix: delta ≈ 0.

Context

Discovered while investigating a goroutine leak in a service using `streamingfast/substreams/sink` in continuous mode. The same bug existed in the now-deprecated `streamingfast/substreams-sink` package and was carried over when the `sink` package was ported into this repo.

Fixes streamingfast#786

Stats.Close() stopped dataMsgRate and undoMsgRate but omitted
progressBlockRate.Stop(). Each AvgRate* object spawns an internal
janitor goroutine on construction; without Stop() that goroutine leaks.

In code paths that repeatedly construct and tear down a Sinker (e.g.,
a continuous-mode loop that builds a fresh Sinker each collection
interval), this produces one leaked goroutine per iteration — a linear
growth visible in runtime metrics over hours/days.

Adds TestStatsCloseStopsAllRateJanitors: creates and closes 50 Stats
objects in a loop and asserts NumGoroutine() does not drift upward.

Co-Authored-By: Hugo Sjoberg <hugo.sjoberg@amberdata.io>
Signed-off-by: Hugo Sjoberg <hugo.sjoberg@amberdata.io>
@hugosjoberg hugosjoberg force-pushed the fix/stats-close-progress-block-rate-leak branch from 3685690 to 2aa2114 Compare May 21, 2026 14:39
@maoueh maoueh merged commit e3c1d4c into streamingfast:develop May 21, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Goroutine leak in sink.Stats.Close(): progressBlockRate janitor never stopped

2 participants