feat: update store cache (#1066)

* feat: update store cache

* chore: update
This commit is contained in:
boojack 2023-02-11 14:19:26 +08:00 committed by GitHub
parent 6e5be6ba75
commit 3590d3f8b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 228 deletions

3
go.mod
View File

@ -14,7 +14,6 @@ require (
require github.com/labstack/echo/v4 v4.9.0
require (
github.com/VictoriaMetrics/fastcache v1.10.0
github.com/gorilla/feeds v1.1.1
github.com/gorilla/sessions v1.2.1
github.com/labstack/echo-contrib v0.13.0
@ -22,10 +21,8 @@ require (
require (
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect

9
go.sum
View File

@ -1,20 +1,12 @@
github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY=
github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
@ -88,7 +80,6 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=

View File

@ -13,48 +13,6 @@ import (
"github.com/usememos/memos/api"
)
func generateRSSFromMemoList(memoList []*api.Memo, baseURL string, profile *api.CustomizedProfile) (string, error) {
feed := &feeds.Feed{
Title: profile.Name,
Link: &feeds.Link{Href: baseURL},
Description: profile.Description,
Created: time.Now(),
}
feed.Items = make([]*feeds.Item, len(memoList))
for i, memo := range memoList {
var useTitle = strings.HasPrefix(memo.Content, "# ")
var title string
if useTitle {
title = strings.Split(memo.Content, "\n")[0][2:]
} else {
title = memo.Creator.Username + "-memos-" + strconv.Itoa(memo.ID)
}
var description string
if useTitle {
var firstLineEnd = strings.Index(memo.Content, "\n")
description = memo.Content[firstLineEnd+1:]
} else {
description = memo.Content
}
feed.Items[i] = &feeds.Item{
Title: title,
Link: &feeds.Link{Href: baseURL + "/m/" + strconv.Itoa(memo.ID)},
Description: description,
Created: time.Unix(memo.CreatedTs, 0),
}
}
rss, err := feed.ToRss()
if err != nil {
return "", err
}
return rss, nil
}
func (s *Server) registerRSSRoutes(g *echo.Group) {
g.GET("/explore/rss.xml", func(c echo.Context) error {
ctx := c.Request().Context()
@ -123,6 +81,48 @@ func (s *Server) registerRSSRoutes(g *echo.Group) {
})
}
func generateRSSFromMemoList(memoList []*api.Memo, baseURL string, profile *api.CustomizedProfile) (string, error) {
feed := &feeds.Feed{
Title: profile.Name,
Link: &feeds.Link{Href: baseURL},
Description: profile.Description,
Created: time.Now(),
}
feed.Items = make([]*feeds.Item, len(memoList))
for i, memo := range memoList {
var useTitle = strings.HasPrefix(memo.Content, "# ")
var title string
if useTitle {
title = strings.Split(memo.Content, "\n")[0][2:]
} else {
title = memo.Creator.Username + "-memos-" + strconv.Itoa(memo.ID)
}
var description string
if useTitle {
var firstLineEnd = strings.Index(memo.Content, "\n")
description = memo.Content[firstLineEnd+1:]
} else {
description = memo.Content
}
feed.Items[i] = &feeds.Item{
Title: title,
Link: &feeds.Link{Href: baseURL + "/m/" + strconv.Itoa(memo.ID)},
Description: description,
Created: time.Unix(memo.CreatedTs, 0),
}
}
rss, err := feed.ToRss()
if err != nil {
return "", err
}
return rss, nil
}
func getSystemCustomizedProfile(ctx context.Context, s *Server) (api.CustomizedProfile, error) {
systemStatus := api.SystemStatus{
CustomizedProfile: api.CustomizedProfile{

View File

@ -1,82 +1,15 @@
package store
import (
"bytes"
"encoding/binary"
"encoding/gob"
"fmt"
"github.com/VictoriaMetrics/fastcache"
"github.com/usememos/memos/api"
)
var (
// 64 MiB.
cacheSize = 1024 * 1024 * 64
)
// CacheService implements a cache.
type CacheService struct {
cache *fastcache.Cache
func getUserSettingCacheKey(userSetting userSettingRaw) string {
return fmt.Sprintf("%d-%s", userSetting.UserID, userSetting.Key.String())
}
// CacheNamespace is the type of a cache.
type CacheNamespace string
const (
// UserCache is the cache type of users.
UserCache CacheNamespace = "u"
// MemoCache is the cache type of memos.
MemoCache CacheNamespace = "m"
// ShortcutCache is the cache type of shortcuts.
ShortcutCache CacheNamespace = "s"
)
// NewCacheService creates a cache service.
func NewCacheService() *CacheService {
return &CacheService{
cache: fastcache.New(cacheSize),
}
}
// FindCache finds the value in cache.
func (s *CacheService) FindCache(namespace CacheNamespace, id int, entry interface{}) (bool, error) {
buf1 := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(buf1, uint64(id))
buf2, has := s.cache.HasGet(nil, append([]byte(namespace), buf1...))
if has {
dec := gob.NewDecoder(bytes.NewReader(buf2))
if err := dec.Decode(entry); err != nil {
return false, fmt.Errorf("failed to decode entry for cache namespace: %s, error: %w", namespace, err)
}
return true, nil
}
return false, nil
}
// UpsertCache upserts the value to cache.
func (s *CacheService) UpsertCache(namespace CacheNamespace, id int, entry interface{}) error {
buf1 := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(buf1, uint64(id))
var buf2 bytes.Buffer
enc := gob.NewEncoder(&buf2)
if err := enc.Encode(entry); err != nil {
return fmt.Errorf("failed to encode entry for cache namespace: %s, error: %w", namespace, err)
}
s.cache.Set(append([]byte(namespace), buf1...), buf2.Bytes())
return nil
}
// DeleteCache deletes the cache.
func (s *CacheService) DeleteCache(namespace CacheNamespace, id int) {
buf1 := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(buf1, uint64(id))
_, has := s.cache.HasGet(nil, append([]byte(namespace), buf1...))
if has {
s.cache.Del(append([]byte(namespace), buf1...))
}
func getUserSettingFindCacheKey(userSettingFind *api.UserSettingFind) string {
return fmt.Sprintf("%d-%s", userSettingFind.UserID, userSettingFind.Key.String())
}

View File

@ -25,6 +25,7 @@ type memoRaw struct {
// Domain specific fields
Content string
Visibility api.Visibility
Pinned bool
}
// toMemo creates an instance of Memo based on the memoRaw.
@ -43,24 +44,15 @@ func (raw *memoRaw) toMemo() *api.Memo {
Content: raw.Content,
Visibility: raw.Visibility,
DisplayTs: raw.CreatedTs,
Pinned: raw.Pinned,
}
}
func (s *Store) ComposeMemo(ctx context.Context, memo *api.Memo) (*api.Memo, error) {
memoOrganizer, err := s.FindMemoOrganizer(ctx, &api.MemoOrganizerFind{
MemoID: memo.ID,
UserID: memo.CreatorID,
})
if err != nil && common.ErrorCode(err) != common.NotFound {
return nil, err
} else if memoOrganizer != nil {
memo.Pinned = memoOrganizer.Pinned
}
if err = s.ComposeMemoCreator(ctx, memo); err != nil {
if err := s.ComposeMemoCreator(ctx, memo); err != nil {
return nil, err
}
if err = s.ComposeMemoResourceList(ctx, memo); err != nil {
if err := s.ComposeMemoResourceList(ctx, memo); err != nil {
return nil, err
}
@ -102,10 +94,7 @@ func (s *Store) CreateMemo(ctx context.Context, create *api.MemoCreate) (*api.Me
return nil, FormatError(err)
}
if err := s.cache.UpsertCache(MemoCache, memoRaw.ID, memoRaw); err != nil {
return nil, err
}
s.memoCache.Store(memoRaw.ID, memoRaw)
memo, err := s.ComposeMemo(ctx, memoRaw.toMemo())
if err != nil {
return nil, err
@ -130,10 +119,7 @@ func (s *Store) PatchMemo(ctx context.Context, patch *api.MemoPatch) (*api.Memo,
return nil, FormatError(err)
}
if err := s.cache.UpsertCache(MemoCache, memoRaw.ID, memoRaw); err != nil {
return nil, err
}
s.memoCache.Store(memoRaw.ID, memoRaw)
memo, err := s.ComposeMemo(ctx, memoRaw.toMemo())
if err != nil {
return nil, err
@ -169,12 +155,8 @@ func (s *Store) FindMemoList(ctx context.Context, find *api.MemoFind) ([]*api.Me
func (s *Store) FindMemo(ctx context.Context, find *api.MemoFind) (*api.Memo, error) {
if find.ID != nil {
memoRaw := &memoRaw{}
has, err := s.cache.FindCache(MemoCache, *find.ID, memoRaw)
if err != nil {
return nil, err
}
if has {
if memo, ok := s.memoCache.Load(*find.ID); ok {
memoRaw := memo.(*memoRaw)
memo, err := s.ComposeMemo(ctx, memoRaw.toMemo())
if err != nil {
return nil, err
@ -199,10 +181,7 @@ func (s *Store) FindMemo(ctx context.Context, find *api.MemoFind) (*api.Memo, er
}
memoRaw := list[0]
if err := s.cache.UpsertCache(MemoCache, memoRaw.ID, memoRaw); err != nil {
return nil, err
}
s.memoCache.Store(memoRaw.ID, memoRaw)
memo, err := s.ComposeMemo(ctx, memoRaw.toMemo())
if err != nil {
return nil, err
@ -229,8 +208,7 @@ func (s *Store) DeleteMemo(ctx context.Context, delete *api.MemoDelete) error {
return FormatError(err)
}
s.cache.DeleteCache(MemoCache, delete.ID)
s.memoCache.Delete(delete.ID)
return nil
}
@ -309,19 +287,19 @@ func findMemoRawList(ctx context.Context, tx *sql.Tx, find *api.MemoFind) ([]*me
where, args := []string{"1 = 1"}, []interface{}{}
if v := find.ID; v != nil {
where, args = append(where, "id = ?"), append(args, *v)
where, args = append(where, "memo.id = ?"), append(args, *v)
}
if v := find.CreatorID; v != nil {
where, args = append(where, "creator_id = ?"), append(args, *v)
where, args = append(where, "memo.creator_id = ?"), append(args, *v)
}
if v := find.RowStatus; v != nil {
where, args = append(where, "row_status = ?"), append(args, *v)
where, args = append(where, "memo.row_status = ?"), append(args, *v)
}
if v := find.Pinned; v != nil {
where = append(where, "id IN (SELECT memo_id FROM memo_organizer WHERE pinned = 1 AND user_id = memo.creator_id)")
where = append(where, "memo_organizer.pinned = 1")
}
if v := find.ContentSearch; v != nil {
where, args = append(where, "content LIKE ?"), append(args, "%"+*v+"%")
where, args = append(where, "memo.content LIKE ?"), append(args, "%"+*v+"%")
}
if v := find.VisibilityList; len(v) != 0 {
list := []string{}
@ -329,21 +307,23 @@ func findMemoRawList(ctx context.Context, tx *sql.Tx, find *api.MemoFind) ([]*me
list = append(list, fmt.Sprintf("$%d", len(args)+1))
args = append(args, visibility)
}
where = append(where, fmt.Sprintf("visibility in (%s)", strings.Join(list, ",")))
where = append(where, fmt.Sprintf("memo.visibility in (%s)", strings.Join(list, ",")))
}
query := `
SELECT
id,
creator_id,
created_ts,
updated_ts,
row_status,
content,
visibility
memo.id,
memo.creator_id,
memo.created_ts,
memo.updated_ts,
memo.row_status,
memo.content,
memo.visibility,
memo_organizer.pinned
FROM memo
LEFT JOIN memo_organizer ON memo_organizer.memo_id = memo.id
WHERE ` + strings.Join(where, " AND ") + `
ORDER BY created_ts DESC
ORDER BY memo.created_ts DESC
`
rows, err := tx.QueryContext(ctx, query, args...)
if err != nil {
@ -354,6 +334,7 @@ func findMemoRawList(ctx context.Context, tx *sql.Tx, find *api.MemoFind) ([]*me
memoRawList := make([]*memoRaw, 0)
for rows.Next() {
var memoRaw memoRaw
var pinned sql.NullBool
if err := rows.Scan(
&memoRaw.ID,
&memoRaw.CreatorID,
@ -362,10 +343,14 @@ func findMemoRawList(ctx context.Context, tx *sql.Tx, find *api.MemoFind) ([]*me
&memoRaw.RowStatus,
&memoRaw.Content,
&memoRaw.Visibility,
&pinned,
); err != nil {
return nil, FormatError(err)
}
if pinned.Valid {
memoRaw.Pinned = pinned.Bool
}
memoRawList = append(memoRawList, &memoRaw)
}

View File

@ -56,10 +56,7 @@ func (s *Store) CreateShortcut(ctx context.Context, create *api.ShortcutCreate)
return nil, FormatError(err)
}
if err := s.cache.UpsertCache(ShortcutCache, shortcutRaw.ID, shortcutRaw); err != nil {
return nil, err
}
s.shortcutCache.Store(shortcutRaw.ID, shortcutRaw)
shortcut := shortcutRaw.toShortcut()
return shortcut, nil
@ -81,10 +78,7 @@ func (s *Store) PatchShortcut(ctx context.Context, patch *api.ShortcutPatch) (*a
return nil, FormatError(err)
}
if err := s.cache.UpsertCache(ShortcutCache, shortcutRaw.ID, shortcutRaw); err != nil {
return nil, err
}
s.shortcutCache.Store(shortcutRaw.ID, shortcutRaw)
shortcut := shortcutRaw.toShortcut()
return shortcut, nil
@ -112,13 +106,8 @@ func (s *Store) FindShortcutList(ctx context.Context, find *api.ShortcutFind) ([
func (s *Store) FindShortcut(ctx context.Context, find *api.ShortcutFind) (*api.Shortcut, error) {
if find.ID != nil {
shortcutRaw := &shortcutRaw{}
has, err := s.cache.FindCache(ShortcutCache, *find.ID, shortcutRaw)
if err != nil {
return nil, err
}
if has {
return shortcutRaw.toShortcut(), nil
if shortcut, ok := s.shortcutCache.Load(*find.ID); ok {
return shortcut.(*shortcutRaw).toShortcut(), nil
}
}
@ -138,11 +127,7 @@ func (s *Store) FindShortcut(ctx context.Context, find *api.ShortcutFind) (*api.
}
shortcutRaw := list[0]
if err := s.cache.UpsertCache(ShortcutCache, shortcutRaw.ID, shortcutRaw); err != nil {
return nil, err
}
s.shortcutCache.Store(shortcutRaw.ID, shortcutRaw)
shortcut := shortcutRaw.toShortcut()
return shortcut, nil
@ -164,8 +149,7 @@ func (s *Store) DeleteShortcut(ctx context.Context, delete *api.ShortcutDelete)
return FormatError(err)
}
s.cache.DeleteCache(ShortcutCache, *delete.ID)
s.shortcutCache.Delete(*delete.ID)
return nil
}

View File

@ -3,6 +3,7 @@ package store
import (
"context"
"database/sql"
"sync"
"github.com/usememos/memos/server/profile"
)
@ -11,17 +12,18 @@ import (
type Store struct {
db *sql.DB
profile *profile.Profile
cache *CacheService
userCache sync.Map // map[int]*userRaw
userSettingCache sync.Map // map[string]*userSettingRaw
memoCache sync.Map // map[int]*memoRaw
shortcutCache sync.Map // map[int]*shortcutRaw
}
// New creates a new instance of Store.
func New(db *sql.DB, profile *profile.Profile) *Store {
cacheService := NewCacheService()
return &Store{
db: db,
profile: profile,
cache: cacheService,
}
}

View File

@ -58,10 +58,8 @@ func (s *Store) ComposeMemoCreator(ctx context.Context, memo *api.Memo) error {
user.OpenID = ""
user.UserSettingList = nil
memo.Creator = user
return nil
}
func (s *Store) CreateUser(ctx context.Context, create *api.UserCreate) (*api.User, error) {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
@ -78,12 +76,8 @@ func (s *Store) CreateUser(ctx context.Context, create *api.UserCreate) (*api.Us
return nil, FormatError(err)
}
if err := s.cache.UpsertCache(UserCache, userRaw.ID, userRaw); err != nil {
return nil, err
}
s.userCache.Store(userRaw.ID, userRaw)
user := userRaw.toUser()
return user, nil
}
@ -103,12 +97,8 @@ func (s *Store) PatchUser(ctx context.Context, patch *api.UserPatch) (*api.User,
return nil, FormatError(err)
}
if err := s.cache.UpsertCache(UserCache, userRaw.ID, userRaw); err != nil {
return nil, err
}
s.userCache.Store(userRaw.ID, userRaw)
user := userRaw.toUser()
return user, nil
}
@ -134,13 +124,8 @@ func (s *Store) FindUserList(ctx context.Context, find *api.UserFind) ([]*api.Us
func (s *Store) FindUser(ctx context.Context, find *api.UserFind) (*api.User, error) {
if find.ID != nil {
userRaw := &userRaw{}
has, err := s.cache.FindCache(UserCache, *find.ID, userRaw)
if err != nil {
return nil, err
}
if has {
return userRaw.toUser(), nil
if user, ok := s.userCache.Load(*find.ID); ok {
return user.(*userRaw).toUser(), nil
}
}
@ -160,13 +145,8 @@ func (s *Store) FindUser(ctx context.Context, find *api.UserFind) (*api.User, er
}
userRaw := list[0]
if err := s.cache.UpsertCache(UserCache, userRaw.ID, userRaw); err != nil {
return nil, err
}
s.userCache.Store(userRaw.ID, userRaw)
user := userRaw.toUser()
return user, nil
}
@ -188,8 +168,7 @@ func (s *Store) DeleteUser(ctx context.Context, delete *api.UserDelete) error {
return err
}
s.cache.DeleteCache(UserCache, delete.ID)
s.userCache.Delete(delete.ID)
return nil
}

View File

@ -38,6 +38,7 @@ func (s *Store) UpsertUserSetting(ctx context.Context, upsert *api.UserSettingUp
return nil, err
}
s.userSettingCache.Store(getUserSettingCacheKey(*userSettingRaw), userSettingRaw)
userSetting := userSettingRaw.toUserSetting()
return userSetting, nil
@ -57,6 +58,7 @@ func (s *Store) FindUserSettingList(ctx context.Context, find *api.UserSettingFi
list := []*api.UserSetting{}
for _, raw := range userSettingRawList {
s.userSettingCache.Store(getUserSettingCacheKey(*raw), raw)
list = append(list, raw.toUserSetting())
}
@ -64,6 +66,13 @@ func (s *Store) FindUserSettingList(ctx context.Context, find *api.UserSettingFi
}
func (s *Store) FindUserSetting(ctx context.Context, find *api.UserSettingFind) (*api.UserSetting, error) {
if userSetting, ok := s.userSettingCache.Load(getUserSettingFindCacheKey(find)); ok {
if userSetting == nil {
return nil, nil
}
return userSetting.(*userSettingRaw).toUserSetting(), nil
}
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return nil, FormatError(err)
@ -76,12 +85,13 @@ func (s *Store) FindUserSetting(ctx context.Context, find *api.UserSettingFind)
}
if len(list) == 0 {
s.userSettingCache.Store(getUserSettingFindCacheKey(find), nil)
return nil, nil
}
userSetting := list[0].toUserSetting()
return userSetting, nil
userSettingRaw := list[0]
s.userSettingCache.Store(getUserSettingCacheKey(*userSettingRaw), userSettingRaw)
return userSettingRaw.toUserSetting(), nil
}
func upsertUserSetting(ctx context.Context, tx *sql.Tx, upsert *api.UserSettingUpsert) (*userSettingRaw, error) {