summaryrefslogtreecommitdiff
path: root/internal
diff options
from:
to:
context:
space:
mode:
authorGravatar SayaAndy <saya.andy@posteo.com> 2025-08-30 00:36:46 +0700
committerGravatar SayaAndy <saya.andy@posteo.com> 2025-08-30 00:36:46 +0700
commit78fe4481099fcf6666f1a259e38d9d828a253347 (patch)
treec4f8bc344da8a26536fb85d5fb3fec891bdf7eb6 /internal
parent753fe7f1d5bf837a85481425bd6bf6b431ffb611 (diff)
downloadweb-78fe4481099fcf6666f1a259e38d9d828a253347.tar.gz
web-78fe4481099fcf6666f1a259e38d9d828a253347.zip
feat: add persistent storage for likes
Diffstat (limited to 'internal')
-rw-r--r--internal/router/client-cache.go99
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 {