Refactor: remove pkg mem cache
This commit is contained in:
		| @@ -5,10 +5,8 @@ import ( | |||||||
| 	"crypto/hmac" | 	"crypto/hmac" | ||||||
| 	"crypto/sha256" | 	"crypto/sha256" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"gralias/models" | 	"gralias/models" | ||||||
| 	"gralias/pkg/cache" |  | ||||||
| 	"gralias/utils" | 	"gralias/utils" | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @@ -187,32 +185,5 @@ func makeCookie(username string, remote string) (*http.Cookie, error) { | |||||||
| 	if err := repo.SessionCreate(context.Background(), session); err != nil { | 	if err := repo.SessionCreate(context.Background(), session); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	// set user in session |  | ||||||
| 	if err := cacheSetSession(sessionToken, session); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return cookie, nil | 	return cookie, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| //nolint: unused |  | ||||||
| func cacheGetSession(key string) (*models.Session, error) { |  | ||||||
| 	userSessionB, err := cache.MemCache.Get(key) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	var us *models.Session |  | ||||||
| 	if err := json.Unmarshal(userSessionB, &us); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return us, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func cacheSetSession(key string, session *models.Session) error { |  | ||||||
| 	sesb, err := json.Marshal(session) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	cache.MemCache.Set(key, sesb) |  | ||||||
| 	cache.MemCache.Expire(key, cfg.SessionLifetime) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ import ( | |||||||
| 	"crypto/sha256" | 	"crypto/sha256" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"gralias/models" | 	"gralias/models" | ||||||
| 	"gralias/pkg/cache" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -71,7 +70,8 @@ func GetSession(next http.Handler) http.Handler { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if userSession.IsExpired() { | 		if userSession.IsExpired() { | ||||||
| 			cache.MemCache.RemoveKey(sessionToken) | 			repo.SessionDelete(r.Context(), sessionToken) | ||||||
|  | 			// cache.MemCache.RemoveKey(sessionToken) | ||||||
| 			msg := "session is expired" | 			msg := "session is expired" | ||||||
| 			log.Debug(msg, "error", err, "token", sessionToken) | 			log.Debug(msg, "error", err, "token", sessionToken) | ||||||
| 			next.ServeHTTP(w, r) | 			next.ServeHTTP(w, r) | ||||||
| @@ -81,13 +81,13 @@ func GetSession(next http.Handler) http.Handler { | |||||||
| 			models.CtxUsernameKey, userSession.Username) | 			models.CtxUsernameKey, userSession.Username) | ||||||
| 		ctx = context.WithValue(ctx, | 		ctx = context.WithValue(ctx, | ||||||
| 			models.CtxSessionKey, userSession) | 			models.CtxSessionKey, userSession) | ||||||
| 		if err := cacheSetSession(sessionToken, | 		// if err := cacheSetSession(sessionToken, | ||||||
| 			userSession); err != nil { | 		// 	userSession); err != nil { | ||||||
| 			msg := "failed to marshal user session" | 		// 	msg := "failed to marshal user session" | ||||||
| 			log.Warn(msg, "error", err) | 		// 	log.Warn(msg, "error", err) | ||||||
| 			next.ServeHTTP(w, r) | 		// 	next.ServeHTTP(w, r) | ||||||
| 			return | 		// 	return | ||||||
| 		} | 		// } | ||||||
| 		next.ServeHTTP(w, r.WithContext(ctx)) | 		next.ServeHTTP(w, r.WithContext(ctx)) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| package llmapi | package llmapi | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"gralias/broker" | 	"gralias/broker" | ||||||
| 	"gralias/config" | 	"gralias/config" | ||||||
| 	"gralias/models" | 	"gralias/models" | ||||||
| 	"gralias/pkg/cache" | 	"gralias/repos" | ||||||
| 	"io" | 	"io" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @@ -19,6 +19,7 @@ import ( | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// botname -> channel | 	// botname -> channel | ||||||
|  | 	repo          = repos.NewRepoProvider("sqlite3://../gralias.db") | ||||||
| 	SignalChanMap = make(map[string]chan bool) | 	SignalChanMap = make(map[string]chan bool) | ||||||
| 	DoneChanMap   = make(map[string]chan bool) | 	DoneChanMap   = make(map[string]chan bool) | ||||||
| 	// got prompt: control character (\\u0000-\\u001F) found while parsing a string at line 4 column 0 | 	// got prompt: control character (\\u0000-\\u001F) found while parsing a string at line 4 column 0 | ||||||
| @@ -49,7 +50,7 @@ func convertToSliceOfStrings(value any) ([]string, error) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| //nolint: unused | // nolint: unused | ||||||
| func (b *Bot) checkGuesses(tempMap map[string]any, room *models.Room) error { | func (b *Bot) checkGuesses(tempMap map[string]any, room *models.Room) error { | ||||||
| 	guesses, err := convertToSliceOfStrings(tempMap["guesses"]) | 	guesses, err := convertToSliceOfStrings(tempMap["guesses"]) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -162,7 +163,8 @@ func (b *Bot) BotMove() { | |||||||
| 	// botJournalName := models.NotifyJournalPrefix + b.RoomID | 	// botJournalName := models.NotifyJournalPrefix + b.RoomID | ||||||
| 	b.log.Debug("got signal", "bot-team", b.Team, "bot-role", b.Role) | 	b.log.Debug("got signal", "bot-team", b.Team, "bot-role", b.Role) | ||||||
| 	// get room cards and actions | 	// get room cards and actions | ||||||
| 	room, err := getRoomByID(b.RoomID) | 	// room, err := getRoomByID(b.RoomID) | ||||||
|  | 	room, err := repo.RoomGetExtended(context.Background(), b.RoomID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		b.log.Error("bot loop", "error", err) | 		b.log.Error("bot loop", "error", err) | ||||||
| 		return | 		return | ||||||
| @@ -316,7 +318,8 @@ func NewBot(role, team, name, roomID string, cfg *config.Config, recovery bool) | |||||||
| 		bot.LLMParser = NewOpenRouterParser(bot.log) | 		bot.LLMParser = NewOpenRouterParser(bot.log) | ||||||
| 	} | 	} | ||||||
| 	// add to room | 	// add to room | ||||||
| 	room, err := getRoomByID(bot.RoomID) | 	// room, err := getRoomByID(bot.RoomID) | ||||||
|  | 	room, err := repo.RoomGetExtended(context.Background(), bot.RoomID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -371,35 +374,39 @@ func NewBot(role, team, name, roomID string, cfg *config.Config, recovery bool) | |||||||
| } | } | ||||||
|  |  | ||||||
| func saveBot(bot *Bot) error { | func saveBot(bot *Bot) error { | ||||||
| 	key := models.CacheBotPredix + bot.RoomID + bot.BotName | 	// key := models.CacheBotPredix + bot.RoomID + bot.BotName | ||||||
| 	data, err := json.Marshal(bot) | 	// data, err := json.Marshal(bot) | ||||||
| 	if err != nil { | 	// if err != nil { | ||||||
| 		return err | 	// 	return err | ||||||
| 	} | 	// // } | ||||||
| 	cache.MemCache.Set(key, data) | 	// cache.MemCache.Set(key, data) | ||||||
| 	return nil | 	botPlayer := bot.ToPlayer() | ||||||
|  | 	return repo.PlayerAdd(context.Background(), botPlayer) | ||||||
| } | } | ||||||
|  |  | ||||||
| func getRoomByID(roomID string) (*models.Room, error) { | // func getRoomByID(roomID string) (*models.Room, error) { | ||||||
| 	roomBytes, err := cache.MemCache.Get(models.CacheRoomPrefix + roomID) | // 	roomBytes, err := cache.MemCache.Get(models.CacheRoomPrefix + roomID) | ||||||
| 	if err != nil { | // 	if err != nil { | ||||||
| 		return nil, err | // 		return nil, err | ||||||
| 	} | // 	} | ||||||
| 	resp := &models.Room{} | // 	resp := &models.Room{} | ||||||
| 	if err := json.Unmarshal(roomBytes, &resp); err != nil { | // 	if err := json.Unmarshal(roomBytes, &resp); err != nil { | ||||||
| 		return nil, err | // 		return nil, err | ||||||
| 	} | // 	} | ||||||
| 	return resp, nil | // 	return resp, nil | ||||||
| } | // } | ||||||
|  |  | ||||||
| func saveRoom(room *models.Room) error { | func saveRoom(room *models.Room) error { | ||||||
| 	key := models.CacheRoomPrefix + room.ID | 	// key := models.CacheRoomPrefix + room.ID | ||||||
| 	data, err := json.Marshal(room) | 	// data, err := json.Marshal(room) | ||||||
| 	if err != nil { | 	// if err != nil { | ||||||
| 		return err | 	// 	return err | ||||||
| 	} | 	// } | ||||||
| 	cache.MemCache.Set(key, data) | 	// cache.MemCache.Set(key, data) | ||||||
| 	return nil | 	// ------------ | ||||||
|  | 	// probably need to update other tables | ||||||
|  | 	// like word_cards or marks; | ||||||
|  | 	return repo.RoomUpdate(context.Background(), room) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bot) BuildSimpleGuesserPrompt(room *models.Room) string { | func (b *Bot) BuildSimpleGuesserPrompt(room *models.Room) string { | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package llmapi | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"gralias/config" | 	"gralias/config" | ||||||
|  | 	"gralias/models" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -84,3 +85,13 @@ type Bot struct { | |||||||
| 	// SignalsCh chan bool | 	// SignalsCh chan bool | ||||||
| 	// DoneCh    chan bool | 	// DoneCh    chan bool | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Bot) ToPlayer() *models.Player { | ||||||
|  | 	return &models.Player{ | ||||||
|  | 		Role:     models.StrToUserRole(b.Role), | ||||||
|  | 		Team:     models.StrToUserTeam(b.Team), | ||||||
|  | 		RoomID:   &b.RoomID, | ||||||
|  | 		Username: b.BotName, | ||||||
|  | 		IsBot:    true, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							| @@ -4,7 +4,6 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"gralias/config" | 	"gralias/config" | ||||||
| 	"gralias/handlers" | 	"gralias/handlers" | ||||||
| 	"gralias/pkg/cache" |  | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| @@ -76,5 +75,4 @@ func main() { | |||||||
| 	if err := server.Shutdown(ctx); err != nil { | 	if err := server.Shutdown(ctx); err != nil { | ||||||
| 		slog.Error("server shutdown failed", "error", err) | 		slog.Error("server shutdown failed", "error", err) | ||||||
| 	} | 	} | ||||||
| 	cache.MemCache.BackupNow() |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										146
									
								
								pkg/cache/impl.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										146
									
								
								pkg/cache/impl.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,146 +0,0 @@ | |||||||
| 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) BackupNow() { |  | ||||||
| 	data := mc.GetAll() |  | ||||||
| 	jsonString, err := json.Marshal(data) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Warn("immediate backup failed to marshal", "err", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	err = os.WriteFile(storeFileName, jsonString, os.ModePerm) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Warn("immediate backup failed to write", "err", err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
| 			mc.BackupNow() |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
							
								
								
									
										9
									
								
								pkg/cache/main.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								pkg/cache/main.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +0,0 @@ | |||||||
| 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) |  | ||||||
| } |  | ||||||
| @@ -84,6 +84,9 @@ func (p *RepoProvider) RoomGetExtended(ctx context.Context, id string) (*models. | |||||||
| 	} | 	} | ||||||
| 	room.RedTeam.Color = string(models.UserTeamRed) | 	room.RedTeam.Color = string(models.UserTeamRed) | ||||||
| 	room.BlueTeam.Color = string(models.UserTeamBlue) | 	room.BlueTeam.Color = string(models.UserTeamBlue) | ||||||
|  | 	if room.BotMap == nil { | ||||||
|  | 		room.BotMap = make(map[string]models.BotPlayer) | ||||||
|  | 	} | ||||||
| 	for _, player := range players { | 	for _, player := range players { | ||||||
| 		if player.Team == models.UserTeamRed { | 		if player.Team == models.UserTeamRed { | ||||||
| 			if player.Role == models.UserRoleMime { | 			if player.Role == models.UserRoleMime { | ||||||
| @@ -99,9 +102,6 @@ func (p *RepoProvider) RoomGetExtended(ctx context.Context, id string) (*models. | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if player.IsBot { | 		if player.IsBot { | ||||||
| 			if room.BotMap == nil { |  | ||||||
| 				room.BotMap = make(map[string]models.BotPlayer) |  | ||||||
| 			} |  | ||||||
| 			room.BotMap[player.Username] = models.BotPlayer{ | 			room.BotMap[player.Username] = models.BotPlayer{ | ||||||
| 				Role: player.Role, | 				Role: player.Role, | ||||||
| 				Team: player.Team, | 				Team: player.Team, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder