init elowdb go-port commit
This commit is contained in:
200
pkg/linedb/cache.go
Normal file
200
pkg/linedb/cache.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package linedb
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CacheEntry представляет запись в кэше
|
||||
type CacheEntry struct {
|
||||
Data any
|
||||
Timestamp time.Time
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
// RecordCache представляет кэш записей
|
||||
type RecordCache struct {
|
||||
cache map[string]*CacheEntry
|
||||
mutex sync.RWMutex
|
||||
maxSize int
|
||||
ttl time.Duration
|
||||
stopChan chan struct{}
|
||||
}
|
||||
|
||||
// NewRecordCache создает новый кэш
|
||||
func NewRecordCache(maxSize int, ttl time.Duration) *RecordCache {
|
||||
cache := &RecordCache{
|
||||
cache: make(map[string]*CacheEntry),
|
||||
maxSize: maxSize,
|
||||
ttl: ttl,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Запускаем очистку устаревших записей только если TTL > 0
|
||||
if ttl > 0 {
|
||||
go cache.cleanupLoop()
|
||||
}
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
// Set устанавливает значение в кэш
|
||||
func (c *RecordCache) Set(key string, value any) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
// Проверяем размер кэша
|
||||
if len(c.cache) >= c.maxSize {
|
||||
c.evictOldest()
|
||||
}
|
||||
|
||||
c.cache[key] = &CacheEntry{
|
||||
Data: value,
|
||||
Timestamp: time.Now(),
|
||||
TTL: c.ttl,
|
||||
}
|
||||
}
|
||||
|
||||
// Get получает значение из кэша
|
||||
func (c *RecordCache) Get(key string) (any, bool) {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
entry, exists := c.cache[key]
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Проверяем TTL
|
||||
if c.ttl > 0 && time.Since(entry.Timestamp) > c.ttl {
|
||||
delete(c.cache, key)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return entry.Data, true
|
||||
}
|
||||
|
||||
// Has проверяет наличие ключа в кэше
|
||||
func (c *RecordCache) Has(key string) bool {
|
||||
_, exists := c.Get(key)
|
||||
return exists
|
||||
}
|
||||
|
||||
// Delete удаляет ключ из кэша
|
||||
func (c *RecordCache) Delete(key string) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
delete(c.cache, key)
|
||||
}
|
||||
|
||||
// Clear очищает кэш
|
||||
func (c *RecordCache) Clear() {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.cache = make(map[string]*CacheEntry)
|
||||
}
|
||||
|
||||
// Stop останавливает кэш
|
||||
func (c *RecordCache) Stop() {
|
||||
close(c.stopChan)
|
||||
}
|
||||
|
||||
// Size возвращает размер кэша
|
||||
func (c *RecordCache) Size() int {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
return len(c.cache)
|
||||
}
|
||||
|
||||
// GetFlatCacheMap возвращает плоскую карту кэша
|
||||
func (c *RecordCache) GetFlatCacheMap() map[string]*CacheEntry {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
result := make(map[string]*CacheEntry)
|
||||
for key, entry := range c.cache {
|
||||
result[key] = entry
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// evictOldest удаляет самую старую запись из кэша
|
||||
func (c *RecordCache) evictOldest() {
|
||||
var oldestKey string
|
||||
var oldestTime time.Time
|
||||
|
||||
for key, entry := range c.cache {
|
||||
if oldestKey == "" || entry.Timestamp.Before(oldestTime) {
|
||||
oldestKey = key
|
||||
oldestTime = entry.Timestamp
|
||||
}
|
||||
}
|
||||
|
||||
if oldestKey != "" {
|
||||
delete(c.cache, oldestKey)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupLoop запускает цикл очистки устаревших записей
|
||||
func (c *RecordCache) cleanupLoop() {
|
||||
// Используем более безопасный интервал
|
||||
interval := c.ttl / 4
|
||||
if interval < time.Second {
|
||||
interval = time.Second
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.cleanup()
|
||||
case <-c.stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup очищает устаревшие записи
|
||||
func (c *RecordCache) cleanup() {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for key, entry := range c.cache {
|
||||
if c.ttl > 0 && now.Sub(entry.Timestamp) > c.ttl {
|
||||
delete(c.cache, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetByRecord устанавливает запись в кэш по записи
|
||||
func (c *RecordCache) SetByRecord(record any, collectionName string) {
|
||||
key := toString(record) + ":" + collectionName
|
||||
c.Set(key, record)
|
||||
}
|
||||
|
||||
// UpdateCacheAfterInsert обновляет кэш после вставки
|
||||
func (c *RecordCache) UpdateCacheAfterInsert(record any, collectionName string) {
|
||||
c.SetByRecord(record, collectionName)
|
||||
}
|
||||
|
||||
// toString преобразует значение в строку
|
||||
func toString(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v
|
||||
case int:
|
||||
return string(rune(v))
|
||||
case float64:
|
||||
return string(rune(int(v)))
|
||||
case map[string]any:
|
||||
if id, exists := v["id"]; exists {
|
||||
return toString(id)
|
||||
}
|
||||
return "unknown"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user