MM-32693: Use master when emoji_layer is invalidated (#16951)

We add a map which tracks invalidations to the emoji caches
and use master whenever a fresh item is fetched from DB.

https://mattermost.atlassian.net/browse/MM-32693

```release-note
NONE
```

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Agniva De Sarker
2021-02-26 22:17:51 +05:30
коммит произвёл GitHub
родитель 33c8b0ad73
Коммит 6dcbad049c
14 изменённых файлов: 169 добавлений и 84 удалений

Просмотреть файл

@@ -5,6 +5,7 @@ package app
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
"fmt" "fmt"
"image" "image"
@@ -57,7 +58,7 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
return nil, model.NewAppError("createEmoji", "api.emoji.create.other_user.app_error", nil, "", http.StatusForbidden) return nil, model.NewAppError("createEmoji", "api.emoji.create.other_user.app_error", nil, "", http.StatusForbidden)
} }
if existingEmoji, err := a.Srv().Store.Emoji().GetByName(emoji.Name, true); err == nil && existingEmoji != nil { if existingEmoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emoji.Name, true); err == nil && existingEmoji != nil {
return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest) return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest)
} }
@@ -185,7 +186,7 @@ func (a *App) GetEmoji(emojiId string) (*model.Emoji, *model.AppError) {
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented) return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
} }
emoji, err := a.Srv().Store.Emoji().Get(emojiId, false) emoji, err := a.Srv().Store.Emoji().Get(context.Background(), emojiId, false)
if err != nil { if err != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
switch { switch {
@@ -208,7 +209,7 @@ func (a *App) GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError) {
return nil, model.NewAppError("GetEmojiByName", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented) return nil, model.NewAppError("GetEmojiByName", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
} }
emoji, err := a.Srv().Store.Emoji().GetByName(emojiName, true) emoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emojiName, true)
if err != nil { if err != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
switch { switch {
@@ -236,7 +237,7 @@ func (a *App) GetMultipleEmojiByName(names []string) ([]*model.Emoji, *model.App
} }
func (a *App) GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) { func (a *App) GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) {
_, storeErr := a.Srv().Store.Emoji().Get(emojiId, true) _, storeErr := a.Srv().Store.Emoji().Get(context.Background(), emojiId, true)
if storeErr != nil { if storeErr != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
switch { switch {
@@ -282,7 +283,7 @@ func (a *App) GetEmojiStaticUrl(emojiName string) (string, *model.AppError) {
return path.Join(subPath, "/static/emoji", id+".png"), nil return path.Join(subPath, "/static/emoji", id+".png"), nil
} }
emoji, err := a.Srv().Store.Emoji().GetByName(emojiName, true) emoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emojiName, true)
if err == nil { if err == nil {
return path.Join(subPath, "/api/v4/emoji", emoji.Id, "image"), nil return path.Join(subPath, "/api/v4/emoji", emoji.Id, "image"), nil
} }

Просмотреть файл

@@ -1735,7 +1735,7 @@ func (a *App) importEmoji(data *EmojiImportData, dryRun bool) *model.AppError {
var emoji *model.Emoji var emoji *model.Emoji
emoji, err := a.Srv().Store.Emoji().GetByName(*data.Name, true) emoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), *data.Name, true)
if err != nil { if err != nil {
var nfErr *store.ErrNotFound var nfErr *store.ErrNotFound
if !errors.As(err, &nfErr) { if !errors.As(err, &nfErr) {

Просмотреть файл

@@ -3853,7 +3853,7 @@ func TestImportImportEmoji(t *testing.T) {
err := th.App.importEmoji(&data, true) err := th.App.importEmoji(&data, true)
assert.NotNil(t, err, "Invalid emoji should have failed dry run") assert.NotNil(t, err, "Invalid emoji should have failed dry run")
emoji, nErr := th.App.Srv().Store.Emoji().GetByName(*data.Name, true) emoji, nErr := th.App.Srv().Store.Emoji().GetByName(context.Background(), *data.Name, true)
assert.Nil(t, emoji, "Emoji should not have been imported") assert.Nil(t, emoji, "Emoji should not have been imported")
assert.Error(t, nErr) assert.Error(t, nErr)
@@ -3873,7 +3873,7 @@ func TestImportImportEmoji(t *testing.T) {
err = th.App.importEmoji(&data, false) err = th.App.importEmoji(&data, false)
assert.Nil(t, err, "Valid emoji should have succeeded apply mode") assert.Nil(t, err, "Valid emoji should have succeeded apply mode")
emoji, nErr = th.App.Srv().Store.Emoji().GetByName(*data.Name, true) emoji, nErr = th.App.Srv().Store.Emoji().GetByName(context.Background(), *data.Name, true)
assert.NotNil(t, emoji, "Emoji should have been imported") assert.NotNil(t, emoji, "Emoji should have been imported")
assert.NoError(t, nErr, "Emoji should have been imported without any error") assert.NoError(t, nErr, "Emoji should have been imported without any error")

Просмотреть файл

@@ -4,19 +4,30 @@
package localcachelayer package localcachelayer
import ( import (
"context"
"sync"
"github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/store" "github.com/mattermost/mattermost-server/v5/store"
"github.com/mattermost/mattermost-server/v5/store/sqlstore"
) )
type LocalCacheEmojiStore struct { type LocalCacheEmojiStore struct {
store.EmojiStore store.EmojiStore
rootStore *LocalCacheStore rootStore *LocalCacheStore
emojiByIdMut sync.Mutex
emojiByIdInvalidations map[string]bool
emojiByNameMut sync.Mutex
emojiByNameInvalidations map[string]bool
} }
func (es *LocalCacheEmojiStore) handleClusterInvalidateEmojiById(msg *model.ClusterMessage) { func (es *LocalCacheEmojiStore) handleClusterInvalidateEmojiById(msg *model.ClusterMessage) {
if msg.Data == ClearCacheMessageData { if msg.Data == ClearCacheMessageData {
es.rootStore.emojiCacheById.Purge() es.rootStore.emojiCacheById.Purge()
} else { } else {
es.emojiByIdMut.Lock()
es.emojiByIdInvalidations[msg.Data] = true
es.emojiByIdMut.Unlock()
es.rootStore.emojiCacheById.Remove(msg.Data) es.rootStore.emojiCacheById.Remove(msg.Data)
} }
} }
@@ -25,18 +36,30 @@ func (es *LocalCacheEmojiStore) handleClusterInvalidateEmojiIdByName(msg *model.
if msg.Data == ClearCacheMessageData { if msg.Data == ClearCacheMessageData {
es.rootStore.emojiIdCacheByName.Purge() es.rootStore.emojiIdCacheByName.Purge()
} else { } else {
es.emojiByNameMut.Lock()
es.emojiByNameInvalidations[msg.Data] = true
es.emojiByNameMut.Unlock()
es.rootStore.emojiIdCacheByName.Remove(msg.Data) es.rootStore.emojiIdCacheByName.Remove(msg.Data)
} }
} }
func (es LocalCacheEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error) { func (es *LocalCacheEmojiStore) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error) {
if allowFromCache { if allowFromCache {
if emoji, ok := es.getFromCacheById(id); ok { if emoji, ok := es.getFromCacheById(id); ok {
return emoji, nil return emoji, nil
} }
} }
emoji, err := es.EmojiStore.Get(id, allowFromCache) // If it was invalidated, then we need to query master.
es.emojiByIdMut.Lock()
if es.emojiByIdInvalidations[id] {
// And then remove the key from the map.
ctx = sqlstore.WithMaster(ctx)
delete(es.emojiByIdInvalidations, id)
}
es.emojiByIdMut.Unlock()
emoji, err := es.EmojiStore.Get(ctx, id, allowFromCache)
if allowFromCache && err == nil { if allowFromCache && err == nil {
es.addToCache(emoji) es.addToCache(emoji)
@@ -45,9 +68,9 @@ func (es LocalCacheEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji
return emoji, err return emoji, err
} }
func (es LocalCacheEmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji, error) { func (es *LocalCacheEmojiStore) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error) {
if id, ok := model.GetSystemEmojiId(name); ok { if id, ok := model.GetSystemEmojiId(name); ok {
return es.Get(id, allowFromCache) return es.Get(ctx, id, allowFromCache)
} }
if allowFromCache { if allowFromCache {
@@ -56,7 +79,16 @@ func (es LocalCacheEmojiStore) GetByName(name string, allowFromCache bool) (*mod
} }
} }
emoji, err := es.EmojiStore.GetByName(name, allowFromCache) // If it was invalidated, then we need to query master.
es.emojiByNameMut.Lock()
if es.emojiByNameInvalidations[name] {
ctx = sqlstore.WithMaster(ctx)
// And then remove the key from the map.
delete(es.emojiByNameInvalidations, name)
}
es.emojiByNameMut.Unlock()
emoji, err := es.EmojiStore.GetByName(ctx, name, allowFromCache)
if allowFromCache && err == nil { if allowFromCache && err == nil {
es.addToCache(emoji) es.addToCache(emoji)
@@ -65,7 +97,7 @@ func (es LocalCacheEmojiStore) GetByName(name string, allowFromCache bool) (*mod
return emoji, err return emoji, err
} }
func (es LocalCacheEmojiStore) Delete(emoji *model.Emoji, time int64) error { func (es *LocalCacheEmojiStore) Delete(emoji *model.Emoji, time int64) error {
err := es.EmojiStore.Delete(emoji, time) err := es.EmojiStore.Delete(emoji, time)
if err == nil { if err == nil {
@@ -75,12 +107,12 @@ func (es LocalCacheEmojiStore) Delete(emoji *model.Emoji, time int64) error {
return err return err
} }
func (es LocalCacheEmojiStore) addToCache(emoji *model.Emoji) { func (es *LocalCacheEmojiStore) addToCache(emoji *model.Emoji) {
es.rootStore.doStandardAddToCache(es.rootStore.emojiCacheById, emoji.Id, emoji) es.rootStore.doStandardAddToCache(es.rootStore.emojiCacheById, emoji.Id, emoji)
es.rootStore.doStandardAddToCache(es.rootStore.emojiIdCacheByName, emoji.Name, emoji.Id) es.rootStore.doStandardAddToCache(es.rootStore.emojiIdCacheByName, emoji.Name, emoji.Id)
} }
func (es LocalCacheEmojiStore) getFromCacheById(id string) (*model.Emoji, bool) { func (es *LocalCacheEmojiStore) getFromCacheById(id string) (*model.Emoji, bool) {
var emoji *model.Emoji var emoji *model.Emoji
if err := es.rootStore.doStandardReadCache(es.rootStore.emojiCacheById, id, &emoji); err == nil { if err := es.rootStore.doStandardReadCache(es.rootStore.emojiCacheById, id, &emoji); err == nil {
return emoji, true return emoji, true
@@ -88,7 +120,7 @@ func (es LocalCacheEmojiStore) getFromCacheById(id string) (*model.Emoji, bool)
return nil, false return nil, false
} }
func (es LocalCacheEmojiStore) getFromCacheByName(name string) (*model.Emoji, bool) { func (es *LocalCacheEmojiStore) getFromCacheByName(name string) (*model.Emoji, bool) {
var emojiId string var emojiId string
if err := es.rootStore.doStandardReadCache(es.rootStore.emojiIdCacheByName, name, &emojiId); err == nil { if err := es.rootStore.doStandardReadCache(es.rootStore.emojiIdCacheByName, name, &emojiId); err == nil {
return es.getFromCacheById(emojiId) return es.getFromCacheById(emojiId)
@@ -96,7 +128,14 @@ func (es LocalCacheEmojiStore) getFromCacheByName(name string) (*model.Emoji, bo
return nil, false return nil, false
} }
func (es LocalCacheEmojiStore) removeFromCache(emoji *model.Emoji) { func (es *LocalCacheEmojiStore) removeFromCache(emoji *model.Emoji) {
es.emojiByIdMut.Lock()
es.emojiByIdInvalidations[emoji.Id] = true
es.emojiByIdMut.Unlock()
es.rootStore.doInvalidateCacheCluster(es.rootStore.emojiCacheById, emoji.Id) es.rootStore.doInvalidateCacheCluster(es.rootStore.emojiCacheById, emoji.Id)
es.emojiByNameMut.Lock()
es.emojiByNameInvalidations[emoji.Name] = true
es.emojiByNameMut.Unlock()
es.rootStore.doInvalidateCacheCluster(es.rootStore.emojiIdCacheByName, emoji.Name) es.rootStore.doInvalidateCacheCluster(es.rootStore.emojiIdCacheByName, emoji.Name)
} }

Просмотреть файл

@@ -4,6 +4,7 @@
package localcachelayer package localcachelayer
import ( import (
"context"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -20,6 +21,7 @@ func TestEmojiStore(t *testing.T) {
func TestEmojiStoreCache(t *testing.T) { func TestEmojiStoreCache(t *testing.T) {
fakeEmoji := model.Emoji{Id: "123", Name: "name123"} fakeEmoji := model.Emoji{Id: "123", Name: "name123"}
ctxEmoji := model.Emoji{Id: "master", Name: "name123"}
t.Run("first call by id not cached, second cached and returning same data", func(t *testing.T) { t.Run("first call by id not cached, second cached and returning same data", func(t *testing.T) {
mockStore := getMockStore() mockStore := getMockStore()
@@ -27,11 +29,11 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
emoji, err := cachedStore.Emoji().Get("123", true) emoji, err := cachedStore.Emoji().Get(context.Background(), "123", true)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, emoji, &fakeEmoji) assert.Equal(t, emoji, &fakeEmoji)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
emoji, err = cachedStore.Emoji().Get("123", true) emoji, err = cachedStore.Emoji().Get(context.Background(), "123", true)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, emoji, &fakeEmoji) assert.Equal(t, emoji, &fakeEmoji)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
@@ -43,11 +45,11 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
emoji, err := cachedStore.Emoji().GetByName("name123", true) emoji, err := cachedStore.Emoji().GetByName(context.Background(), "name123", true)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, emoji, &fakeEmoji) assert.Equal(t, emoji, &fakeEmoji)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
emoji, err = cachedStore.Emoji().GetByName("name123", true) emoji, err = cachedStore.Emoji().GetByName(context.Background(), "name123", true)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, emoji, &fakeEmoji) assert.Equal(t, emoji, &fakeEmoji)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
@@ -59,9 +61,9 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
cachedStore.Emoji().Get("123", false) cachedStore.Emoji().Get(context.Background(), "123", false)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2)
}) })
@@ -71,9 +73,9 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
cachedStore.Emoji().GetByName("name123", false) cachedStore.Emoji().GetByName(context.Background(), "name123", false)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2)
}) })
@@ -83,11 +85,11 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().Get("123", false) cachedStore.Emoji().Get(context.Background(), "123", false)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2)
}) })
@@ -97,11 +99,11 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().GetByName("name123", false) cachedStore.Emoji().GetByName(context.Background(), "name123", false)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2)
}) })
@@ -111,9 +113,9 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 0) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 0)
}) })
@@ -123,9 +125,9 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 0) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 0)
}) })
@@ -135,10 +137,23 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
cachedStore.Emoji().Delete(&fakeEmoji, 0) cachedStore.Emoji().Delete(&fakeEmoji, 0)
cachedStore.Emoji().Get("123", true) cachedStore.Emoji().Get(context.Background(), "123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2)
})
t.Run("call by id, use master", func(t *testing.T) {
mockStore := getMockStore()
mockCacheProvider := getMockCacheProvider()
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err)
cachedStore.Emoji().Get(context.Background(), "master", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1)
cachedStore.Emoji().Delete(&ctxEmoji, 0)
cachedStore.Emoji().Get(context.Background(), "master", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 2)
}) })
@@ -148,10 +163,23 @@ func TestEmojiStoreCache(t *testing.T) {
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err) require.NoError(t, err)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
cachedStore.Emoji().Delete(&fakeEmoji, 0) cachedStore.Emoji().Delete(&fakeEmoji, 0)
cachedStore.Emoji().GetByName("name123", true) cachedStore.Emoji().GetByName(context.Background(), "name123", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2)
})
t.Run("call by name, use master", func(t *testing.T) {
mockStore := getMockStore()
mockCacheProvider := getMockCacheProvider()
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider)
require.NoError(t, err)
cachedStore.Emoji().GetByName(context.Background(), "master", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1)
cachedStore.Emoji().Delete(&ctxEmoji, 0)
cachedStore.Emoji().GetByName(context.Background(), "master", true)
mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 2)
}) })
} }

