Feat: add models/cache/config
This commit is contained in:
		
							
								
								
									
										144
									
								
								pkg/cache/impl.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								pkg/cache/impl.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| 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 | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
							
								
								
									
										9
									
								
								pkg/cache/main.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								pkg/cache/main.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder