summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/cache/interface.go9
-rw-r--r--pkg/cache/main.go146
2 files changed, 155 insertions, 0 deletions
diff --git a/pkg/cache/interface.go b/pkg/cache/interface.go
new file mode 100644
index 0000000..606f50f
--- /dev/null
+++ b/pkg/cache/interface.go
@@ -0,0 +1,9 @@
+package cache
+
+type Cache interface {
+ Get(key string) ([]byte, error)
+ Set(key string, value []byte)
+ Expire(key string, exp int64)
+ GetAll() (resp map[string][]byte)
+ RemoveKey(key string)
+}
diff --git a/pkg/cache/main.go b/pkg/cache/main.go
new file mode 100644
index 0000000..d617f49
--- /dev/null
+++ b/pkg/cache/main.go
@@ -0,0 +1,146 @@
+package cache
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log/slog"
+ "os"
+ "sync"
+ "time"
+)
+
+const storeFileName = "store.json"
+
+// var MemCache Cache
+var (
+ MemCache *MemoryCache
+ log = slog.New(slog.NewJSONHandler(os.Stdout, nil))
+)
+
+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 {
+ log.Warn("failed to load store from file", "error", err)
+ }
+ 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)
+ log.Info("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 {
+ log.Warn("failed to marshal kv store", "error", err)
+ continue
+ }
+ err = ioutil.WriteFile(storeFileName, jsonString, os.ModePerm)
+ if err != nil {
+ log.Warn("failed to write to json file", "error", err)
+ continue
+ }
+ }
+ }()
+}