Skip to content

[cache] periodic cache clenup - #279

Open
capcom6 wants to merge 1 commit into
masterfrom
cache/unbounded-memory-growth
Open

capcom6 wants to merge 1 commit into
masterfrom
cache/unbounded-memory-growth

Conversation

@capcom6

@capcom6 capcom6 commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

RetriggerConfidence Score: 4/5

The PR appears safe to merge, with non-blocking gaps in cleanup coverage and its tests.

Findings

  1. P2 Test misses broken cleanup ▶
  2. P2 One-time codes miss cleanup ▶
  3. P2 Named caches lose cleanup ▶
Fix with agent prompt
### Issue 1
internal/sms-gateway/cache/sweeper_test.go:48-54
`TestSweeper_EvictsExpiredEntries` checks whether `Drain` returns no live entries, not whether the sweeper removed expired entries from memory. As the comment explains, `Drain` excludes expired entries, so this check can pass without any sweep. Assert that `Cleanup` runs on the tracked caches, or inspect the stored entry count without calling `Drain`.

### Issue 2
internal/sms-gateway/cache/module.go:17-24
The new sweeper misses the cache used for one-time codes. `otp.Module` creates it through `cachefx.Factory`, which bypasses `trackingFactory.New`. If the server uses the memory cache, unused codes remain allocated after they expire because `CleanupAll` never visits this cache and `Storage.Cleanup` has no caller. Route it through the tracked factory or give it its own cleanup job.

### Issue 3
internal/sms-gateway/cache/sweeper.go:57-63
`trackingFactory.WithName` creates a separate cache list. Caches created through the returned factory are invisible to the existing sweeper. No current caller uses this path, but a caller that adds a name prefix will silently lose periodic cleanup. Share the list and its lock between the parent and named factories.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds a factory wrapper that records cache instances and a sweeper that calls CleanupAll every minute. Registers the sweeper with the app lifecycle and adds tests and dependency updates.

  • The eviction test does not prove that expired entries leave memory.
  • The cache for one-time codes bypasses the sweeper.
  • Named factories do not share the sweeper's recorded caches.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Tracked factory] --> B[Recorded cache instances]
  C[Minute tick] --> D[Sweeper]
  D --> E[CleanupAll]
  E --> B
  F[Raw cachefx factory] --> G[One-time code cache]
  A --> H[WithName]
  H --> I[Separate cache list]
Loading

Reviews (1) · Last reviewed commit: "[cache] periodic cache clenup" · Reviewed by Greptile

Comment on lines +48 to +54
// Drain returns only non-expired items; an empty result means the
// sweeper has evicted the expired ones.
items, drainErr := storage.Drain(ctx)
assert.Equal(t, nil, drainErr)

if len(items) == 0 {
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Test misses broken cleanup

TestSweeper_EvictsExpiredEntries checks whether Drain returns no live entries, not whether the sweeper removed expired entries from memory. As the comment explains, Drain excludes expired entries, so this check can pass without any sweep. Assert that Cleanup runs on the tracked caches, or inspect the stored entry count without calling Drain.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/sms-gateway/cache/sweeper_test.go
Line: 48-54

Comment:
**Test misses broken cleanup**

`TestSweeper_EvictsExpiredEntries` checks whether `Drain` returns no live entries, not whether the sweeper removed expired entries from memory. As the comment explains, `Drain` excludes expired entries, so this check can pass without any sweep. Assert that `Cleanup` runs on the tracked caches, or inspect the stored entry count without calling `Drain`.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines 17 to 24
fx.Provide(func(factory cachefx.Factory) Factory {
return factory.WithName("sms-gateway")
return &trackingFactory{
Factory: factory.WithName("sms-gateway"),

cachesMu: sync.Mutex{},
caches: []cache.Cache{},
}
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 One-time codes miss cleanup

The new sweeper misses the cache used for one-time codes. otp.Module creates it through cachefx.Factory, which bypasses trackingFactory.New. If the server uses the memory cache, unused codes remain allocated after they expire because CleanupAll never visits this cache and Storage.Cleanup has no caller. Route it through the tracked factory or give it its own cleanup job.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/sms-gateway/cache/module.go
Line: 17-24

Comment:
**One-time codes miss cleanup**

The new sweeper misses the cache used for one-time codes. `otp.Module` creates it through `cachefx.Factory`, which bypasses `trackingFactory.New`. If the server uses the memory cache, unused codes remain allocated after they expire because `CleanupAll` never visits this cache and `Storage.Cleanup` has no caller. Route it through the tracked factory or give it its own cleanup job.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +57 to +63
func (f *trackingFactory) WithName(prefix string) cachefx.Factory {
return &trackingFactory{
Factory: f.Factory.WithName(prefix),

cachesMu: sync.Mutex{},
caches: []cache.Cache{},
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Named caches lose cleanup

trackingFactory.WithName creates a separate cache list. Caches created through the returned factory are invisible to the existing sweeper. No current caller uses this path, but a caller that adds a name prefix will silently lose periodic cleanup. Share the list and its lock between the parent and named factories.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/sms-gateway/cache/sweeper.go
Line: 57-63

Comment:
**Named caches lose cleanup**

`trackingFactory.WithName` creates a separate cache list. Caches created through the returned factory are invisible to the existing sweeper. No current caller uses this path, but a caller that adds a name prefix will silently lose periodic cleanup. Share the list and its lock between the parent and named factories.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

🤖 Pull request artifacts

Platform File
🐳 Docker GitHub Container Registry
🍎 Darwin arm64 server_Darwin_arm64.tar.gz
🍎 Darwin x86_64 server_Darwin_x86_64.tar.gz
🐧 Linux arm64 server_Linux_arm64.tar.gz
🐧 Linux i386 server_Linux_i386.tar.gz
🐧 Linux x86_64 server_Linux_x86_64.tar.gz
🪟 Windows arm64 server_Windows_arm64.zip
🪟 Windows i386 server_Windows_i386.zip
🪟 Windows x86_64 server_Windows_x86_64.zip

This branch has not been deployed

No deployments
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.

1 participant