Просмотреть файл

@@ -81,7 +81,7 @@ type LocalCacheStore struct {
scheme LocalCacheSchemeStore scheme LocalCacheSchemeStore
schemeCache cache.Cache schemeCache cache.Cache
emoji LocalCacheEmojiStore emoji *LocalCacheEmojiStore
emojiCacheById cache.Cache emojiCacheById cache.Cache
emojiIdCacheByName cache.Cache emojiIdCacheByName cache.Cache
@@ -197,7 +197,12 @@ func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterf
}); err != nil { }); err != nil {
return return
} }
localCacheStore.emoji = LocalCacheEmojiStore{EmojiStore: baseStore.Emoji(), rootStore: &localCacheStore} localCacheStore.emoji = &LocalCacheEmojiStore{
EmojiStore: baseStore.Emoji(),
rootStore: &localCacheStore,
emojiByIdInvalidations: make(map[string]bool),
emojiByNameInvalidations: make(map[string]bool),
}
// Channels // Channels
if localCacheStore.channelPinnedPostCountsCache, err = cacheProvider.NewCache(&cache.CacheOptions{ if localCacheStore.channelPinnedPostCountsCache, err = cacheProvider.NewCache(&cache.CacheOptions{

Просмотреть файл

@@ -4,6 +4,7 @@
package localcachelayer package localcachelayer
import ( import (
"context"
"fmt" "fmt"
"testing" "testing"
@@ -14,6 +15,7 @@ import (
"github.com/mattermost/mattermost-server/v5/services/cache" "github.com/mattermost/mattermost-server/v5/services/cache"
cachemocks "github.com/mattermost/mattermost-server/v5/services/cache/mocks" cachemocks "github.com/mattermost/mattermost-server/v5/services/cache/mocks"
"github.com/mattermost/mattermost-server/v5/store" "github.com/mattermost/mattermost-server/v5/store"
"github.com/mattermost/mattermost-server/v5/store/sqlstore"
"github.com/mattermost/mattermost-server/v5/store/storetest/mocks" "github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
"github.com/mattermost/mattermost-server/v5/testlib" "github.com/mattermost/mattermost-server/v5/testlib"
) )
@@ -68,12 +70,18 @@ func getMockStore() *mocks.Store {
mockStore.On("Webhook").Return(&mockWebhookStore) mockStore.On("Webhook").Return(&mockWebhookStore)
fakeEmoji := model.Emoji{Id: "123", Name: "name123"} fakeEmoji := model.Emoji{Id: "123", Name: "name123"}
ctxEmoji := model.Emoji{Id: "master", Name: "name123"}
mockEmojiStore := mocks.EmojiStore{} mockEmojiStore := mocks.EmojiStore{}
mockEmojiStore.On("Get", "123", true).Return(&fakeEmoji, nil) mockEmojiStore.On("Get", mock.Anything, "123", true).Return(&fakeEmoji, nil)
mockEmojiStore.On("Get", "123", false).Return(&fakeEmoji, nil) mockEmojiStore.On("Get", mock.Anything, "123", false).Return(&fakeEmoji, nil)
mockEmojiStore.On("GetByName", "name123", true).Return(&fakeEmoji, nil) mockEmojiStore.On("Get", context.Background(), "master", true).Return(&ctxEmoji, nil)
mockEmojiStore.On("GetByName", "name123", false).Return(&fakeEmoji, nil) mockEmojiStore.On("Get", sqlstore.WithMaster(context.Background()), "master", true).Return(&ctxEmoji, nil)
mockEmojiStore.On("GetByName", mock.Anything, "name123", true).Return(&fakeEmoji, nil)
mockEmojiStore.On("GetByName", mock.Anything, "name123", false).Return(&fakeEmoji, nil)
mockEmojiStore.On("GetByName", context.Background(), "master", true).Return(&ctxEmoji, nil)
mockEmojiStore.On("GetByName", sqlstore.WithMaster(context.Background()), "master", false).Return(&ctxEmoji, nil)
mockEmojiStore.On("Delete", &fakeEmoji, int64(0)).Return(nil) mockEmojiStore.On("Delete", &fakeEmoji, int64(0)).Return(nil)
mockEmojiStore.On("Delete", &ctxEmoji, int64(0)).Return(nil)
mockStore.On("Emoji").Return(&mockEmojiStore) mockStore.On("Emoji").Return(&mockEmojiStore)
mockCount := int64(10) mockCount := int64(10)

Просмотреть файл

@@ -2807,7 +2807,7 @@ func (s *OpenTracingLayerEmojiStore) Delete(emoji *model.Emoji, time int64) erro
return err return err
} }
func (s *OpenTracingLayerEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error) { func (s *OpenTracingLayerEmojiStore) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "EmojiStore.Get") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "EmojiStore.Get")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
@@ -2816,7 +2816,7 @@ func (s *OpenTracingLayerEmojiStore) Get(id string, allowFromCache bool) (*model
}() }()
defer span.Finish() defer span.Finish()
result, err := s.EmojiStore.Get(id, allowFromCache) result, err := s.EmojiStore.Get(ctx, id, allowFromCache)
if err != nil { if err != nil {
span.LogFields(spanlog.Error(err)) span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true) ext.Error.Set(span, true)
@@ -2825,7 +2825,7 @@ func (s *OpenTracingLayerEmojiStore) Get(id string, allowFromCache bool) (*model
return result, err return result, err
} }
func (s *OpenTracingLayerEmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji, error) { func (s *OpenTracingLayerEmojiStore) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "EmojiStore.GetByName") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "EmojiStore.GetByName")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
@@ -2834,7 +2834,7 @@ func (s *OpenTracingLayerEmojiStore) GetByName(name string, allowFromCache bool)
}() }()
defer span.Finish() defer span.Finish()
result, err := s.EmojiStore.GetByName(name, allowFromCache) result, err := s.EmojiStore.GetByName(ctx, name, allowFromCache)
if err != nil { if err != nil {
span.LogFields(spanlog.Error(err)) span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true) ext.Error.Set(span, true)

Просмотреть файл

@@ -3004,11 +3004,11 @@ func (s *RetryLayerEmojiStore) Delete(emoji *model.Emoji, time int64) error {
} }
func (s *RetryLayerEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error) { func (s *RetryLayerEmojiStore) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error) {
tries := 0 tries := 0
for { for {
result, err := s.EmojiStore.Get(id, allowFromCache) result, err := s.EmojiStore.Get(ctx, id, allowFromCache)
if err == nil { if err == nil {
return result, nil return result, nil
} }
@@ -3024,11 +3024,11 @@ func (s *RetryLayerEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji
} }
func (s *RetryLayerEmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji, error) { func (s *RetryLayerEmojiStore) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error) {
tries := 0 tries := 0
for { for {
result, err := s.EmojiStore.GetByName(name, allowFromCache) result, err := s.EmojiStore.GetByName(ctx, name, allowFromCache)
if err == nil { if err == nil {
return result, nil return result, nil
} }

Просмотреть файл

@@ -4,6 +4,7 @@
package sqlstore package sqlstore
import ( import (
"context"
"database/sql" "database/sql"
"fmt" "fmt"
@@ -57,12 +58,12 @@ func (es SqlEmojiStore) Save(emoji *model.Emoji) (*model.Emoji, error) {
return emoji, nil return emoji, nil
} }
func (es SqlEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error) { func (es SqlEmojiStore) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error) {
return es.getBy("Id", id) return es.getBy(ctx, "Id", id)
} }
func (es SqlEmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji, error) { func (es SqlEmojiStore) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error) {
return es.getBy("Name", name) return es.getBy(ctx, "Name", name)
} }
func (es SqlEmojiStore) GetMultipleByName(names []string) ([]*model.Emoji, error) { func (es SqlEmojiStore) GetMultipleByName(names []string) ([]*model.Emoji, error) {
@@ -146,10 +147,10 @@ func (es SqlEmojiStore) Search(name string, prefixOnly bool, limit int) ([]*mode
} }
// getBy returns one active (not deleted) emoji, found by any one column (what/key). // getBy returns one active (not deleted) emoji, found by any one column (what/key).
func (es SqlEmojiStore) getBy(what, key string) (*model.Emoji, error) { func (es SqlEmojiStore) getBy(ctx context.Context, what, key string) (*model.Emoji, error) {
var emoji *model.Emoji var emoji *model.Emoji
err := es.GetReplica().SelectOne(&emoji, err := es.DBFromContext(ctx).SelectOne(&emoji,
`SELECT `SELECT
* *
FROM FROM

Просмотреть файл

@@ -546,8 +546,8 @@ type TokenStore interface {
type EmojiStore interface { type EmojiStore interface {
Save(emoji *model.Emoji) (*model.Emoji, error) Save(emoji *model.Emoji) (*model.Emoji, error)
Get(id string, allowFromCache bool) (*model.Emoji, error) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error)
GetByName(name string, allowFromCache bool) (*model.Emoji, error) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error)
GetMultipleByName(names []string) ([]*model.Emoji, error) GetMultipleByName(names []string) ([]*model.Emoji, error)
GetList(offset, limit int, sort string) ([]*model.Emoji, error) GetList(offset, limit int, sort string) ([]*model.Emoji, error)
Delete(emoji *model.Emoji, time int64) error Delete(emoji *model.Emoji, time int64) error

Просмотреть файл

@@ -4,6 +4,7 @@
package storetest package storetest
import ( import (
"context"
"testing" "testing"
"time" "time"
@@ -80,12 +81,12 @@ func testEmojiGet(t *testing.T, ss store.Store) {
}() }()
for _, emoji := range emojis { for _, emoji := range emojis {
_, err := ss.Emoji().Get(emoji.Id, false) _, err := ss.Emoji().Get(context.Background(), emoji.Id, false)
require.NoErrorf(t, err, "failed to get emoji with id %v", emoji.Id) require.NoErrorf(t, err, "failed to get emoji with id %v", emoji.Id)
} }
for _, emoji := range emojis { for _, emoji := range emojis {
_, err := ss.Emoji().Get(emoji.Id, true) _, err := ss.Emoji().Get(context.Background(), emoji.Id, true)
require.NoErrorf(t, err, "failed to get emoji with id %v", emoji.Id) require.NoErrorf(t, err, "failed to get emoji with id %v", emoji.Id)
} }
} }
@@ -119,7 +120,7 @@ func testEmojiGetByName(t *testing.T, ss store.Store) {
}() }()
for _, emoji := range emojis { for _, emoji := range emojis {
_, err := ss.Emoji().GetByName(emoji.Name, true) _, err := ss.Emoji().GetByName(context.Background(), emoji.Name, true)
require.NoErrorf(t, err, "failed to get emoji with name %v", emoji.Name) require.NoErrorf(t, err, "failed to get emoji with name %v", emoji.Name)
} }
} }

Просмотреть файл

@@ -5,6 +5,8 @@
package mocks package mocks
import ( import (
context "context"
model "github.com/mattermost/mattermost-server/v5/model" model "github.com/mattermost/mattermost-server/v5/model"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
) )
@@ -28,13 +30,13 @@ func (_m *EmojiStore) Delete(emoji *model.Emoji, time int64) error {
return r0 return r0
} }
// Get provides a mock function with given fields: id, allowFromCache // Get provides a mock function with given fields: ctx, id, allowFromCache
func (_m *EmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error) { func (_m *EmojiStore) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error) {
ret := _m.Called(id, allowFromCache) ret := _m.Called(ctx, id, allowFromCache)
var r0 *model.Emoji var r0 *model.Emoji
if rf, ok := ret.Get(0).(func(string, bool) *model.Emoji); ok { if rf, ok := ret.Get(0).(func(context.Context, string, bool) *model.Emoji); ok {
r0 = rf(id, allowFromCache) r0 = rf(ctx, id, allowFromCache)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Emoji) r0 = ret.Get(0).(*model.Emoji)
@@ -42,8 +44,8 @@ func (_m *EmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error)
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(string, bool) error); ok { if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok {
r1 = rf(id, allowFromCache) r1 = rf(ctx, id, allowFromCache)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -51,13 +53,13 @@ func (_m *EmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error)
return r0, r1 return r0, r1
} }
// GetByName provides a mock function with given fields: name, allowFromCache // GetByName provides a mock function with given fields: ctx, name, allowFromCache
func (_m *EmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji, error) { func (_m *EmojiStore) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error) {
ret := _m.Called(name, allowFromCache) ret := _m.Called(ctx, name, allowFromCache)
var r0 *model.Emoji var r0 *model.Emoji
if rf, ok := ret.Get(0).(func(string, bool) *model.Emoji); ok { if rf, ok := ret.Get(0).(func(context.Context, string, bool) *model.Emoji); ok {
r0 = rf(name, allowFromCache) r0 = rf(ctx, name, allowFromCache)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Emoji) r0 = ret.Get(0).(*model.Emoji)
@@ -65,8 +67,8 @@ func (_m *EmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji,
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(string, bool) error); ok { if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok {
r1 = rf(name, allowFromCache) r1 = rf(ctx, name, allowFromCache)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }

Просмотреть файл

@@ -2575,10 +2575,10 @@ func (s *TimerLayerEmojiStore) Delete(emoji *model.Emoji, time int64) error {
return err return err
} }
func (s *TimerLayerEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji, error) { func (s *TimerLayerEmojiStore) Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error) {
start := timemodule.Now() start := timemodule.Now()
result, err := s.EmojiStore.Get(id, allowFromCache) result, err := s.EmojiStore.Get(ctx, id, allowFromCache)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {
@@ -2591,10 +2591,10 @@ func (s *TimerLayerEmojiStore) Get(id string, allowFromCache bool) (*model.Emoji
return result, err return result, err
} }
func (s *TimerLayerEmojiStore) GetByName(name string, allowFromCache bool) (*model.Emoji, error) { func (s *TimerLayerEmojiStore) GetByName(ctx context.Context, name string, allowFromCache bool) (*model.Emoji, error) {
start := timemodule.Now() start := timemodule.Now()
result, err := s.EmojiStore.GetByName(name, allowFromCache) result, err := s.EmojiStore.GetByName(ctx, name, allowFromCache)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {