Skip to content

Commit 333fa85

Browse files
handle stopping better (#150)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved internal cache lifecycle management to ensure more reliable and predictable resource cleanup during shutdown. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Robert Landers <[email protected]>
1 parent b5f1386 commit 333fa85

File tree

1 file changed

+19
-13
lines changed

1 file changed

+19
-13
lines changed

atlas/cache/cloxcache.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ type CloxCache[K Key, V any] struct {
5353
// Adaptive decay step (1-4)
5454
decayStep atomic.Uint32
5555

56-
// Control channels
57-
stopSweeper chan struct{}
58-
stopDecay chan struct{}
56+
// Lifecycle management
57+
stop chan struct{}
58+
wg sync.WaitGroup
59+
closeOnce sync.Once
5960
}
6061

6162
// shard contains a portion of the cache slots with minimal lock contention
@@ -103,11 +104,10 @@ func NewCloxCache[K Key, V any](cfg Config) *CloxCache[K, V] {
103104
}
104105

105106
c := &CloxCache[K, V]{
106-
numShards: cfg.NumShards,
107-
shardBits: bits.Len(uint(cfg.NumShards - 1)),
108-
shards: make([]shard[K, V], cfg.NumShards),
109-
stopSweeper: make(chan struct{}),
110-
stopDecay: make(chan struct{}),
107+
numShards: cfg.NumShards,
108+
shardBits: bits.Len(uint(cfg.NumShards - 1)),
109+
shards: make([]shard[K, V], cfg.NumShards),
110+
stop: make(chan struct{}),
111111
}
112112

113113
// Initialize shards
@@ -119,16 +119,20 @@ func NewCloxCache[K Key, V any](cfg Config) *CloxCache[K, V] {
119119
c.decayStep.Store(1)
120120

121121
// Start background goroutines
122+
c.wg.Add(2)
122123
go c.sweeper(cfg.SlotsPerShard)
123124
go c.retargetDecayLoop()
124125

125126
return c
126127
}
127128

128-
// Close stops background goroutines
129+
// Close stops background goroutines and waits for them to exit.
130+
// Safe to call multiple times.
129131
func (c *CloxCache[K, V]) Close() {
130-
close(c.stopSweeper)
131-
close(c.stopDecay)
132+
c.closeOnce.Do(func() {
133+
close(c.stop)
134+
})
135+
c.wg.Wait()
132136
}
133137

134138
func keysEqual[K Key](a, b K) bool {
@@ -306,6 +310,7 @@ func (c *CloxCache[K, V]) shouldAdmit() bool {
306310

307311
// sweeper is the background CLOCK hand that ages and evicts entries
308312
func (c *CloxCache[K, V]) sweeper(slotsPerShard int) {
313+
defer c.wg.Done()
309314
hand := uint64(0)
310315
totalSlots := uint64(c.numShards * slotsPerShard)
311316

@@ -314,7 +319,7 @@ func (c *CloxCache[K, V]) sweeper(slotsPerShard int) {
314319

315320
for {
316321
select {
317-
case <-c.stopSweeper:
322+
case <-c.stop:
318323
return
319324
case <-ticker.C:
320325
// Process a batch of slots
@@ -394,12 +399,13 @@ func (c *CloxCache[K, V]) sweepSlot(shardID, slotID int, dec uint32) {
394399

395400
// retargetDecayLoop periodically adjusts the decay step based on metrics
396401
func (c *CloxCache[K, V]) retargetDecayLoop() {
402+
defer c.wg.Done()
397403
ticker := time.NewTicker(decayInterval)
398404
defer ticker.Stop()
399405

400406
for {
401407
select {
402-
case <-c.stopDecay:
408+
case <-c.stop:
403409
return
404410
case <-ticker.C:
405411
c.retargetDecay()

0 commit comments

Comments
 (0)