Skip to content

Commit a3889cd

Browse files
AchoArnoldCopilot
andcommitted
fix: make UserRistrettoCache a singleton so all callers share one cache
The root cause of the test failure: UserRistrettoCache() created a new ristretto cache on every call. The auth middleware and the RotateAPIKey handler received separate cache instances, so cache.Del() in the handler had no effect on the middleware's cache — the old key kept authenticating. Fix: store the cache as a field on the Container struct and return it on subsequent calls (lazy singleton pattern), matching how db, app, and eventDispatcher are already handled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d77b99c commit a3889cd

2 files changed

Lines changed: 9 additions & 5 deletions

File tree

api/pkg/di/container.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type Container struct {
8686
eventDispatcher *services.EventDispatcher
8787
logger telemetry.Logger
8888
attachmentRepository repositories.AttachmentRepository
89+
userRistrettoCache *ristretto.Cache[string, entities.AuthContext]
8990
}
9091

9192
// NewLiteContainer creates a Container without any routes or listeners
@@ -1730,8 +1731,11 @@ func (container *Container) PhoneRistrettoCache() (cache *ristretto.Cache[string
17301731
}
17311732

17321733
// UserRistrettoCache creates an in-memory *ristretto.Cache[string, entities.AuthContext]
1733-
func (container *Container) UserRistrettoCache() (cache *ristretto.Cache[string, entities.AuthContext]) {
1734-
container.logger.Debug(fmt.Sprintf("creating %T", cache))
1734+
func (container *Container) UserRistrettoCache() *ristretto.Cache[string, entities.AuthContext] {
1735+
if container.userRistrettoCache != nil {
1736+
return container.userRistrettoCache
1737+
}
1738+
container.logger.Debug(fmt.Sprintf("creating %T", container.userRistrettoCache))
17351739
ristrettoCache, err := ristretto.NewCache[string, entities.AuthContext](&ristretto.Config[string, entities.AuthContext]{
17361740
MaxCost: 5000,
17371741
NumCounters: 5000 * 10,
@@ -1740,6 +1744,7 @@ func (container *Container) UserRistrettoCache() (cache *ristretto.Cache[string,
17401744
if err != nil {
17411745
container.logger.Fatal(stacktrace.Propagate(err, "cannot create user ristretto cache"))
17421746
}
1747+
container.userRistrettoCache = ristrettoCache
17431748
return ristrettoCache
17441749
}
17451750

api/pkg/repositories/gorm_user_repository.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,8 @@ func (repository *gormUserRepository) RotateAPIKey(ctx context.Context, userID e
8484
}
8585

8686
if err == nil && oldAPIKey != "" {
87-
// Wait() flushes any pending buffered Set operations in ristretto
88-
// before Del, preventing a race where a prior request's async Set
89-
// re-adds the key after our Del removes it from the store.
87+
// Flush pending ristretto Set operations before Del to avoid a
88+
// buffered Set re-adding the entry after removal.
9089
repository.cache.Wait()
9190
repository.cache.Del(oldAPIKey)
9291
}

0 commit comments

Comments
 (0)