diff options
| author | 2025-08-30 00:36:46 +0700 | |
|---|---|---|
| committer | 2025-08-30 00:36:46 +0700 | |
| commit | 78fe4481099fcf6666f1a259e38d9d828a253347 (patch) | |
| tree | c4f8bc344da8a26536fb85d5fb3fec891bdf7eb6 /internal | |
| parent | 753fe7f1d5bf837a85481425bd6bf6b431ffb611 (diff) | |
| download | web-78fe4481099fcf6666f1a259e38d9d828a253347.tar.gz web-78fe4481099fcf6666f1a259e38d9d828a253347.zip | |
feat: add persistent storage for likes
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/router/client-cache.go | 99 |
1 files changed, 97 insertions, 2 deletions
diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go index 7d276b1..56cd595 100644 --- a/internal/router/client-cache.go +++ b/internal/router/client-cache.go @@ -1,31 +1,126 @@ package router import ( + "database/sql" "encoding/base64" + "fmt" "log/slog" + "strings" "sync" "golang.org/x/crypto/argon2" ) +type PageLike struct { + PageRef string + UserId string +} + type ClientCache struct { hashMap map[string]string mutexLikeMap map[string]*sync.Mutex mutexHashMap map[string]*sync.Mutex likePageMap map[string]map[string]struct{} salt []byte + db *sql.DB } var CCache *ClientCache -func NewClientCache(salt []byte) *ClientCache { +func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) { + tx, err := db.Begin() + if err != nil { + return nil, fmt.Errorf("fail to init transaction with db to fill cache: %w", err) + } + + rows, err := tx.Query("select * from blog_likes;") + if err != nil { + tx.Rollback() + return nil, fmt.Errorf("fail to query db for blog_likes to fill cache: %w", err) + } + + likePageMap := make(map[string]map[string]struct{}) + + for rows.Next() { + pageRef := make([]byte, 32) + userId := make([]byte, 32) + if err = rows.Scan(&pageRef, &userId); err != nil { + tx.Rollback() + return nil, fmt.Errorf("fail scanning blog_likes to fill cache: %w", err) + } + pageRefString := string(pageRef) + userIdString := base64.RawStdEncoding.EncodeToString(userId) + if _, ok := likePageMap[pageRefString]; !ok { + likePageMap[pageRefString] = make(map[string]struct{}) + } + likePageMap[pageRefString][userIdString] = struct{}{} + } + + if err = tx.Commit(); err != nil { + return nil, fmt.Errorf("fail to commit transaction in db: %w", err) + } + return &ClientCache{ hashMap: make(map[string]string), mutexLikeMap: make(map[string]*sync.Mutex), mutexHashMap: make(map[string]*sync.Mutex), - likePageMap: make(map[string]map[string]struct{}), + likePageMap: likePageMap, salt: salt, + db: db, + }, nil +} + +func (c *ClientCache) Close() error { + tx, err := c.db.Begin() + if err != nil { + return fmt.Errorf("fail to init transaction with db to dump cache: %w", err) } + + userIdBytes := make(map[string][]byte) + + sqlStatement := fmt.Sprintf(` + INSERT OR IGNORE INTO blog_likes (page_ref, user_id) + VALUES %s(?, ?); + `, strings.Repeat("(?, ?), ", 99)) + sqlStatementVars := make([]interface{}, 0, 200) + + for pageRef, userSet := range c.likePageMap { + pageRefBytes := []byte(pageRef) + + for userId := range userSet { + if _, ok := userIdBytes[userId]; !ok { + userIdBytes[userId], err = base64.RawStdEncoding.DecodeString(userId) + if err != nil { + slog.Warn("couldn't parse one of user hashes into bytes back", slog.String("hash", userId), slog.String("error", err.Error())) + continue + } + } + + sqlStatementVars = append(sqlStatementVars, interface{}(pageRefBytes), interface{}(userIdBytes[userId])) + if len(sqlStatementVars) < 200 { + continue + } + + if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil { + slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error())) + } + + sqlStatementVars = make([]interface{}, 0, 200) + } + } + + if len(sqlStatementVars) > 0 { + sqlStatement = fmt.Sprintf(` + INSERT OR IGNORE INTO blog_likes (page_ref, user_id) + VALUES %s(?, ?); + `, strings.Repeat("(?, ?), ", len(sqlStatementVars)/2-1)) + + if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil { + slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error())) + } + } + + return tx.Commit() } func (c *ClientCache) GetHash(id string) string { |