Why does this cache always miss, so Get always returns nil?
This Storage wraps an LRU cache, but profiling shows the underlying store is never relieved — every Get returns nil even right after a Set. Find the bug.
type Storage struct{ cache *lru.Cache }
func (s *Storage) Set(wh *warehouse.Warehouse) {
s.cache.Put(wh.Id, *wh)
}
func (s *Storage) Get(id types.WarehouseId) *warehouse.Warehouse {
item, ok := s.cache.Get(id)
if ok {
if wh, ok := item.(*warehouse.Warehouse); ok {
return wh
}
}
return nil
}
Find and fix the bug.
Type mismatch. Set stores a value: s.cache.Put(wh.Id, *wh) dereferences the pointer, so the cached dynamic type is warehouse.Warehouse. But Get asserts a pointer: item.(*warehouse.Warehouse). That assertion never succeeds, so Get always falls through to return nil and the cache never hits. Fix: store and assert the same type — store wh, assert *warehouse.Warehouse.
- ✗Overlooking that
*whdereferences the pointer, storing a value not a pointer - ✗Assuming a type assertion auto-converts between a value and a pointer type
- ✗Blaming eviction, key types, or a race instead of the value/pointer mismatch
- →Why does
item.(*warehouse.Warehouse)returnok == falserather than panicking? - →What would
item.(warehouse.Warehouse)return given the currentSet?
What is wrong
The stored type does not match the asserted type.
In Set the value put under the key is *wh — a dereference of the pointer, so a value of type warehouse.Warehouse is cached:
s.cache.Put(wh.Id, *wh) // cached dynamic type = warehouse.Warehouse
In Get the type assertion targets a pointer:
if wh, ok := item.(*warehouse.Warehouse); ok { // expects *warehouse.Warehouse
The stored value's dynamic type (warehouse.Warehouse) does not equal the asserted type (*warehouse.Warehouse), so ok is always false and Get always reaches return nil. The cache fills up but never serves an entry — the backing store is hit as if there were no cache.
The fix
Store and read the same type. The simplest is to store the pointer:
func (s *Storage) Set(wh *warehouse.Warehouse) {
s.cache.Put(wh.Id, wh) // store the pointer
}
func (s *Storage) Get(id types.WarehouseId) *warehouse.Warehouse {
item, ok := s.cache.Get(id)
if ok {
if wh, ok := item.(*warehouse.Warehouse); ok { // matches
return wh
}
}
return nil
}