Что такое TTL в контексте кеширования?

Ответ

TTL (Time To Live) — это механизм, который определяет время жизни записи в кеше. По истечении этого времени запись считается «протухшей» (stale) и подлежит удалению или обновлению при следующем запросе.

Зачем нужен TTL?

  1. Актуальность данных: Гарантирует, что пользователи не будут получать устаревшую информацию.
  2. Управление памятью: Автоматически очищает ненужные данные, предотвращая переполнение кеша и утечки памяти.

Реализация в Go

Существует два основных подхода к реализации TTL-кеша:

  1. Пассивная очистка (Lazy Eviction): Время жизни проверяется только в момент запроса данных (Get). Если запись просрочена, она удаляется и не возвращается пользователю. Это самый простой способ.
  2. Активная очистка (Active Eviction): Фоновая горутина периодически сканирует весь кеш и удаляет просроченные записи. Этот подход лучше управляет памятью, но добавляет сложности.

Для большинства задач проще и надежнее использовать готовые библиотеки, например, github.com/patrickmn/go-cache.

Пример простой реализации (пассивная очистка)

import (
    "sync"
    "time"
)

type CacheItem struct {
    Value    interface{}
    Expiry   int64 // Время истечения в Unix наносекундах
}

type TtlCache struct {
    mu    sync.RWMutex
    items map[string]CacheItem
}

func (c *TtlCache) Set(key string, value interface{}, ttl time.Duration) {
    c.mu.Lock()
    defer c.mu.Unlock()

    if c.items == nil {
        c.items = make(map[string]CacheItem)
    }

    c.items[key] = CacheItem{
        Value:  value,
        Expiry: time.Now().Add(ttl).UnixNano(),
    }
}

func (c *TtlCache) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()

    item, found := c.items[key]
    if !found {
        return nil, false
    }

    // Проверяем, не истекло ли время жизни
    if time.Now().UnixNano() > item.Expiry {
        // Можно добавить удаление ключа здесь, но это потребует `c.mu.Lock()`
        return nil, false
    }

    return item.Value, true
}