fix(remote): Unregister metrics emitted by remote.WriteStorage when closed (#16868)

* Unregister metrics emitted by `remote.WriteStorage` when closed

Signed-off-by: Charles Korn <charles.korn@grafana.com>

* Address PR feedback: add test

Signed-off-by: Charles Korn <charles.korn@grafana.com>

---------

Signed-off-by: Charles Korn <charles.korn@grafana.com>
This commit is contained in:
Charles Korn 2025-07-17 19:32:15 +10:00 committed by GitHub
parent 9dc274687b
commit 46acc974c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 43 additions and 0 deletions

View File

@ -268,6 +268,14 @@ func (rws *WriteStorage) Close() error {
q.Stop()
}
close(rws.quit)
rws.watcherMetrics.Unregister()
rws.liveReaderMetrics.Unregister()
if rws.reg != nil {
rws.reg.Unregister(rws.highestTimestamp.Gauge)
}
return nil
}

View File

@ -980,3 +980,12 @@ func (s syncAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t
defer s.lock.Unlock()
return s.Appender.AppendHistogram(ref, l, t, h, f)
}
func TestWriteStorage_CanRegisterMetricsAfterClosing(t *testing.T) {
dir := t.TempDir()
reg := prometheus.NewPedanticRegistry()
s := NewWriteStorage(nil, reg, dir, time.Millisecond, nil)
require.NoError(t, s.Close())
require.NotPanics(t, func() { NewWriteStorage(nil, reg, dir, time.Millisecond, nil) })
}

View File

@ -29,6 +29,7 @@ import (
// LiveReaderMetrics holds all metrics exposed by the LiveReader.
type LiveReaderMetrics struct {
reg prometheus.Registerer
readerCorruptionErrors *prometheus.CounterVec
}
@ -36,6 +37,7 @@ type LiveReaderMetrics struct {
// at LiveReader instantiation.
func NewLiveReaderMetrics(reg prometheus.Registerer) *LiveReaderMetrics {
m := &LiveReaderMetrics{
reg: reg,
readerCorruptionErrors: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "prometheus_tsdb_wal_reader_corruption_errors_total",
Help: "Errors encountered when reading the WAL.",
@ -49,6 +51,15 @@ func NewLiveReaderMetrics(reg prometheus.Registerer) *LiveReaderMetrics {
return m
}
// Unregister unregisters metrics emitted by this instance.
func (m *LiveReaderMetrics) Unregister() {
if m.reg == nil {
return
}
m.reg.Unregister(m.readerCorruptionErrors)
}
// NewLiveReader returns a new live reader.
func NewLiveReader(logger *slog.Logger, metrics *LiveReaderMetrics, r io.Reader) *LiveReader {
lr := &LiveReader{

View File

@ -73,6 +73,7 @@ type WriteNotified interface {
}
type WatcherMetrics struct {
reg prometheus.Registerer
recordsRead *prometheus.CounterVec
recordDecodeFails *prometheus.CounterVec
samplesSentPreTailing *prometheus.CounterVec
@ -113,6 +114,7 @@ type Watcher struct {
func NewWatcherMetrics(reg prometheus.Registerer) *WatcherMetrics {
m := &WatcherMetrics{
reg: reg,
recordsRead: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: "prometheus",
@ -171,6 +173,19 @@ func NewWatcherMetrics(reg prometheus.Registerer) *WatcherMetrics {
return m
}
// Unregister unregisters metrics emitted by this instance.
func (m *WatcherMetrics) Unregister() {
if m.reg == nil {
return
}
m.reg.Unregister(m.recordsRead)
m.reg.Unregister(m.recordDecodeFails)
m.reg.Unregister(m.samplesSentPreTailing)
m.reg.Unregister(m.currentSegment)
m.reg.Unregister(m.notificationsSkipped)
}
// NewWatcher creates a new WAL watcher for a given WriteTo.
func NewWatcher(metrics *WatcherMetrics, readerMetrics *LiveReaderMetrics, logger *slog.Logger, name string, writer WriteTo, dir string, sendExemplars, sendHistograms, sendMetadata bool) *Watcher {
if logger == nil {