Почему этот кэш всегда промахивается, и Get всегда возвращает nil?
Этот Storage оборачивает LRU-кэш, но профилирование показывает, что нижнее хранилище не разгружается — каждый Get возвращает nil даже сразу после Set. Найдите ошибку.
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
}
Найдите и исправьте ошибку.
Несовпадение типов. Set кладёт значение: s.cache.Put(wh.Id, *wh) разыменовывает указатель, поэтому динамический тип в кэше — warehouse.Warehouse. А Get ассертит указатель: item.(*warehouse.Warehouse). Этот ассерт никогда не проходит, и Get всегда доходит до return nil, и кэш не попадает. Исправление: класть и ассертить один тип — класть wh, ассертить *warehouse.Warehouse.
- ✗Не замечать, что
*whразыменовывает указатель, кладя значение, а не указатель - ✗Считать, что type assertion автоматически конвертирует между значением и указателем
- ✗Винить вытеснение, типы ключей или гонку вместо несовпадения значение/указатель
- →Почему
item.(*warehouse.Warehouse)возвращаетok == false, а не паникует? - →Что вернул бы
item.(warehouse.Warehouse)при текущемSet?
Что не так
Несовпадение хранимого и ассертируемого типов.
В Set ключу сопоставляется *wh — это разыменование указателя, то есть в кэш кладётся значение типа warehouse.Warehouse:
s.cache.Put(wh.Id, *wh) // динамический тип в кэше = warehouse.Warehouse
В Get делается type assertion на указатель:
if wh, ok := item.(*warehouse.Warehouse); ok { // ждёт *warehouse.Warehouse
Динамический тип хранимого значения (warehouse.Warehouse) не равен ассертируемому (*warehouse.Warehouse), поэтому ok всегда false, и Get всегда доходит до return nil. Кэш заполняется, но не отдаёт ни одной записи — нижнее хранилище нагружается как без кэша.
Исправление
Класть и читать один и тот же тип. Проще всего хранить указатель:
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
}