Sobes.tech
Back to questions
Senior
4

type TTLCache interface { Set(k string, v any, ttl time.Duration) Get(k string) (any, bool) Del(k string) bool } // Не должно утекать по памяти // гет не должен отдавать просрочку //реализация

Companies where asked
X5

Answer from AI

sobes.tech AI

Для реализации TTLCache в Go, который не допускает утечек памяти и не возвращает просроченные значения, можно использовать структуру с мьютексом для потокобезопасности, карту для хранения значений и время истечения для каждого ключа. Также нужен механизм очистки просроченных элементов, например, через периодический горутин.

Пример реализации:

package ttlcache

import (
    "sync"
    "time"
)

type cacheItem struct {
    value      any
    expiration int64 // unix nano timestamp
}

type TTLCache interface {
    Set(k string, v any, ttl time.Duration)
    Get(k string) (any, bool)
    Del(k string) bool
}

type ttlCache struct {
    mu    sync.RWMutex
    items map[string]cacheItem
    quit  chan struct{}
}

func NewTTLCache(cleanupInterval time.Duration) TTLCache {
    c := &ttlCache{
        items: make(map[string]cacheItem),
        quit:  make(chan struct{}),
    }
    go c.cleanupLoop(cleanupInterval)
    return c
}

func (c *ttlCache) Set(k string, v any, ttl time.Duration) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.items[k] = cacheItem{
        value:      v,
        expiration: time.Now().Add(ttl).UnixNano(),
    }
}

func (c *ttlCache) Get(k string) (any, bool) {
    c.mu.RLock()
    item, found := c.items[k]
    c.mu.RUnlock()
    if !found {
        return nil, false
    }
    if time.Now().UnixNano() > item.expiration {
        // элемент просрочен, удаляем
        c.mu.Lock()
        delete(c.items, k)
        c.mu.Unlock()
        return nil, false
    }
    return item.value, true
}

func (c *ttlCache) Del(k string) bool {
    c.mu.Lock()
    defer c.mu.Unlock()
    if _, found := c.items[k]; found {
        delete(c.items, k)
        return true
    }
    return false
}

func (c *ttlCache) cleanupLoop(interval time.Duration) {
    ticker := time.NewTicker(interval)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            now := time.Now().UnixNano()
            c.mu.Lock()
            for k, item := range c.items {
                if now > item.expiration {
                    delete(c.items, k)
                }
            }
            c.mu.Unlock()
        case <-c.quit:
            return
        }
    }
}

func (c *ttlCache) Close() {
    close(c.quit)
}

В этой реализации:

  • При Set сохраняется значение с временем истечения.
  • При Get проверяется срок годности, если просрочено — удаляется и возвращается false.
  • Есть горутина, которая периодически очищает просроченные записи, чтобы не было утечек памяти.
  • Используется RWMutex для потокобезопасности.