Files
glchat/internal/permissions/cache.go
T

150 lines
3.4 KiB
Go
Raw Normal View History

package permissions
import (
"container/list"
"sync"
)
// Cache — простой LRU-кэш вычисленных прав с инвалидацией при записи
// (AGENT.md 9.1). Ключи: сервер+пользователь и комната+пользователь.
type Cache struct {
mu sync.Mutex
capacity int
items map[guildKey]*list.Element
channels map[channelKey]*list.Element
order *list.List
}
type guildKey struct {
guildID uint64
userID uint64
}
type channelKey struct {
channelID uint64
userID uint64
}
type cacheEntry struct {
guild guildKey
channel channelKey
value Resolved
isChan bool
}
func NewCache(capacity int) *Cache {
if capacity <= 0 {
capacity = 1024
}
return &Cache{
capacity: capacity,
items: make(map[guildKey]*list.Element, capacity),
channels: make(map[channelKey]*list.Element, capacity),
order: list.New(),
}
}
func (c *Cache) GetGuild(guildID, userID uint64) (Resolved, bool) {
c.mu.Lock()
defer c.mu.Unlock()
element, ok := c.items[guildKey{guildID: guildID, userID: userID}]
if !ok {
return Resolved{}, false
}
c.order.MoveToFront(element)
return element.Value.(*cacheEntry).value, true
}
func (c *Cache) PutGuild(guildID, userID uint64, value Resolved) {
c.mu.Lock()
defer c.mu.Unlock()
key := guildKey{guildID: guildID, userID: userID}
if element, ok := c.items[key]; ok {
element.Value.(*cacheEntry).value = value
c.order.MoveToFront(element)
return
}
entry := &cacheEntry{guild: key, value: value}
c.items[key] = c.order.PushFront(entry)
c.evictLocked()
}
func (c *Cache) GetChannel(channelID, userID uint64) (Resolved, bool) {
c.mu.Lock()
defer c.mu.Unlock()
element, ok := c.channels[channelKey{channelID: channelID, userID: userID}]
if !ok {
return Resolved{}, false
}
c.order.MoveToFront(element)
return element.Value.(*cacheEntry).value, true
}
func (c *Cache) PutChannel(channelID, userID uint64, value Resolved) {
c.mu.Lock()
defer c.mu.Unlock()
key := channelKey{channelID: channelID, userID: userID}
if element, ok := c.channels[key]; ok {
element.Value.(*cacheEntry).value = value
c.order.MoveToFront(element)
return
}
entry := &cacheEntry{channel: key, value: value, isChan: true}
c.channels[key] = c.order.PushFront(entry)
c.evictLocked()
}
// InvalidateGuild сбрасывает права всех пользователей сервера.
func (c *Cache) InvalidateGuild(guildID uint64) {
c.mu.Lock()
defer c.mu.Unlock()
for key, element := range c.items {
if key.guildID == guildID {
c.order.Remove(element)
delete(c.items, key)
}
}
}
// InvalidateChannel сбрасывает кэш комнаты.
func (c *Cache) InvalidateChannel(channelID uint64) {
c.mu.Lock()
defer c.mu.Unlock()
for key, element := range c.channels {
if key.channelID == channelID {
c.order.Remove(element)
delete(c.channels, key)
}
}
}
func (c *Cache) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
c.items = make(map[guildKey]*list.Element, c.capacity)
c.channels = make(map[channelKey]*list.Element, c.capacity)
c.order.Init()
}
func (c *Cache) Len() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.order.Len()
}
func (c *Cache) evictLocked() {
for c.order.Len() > c.capacity {
oldest := c.order.Back()
if oldest == nil {
return
}
entry := oldest.Value.(*cacheEntry)
if entry.isChan {
delete(c.channels, entry.channel)
} else {
delete(c.items, entry.guild)
}
c.order.Remove(oldest)
}
}