init elowdb go-port commit

This commit is contained in:
41 changed files with 7273 additions and 0 deletions

200
pkg/linedb/cache.go Normal file
View 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"
}
}