Files
golias/pkg/cache/impl.go
2025-05-02 09:06:17 +03:00

145 lines
2.8 KiB
Go

package cache
import (
"encoding/json"
"fmt"
"log/slog"
"os"
"sync"
"time"
)
const storeFileName = "store.json"
// var MemCache Cache
var (
MemCache *MemoryCache
)
func readJSON(fileName string) (map[string][]byte, error) {
data := make(map[string][]byte)
file, err := os.Open(fileName)
if err != nil {
return data, err
}
defer file.Close()
decoder := json.NewDecoder(file)
if err := decoder.Decode(&data); err != nil {
return data, err
}
return data, nil
}
func init() {
data, err := readJSON(storeFileName)
if err != nil {
slog.Error("failed to load store from file")
}
MemCache = &MemoryCache{
data: data,
timeMap: make(map[string]time.Time),
lock: &sync.RWMutex{},
}
MemCache.StartExpiryRoutine(time.Minute)
MemCache.StartBackupRoutine(time.Minute)
}
type MemoryCache struct {
data map[string][]byte
timeMap map[string]time.Time
lock *sync.RWMutex
}
// Get a value by key from the cache
func (mc *MemoryCache) Get(key string) (value []byte, err error) {
var ok bool
mc.lock.RLock()
if value, ok = mc.data[key]; !ok {
err = fmt.Errorf("not found data in mc for the key: %v", key)
}
mc.lock.RUnlock()
return value, err
}
// Update a single value in the cache
func (mc *MemoryCache) Set(key string, value []byte) {
// no async writing
mc.lock.Lock()
mc.data[key] = value
mc.lock.Unlock()
}
func (mc *MemoryCache) Expire(key string, exp int64) {
mc.lock.RLock()
mc.timeMap[key] = time.Now().Add(time.Duration(exp) * time.Second)
mc.lock.RUnlock()
}
func (mc *MemoryCache) GetAll() (resp map[string][]byte) {
resp = make(map[string][]byte)
mc.lock.RLock()
for k, v := range mc.data {
resp[k] = v
}
mc.lock.RUnlock()
return
}
func (mc *MemoryCache) GetAllTime() (resp map[string]time.Time) {
resp = make(map[string]time.Time)
mc.lock.RLock()
for k, v := range mc.timeMap {
resp[k] = v
}
mc.lock.RUnlock()
return
}
func (mc *MemoryCache) RemoveKey(key string) {
mc.lock.RLock()
delete(mc.data, key)
delete(mc.timeMap, key)
mc.lock.RUnlock()
}
func (mc *MemoryCache) StartExpiryRoutine(n time.Duration) {
ticker := time.NewTicker(n)
go func() {
for {
<-ticker.C
// get all
timeData := mc.GetAllTime()
// check time
currentTS := time.Now()
for k, ts := range timeData {
if ts.Before(currentTS) {
// delete exp keys
mc.RemoveKey(k)
slog.Debug("remove by expiry", "key", k)
}
}
}
}()
}
func (mc *MemoryCache) StartBackupRoutine(n time.Duration) {
ticker := time.NewTicker(n)
go func() {
for {
<-ticker.C
// get all
data := mc.GetAll()
jsonString, err := json.Marshal(data)
if err != nil {
slog.Warn("failed to marshal", "err", err)
continue
}
err = os.WriteFile(storeFileName, jsonString, os.ModePerm)
if err != nil {
slog.Warn("failed to write", "err", err)
continue
}
}
}()
}