Нужен in-memory кеш аккаунтов, где время жизни задаётся отдельно для каждого
элемента в момент добавления.
type Account struct {
Key int64
Label string
}
// NewCache создаёт пустой кеш. now — часы (nil → time.Now).
func NewCache(now func() time.Time) *Cache
// Set кладёт аккаунт по ключу a.Key; он живёт ttl с момента вызова.
func (c *Cache) Set(a Account, ttl time.Duration)
// Get возвращает живой аккаунт. TTL не продлевает.
func (c *Cache) Get(key int64) (Account, bool)
// Delete удаляет аккаунт. Отсутствующий ключ — не ошибка.
func (c *Cache) Delete(key int64)
Требования
- Элемент жив, пока
now() < момент Set + ttl. Протухший элемент Get не
возвращает.
Get срок жизни не продлевает.
- Повторный
Set того же ключа заменяет значение и задаёт новый срок —
как длиннее, так и короче прежнего.
Set с ttl <= 0 означает «сразу протух»: после него Get по ключу
возвращает false, даже если там было живое значение.
- Время берётся только из
now. Кеш безопасен для конкурентного
использования, код проходит go test -race.
Пример
c := NewCache(nil)
c.Set(Account{Key: 1, Label: "short"}, time.Second)
c.Set(Account{Key: 2, Label: "long"}, time.Hour)
time.Sleep(2 * time.Second)
c.Get(1) // {}, false
c.Get(2) // {2 long}, true
Если протухшие элементы никто не читает, они лежат в памяти вечно.
// Cleanup удаляет из памяти все протухшие элементы и возвращает их число.
func (c *Cache) Cleanup() int
- Удаляет ровно протухшие элементы. Удалённые через
Delete и перезаписанные
через Set значения не считаются.
- Работает за O(k·log n), где k — число протухших, а не за O(n): кеш на
сотни тысяч живых элементов,
Cleanup вызывают часто. Полный проход по map на
каждый вызов не годится.
Устно: как бы ты запускал Cleanup по расписанию и останавливал его? Какие
тестовые сценарии нужны для такого кеша?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.