db: make the ephemeral GC safe to close twice

A second Close panicked on the closed cancel channel.
This commit is contained in:
Igor Serganov authored and Kristoffer Dalby committed 2026-10-30 17:05:14 +01:00
1 parent 4472cd8b83
commit 2088f1d2a6
2 files changed
+18 -4

No files matched your search

@@ -512,3 +512,16 @@ func TestEphemeralGarbageCollectorConcurrentScheduleAndClose(t *testing.T) {
t.Logf("Final number of goroutines: %d", runtime.NumGoroutine())
}
// TestEphemeralGarbageCollectorCloseTwice pins that a second Close does not
// panic on the already closed cancel channel.
func TestEphemeralGarbageCollectorCloseTwice(t *testing.T) {
gc := NewEphemeralGarbageCollector(func(types.NodeID) {})
go gc.Start()
gc.Schedule(types.NodeID(1), time.Hour)
gc.Close()
assert.NotPanics(t, gc.Close)
}
+5 -4
View File
@@ -377,8 +377,9 @@ type EphemeralGarbageCollector struct {
// was superseded by a Cancel or reschedule can be recognised and dropped.
gen uint64
deleteCh chan pendingDeletion
cancelCh chan struct{}
deleteCh chan pendingDeletion
cancelCh chan struct{}
closeOnce sync.Once
}
// ephemeralTimer pairs a node's pending-deletion timer with a done channel
@@ -410,7 +411,7 @@ func NewEphemeralGarbageCollector(deleteFunc func(types.NodeID)) *EphemeralGarba
}
}
// Close stops the garbage collector.
// Close stops the garbage collector. It is safe to call more than once.
func (e *EphemeralGarbageCollector) Close() {
e.mu.Lock()
defer e.mu.Unlock()
@@ -421,7 +422,7 @@ func (e *EphemeralGarbageCollector) Close() {
}
// Close the cancel channel to signal all goroutines to exit
close(e.cancelCh)
e.closeOnce.Do(func() { close(e.cancelCh) })
}
// Schedule schedules a node for deletion after the expiry duration.