Проверяет понимание работы с горутинами, мьютексами и структурами данных для реализации потокобезопасного in-memory кэша в Go.
In-memory кэш хранит данные в оперативной памяти для быстрого доступа. В Go для этого удобно использовать map[string]interface{} в паре с sync.RWMutex, чтобы обеспечить безопасный доступ из нескольких горутин.
Создадим структуру Cache с полями data (map) и mu (RWMutex). Метод Set блокирует запись, Get — чтение. RWMutex позволяет одновременно читать данные многим горутинам, но блокирует запись.
type Cache struct {
data map[string]interface{}
mu sync.RWMutex
}
func NewCache() *Cache {
return &Cache{data: make(map[string]interface{})}
}
func (c *Cache) Set(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
val, ok := c.data[key]
return val, ok
}Чтобы кэш не хранил устаревшие данные, добавим время жизни записи (TTL). Для этого будем хранить не только значение, но и время создания. Отдельная горутина будет периодически удалять просроченные записи.
type item struct {
value interface{}
expiration int64 // unix timestamp
}
func (c *Cache) SetWithTTL(key string, value interface{}, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = item{
value: value,
expiration: time.Now().Add(ttl).UnixNano(),
}
}
func (c *Cache) cleanupLoop(interval time.Duration) {
ticker := time.NewTicker(interval)
go func() {
for range ticker.C {
c.mu.Lock()
now := time.Now().UnixNano()
for k, v := range c.data {
if v.expiration <= now {
delete(c.data, k)
}
}
c.mu.Unlock()
}
}()
}Простой in-memory кэш на Go с использованием map и мьютексов — отличное решение для ускорения работы приложения, когда данные редко меняются и не требуют персистентности. Для более сложных сценариев (распределённый кэш, большие объёмы) стоит рассмотреть Redis или группу кэшей с шардированием.