Initial cache migration to new layers system (#11878)

* Initial cache migration to new layers system

* Fixing wrong change

* Remove unneeded constants

* Remove unneeded file

* Adding license headers
This commit is contained in:
Jesús Espino
2019-08-21 20:23:06 +02:00
committed by GitHub
parent bd152c6534
commit f8ad9f3b8f
15 changed files with 398 additions and 555 deletions

View File

@@ -13,6 +13,7 @@ import (
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/services/mailservice"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/store/localcachelayer"
"github.com/mattermost/mattermost-server/store/sqlstore"
"github.com/mattermost/mattermost-server/utils"
"github.com/pkg/errors"
@@ -61,7 +62,7 @@ func (s *Server) RunOldAppInitalization() error {
if s.FakeApp().Srv.newStore == nil {
s.FakeApp().Srv.newStore = func() store.Store {
return store.NewTimerLayer(store.NewLayeredStore(sqlstore.NewSqlSupplier(s.FakeApp().Config().SqlSettings, s.Metrics), s.Metrics, s.Cluster), s.Metrics)
return store.NewTimerLayer(localcachelayer.NewLocalCacheLayer(store.NewLayeredStore(sqlstore.NewSqlSupplier(s.FakeApp().Config().SqlSettings, s.Metrics), s.Metrics, s.Cluster), s.Metrics, s.Cluster), s.Metrics)
}
}

View File

@@ -22,7 +22,6 @@ type LayeredStoreDatabaseLayer interface {
type LayeredStore struct {
TmpContext context.Context
ReactionStore ReactionStore
RoleStore RoleStore
SchemeStore SchemeStore
DatabaseLayer LayeredStoreDatabaseLayer
@@ -38,7 +37,6 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn
LocalCacheLayer: NewLocalCacheSupplier(metrics, cluster),
}
store.ReactionStore = &LayeredReactionStore{store}
store.RoleStore = &LayeredRoleStore{store}
store.SchemeStore = &LayeredSchemeStore{store}
@@ -143,7 +141,7 @@ func (s *LayeredStore) FileInfo() FileInfoStore {
}
func (s *LayeredStore) Reaction() ReactionStore {
return s.ReactionStore
return s.DatabaseLayer.Reaction()
}
func (s *LayeredStore) Job() JobStore {
@@ -219,34 +217,6 @@ func (s *LayeredStore) TotalSearchDbConnections() int {
return s.DatabaseLayer.TotalSearchDbConnections()
}
type LayeredReactionStore struct {
*LayeredStore
}
func (s *LayeredReactionStore) Save(reaction *model.Reaction) (*model.Reaction, *model.AppError) {
return s.LayerChainHead.ReactionSave(s.TmpContext, reaction)
}
func (s *LayeredReactionStore) Delete(reaction *model.Reaction) (*model.Reaction, *model.AppError) {
return s.LayerChainHead.ReactionDelete(s.TmpContext, reaction)
}
func (s *LayeredReactionStore) GetForPost(postId string, allowFromCache bool) ([]*model.Reaction, *model.AppError) {
return s.LayerChainHead.ReactionGetForPost(s.TmpContext, postId)
}
func (s *LayeredReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, *model.AppError) {
return s.LayerChainHead.ReactionsBulkGetForPosts(s.TmpContext, postIds)
}
func (s *LayeredReactionStore) DeleteAllWithEmojiName(emojiName string) *model.AppError {
return s.LayerChainHead.ReactionDeleteAllWithEmojiName(s.TmpContext, emojiName)
}
func (s *LayeredReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) {
return s.LayerChainHead.ReactionPermanentDeleteBatch(s.TmpContext, endTime, limit)
}
type LayeredRoleStore struct {
*LayeredStore
}

View File

@@ -21,16 +21,6 @@ type LayeredStoreSupplier interface {
SetChainNext(LayeredStoreSupplier)
Next() LayeredStoreSupplier
//
// Reactions
//), hints ...LayeredStoreHint)
ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) (*model.Reaction, *model.AppError)
ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) (*model.Reaction, *model.AppError)
ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) ([]*model.Reaction, *model.AppError)
ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *model.AppError
ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) (int64, *model.AppError)
ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...LayeredStoreHint) ([]*model.Reaction, *model.AppError)
// Roles
RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) (*model.Role, *model.AppError)
RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) (*model.Role, *model.AppError)

View File

@@ -12,9 +12,6 @@ import (
)
const (
REACTION_CACHE_SIZE = 20000
REACTION_CACHE_SEC = 30 * 60
ROLE_CACHE_SIZE = 20000
ROLE_CACHE_SEC = 30 * 60
@@ -28,12 +25,11 @@ const (
)
type LocalCacheSupplier struct {
next LayeredStoreSupplier
reactionCache *utils.Cache
roleCache *utils.Cache
schemeCache *utils.Cache
metrics einterfaces.MetricsInterface
cluster einterfaces.ClusterInterface
next LayeredStoreSupplier
roleCache *utils.Cache
schemeCache *utils.Cache
metrics einterfaces.MetricsInterface
cluster einterfaces.ClusterInterface
}
// Caching Interface
@@ -50,15 +46,13 @@ type ObjectCache interface {
func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) *LocalCacheSupplier {
supplier := &LocalCacheSupplier{
reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS),
roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES),
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
metrics: metrics,
cluster: cluster,
roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES),
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
metrics: metrics,
cluster: cluster,
}
if cluster != nil {
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, supplier.handleClusterInvalidateReaction)
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, supplier.handleClusterInvalidateRole)
}
@@ -128,7 +122,6 @@ func (s *LocalCacheSupplier) doClearCacheCluster(cache ObjectCache) {
}
func (s *LocalCacheSupplier) Invalidate() {
s.doClearCacheCluster(s.reactionCache)
s.doClearCacheCluster(s.roleCache)
s.doClearCacheCluster(s.schemeCache)
}

View File

@@ -1,63 +0,0 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"context"
"github.com/mattermost/mattermost-server/model"
)
func (s *LocalCacheSupplier) handleClusterInvalidateReaction(msg *model.ClusterMessage) {
if msg.Data == CLEAR_CACHE_MESSAGE_DATA {
s.reactionCache.Purge()
} else {
s.reactionCache.Remove(msg.Data)
}
}
func (s *LocalCacheSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) (*model.Reaction, *model.AppError) {
defer s.doInvalidateCacheCluster(s.reactionCache, reaction.PostId)
return s.Next().ReactionSave(ctx, reaction, hints...)
}
func (s *LocalCacheSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) (*model.Reaction, *model.AppError) {
defer s.doInvalidateCacheCluster(s.reactionCache, reaction.PostId)
return s.Next().ReactionDelete(ctx, reaction, hints...)
}
func (s *LocalCacheSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
if result := s.doStandardReadCache(ctx, s.reactionCache, postId, hints...); result != nil {
return result.Data.([]*model.Reaction), nil
}
reaction, err := s.Next().ReactionGetForPost(ctx, postId, hints...)
if err != nil {
return nil, err
}
result := NewSupplierResult()
result.Data = reaction
s.doStandardAddToCache(ctx, s.reactionCache, postId, result, hints...)
return reaction, nil
}
func (s *LocalCacheSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *model.AppError {
// This could be improved. Right now we just clear the whole
// cache because we don't have a way find what post Ids have this emoji name.
defer s.doClearCacheCluster(s.reactionCache)
return s.Next().ReactionDeleteAllWithEmojiName(ctx, emojiName, hints...)
}
func (s *LocalCacheSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) (int64, *model.AppError) {
// Don't bother to clear the cache as the posts will be gone anyway and the reactions being deleted will
// expire from the cache in due course.
return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit)
}
func (s *LocalCacheSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
// Ignoring this.
return s.Next().ReactionsBulkGetForPosts(ctx, postIds, hints...)
}

View File

@@ -0,0 +1,97 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package localcachelayer
import (
"github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
)
const (
REACTION_CACHE_SIZE = 20000
REACTION_CACHE_SEC = 30 * 60
CLEAR_CACHE_MESSAGE_DATA = ""
)
type LocalCacheStore struct {
store.Store
metrics einterfaces.MetricsInterface
cluster einterfaces.ClusterInterface
reaction LocalCacheReactionStore
reactionCache *utils.Cache
}
func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) LocalCacheStore {
localCacheStore := LocalCacheStore{
Store: baseStore,
cluster: cluster,
metrics: metrics,
}
localCacheStore.reactionCache = utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS)
localCacheStore.reaction = LocalCacheReactionStore{ReactionStore: baseStore.Reaction(), rootStore: &localCacheStore}
if cluster != nil {
cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, localCacheStore.reaction.handleClusterInvalidateReaction)
}
return localCacheStore
}
func (s LocalCacheStore) Reaction() store.ReactionStore {
return s.reaction
}
func (s LocalCacheStore) DropAllTables() {
s.Invalidate()
s.Store.DropAllTables()
}
func (s *LocalCacheStore) doInvalidateCacheCluster(cache *utils.Cache, key string) {
cache.Remove(key)
if s.cluster != nil {
msg := &model.ClusterMessage{
Event: cache.GetInvalidateClusterEvent(),
SendType: model.CLUSTER_SEND_BEST_EFFORT,
Data: key,
}
s.cluster.SendClusterMessage(msg)
}
}
func (s *LocalCacheStore) doStandardAddToCache(cache *utils.Cache, key string, value interface{}) {
cache.AddWithDefaultExpires(key, value)
}
func (s *LocalCacheStore) doStandardReadCache(cache *utils.Cache, key string) interface{} {
if cacheItem, ok := cache.Get(key); ok {
if s.metrics != nil {
s.metrics.IncrementMemCacheHitCounter(cache.Name())
}
return cacheItem
}
if s.metrics != nil {
s.metrics.IncrementMemCacheMissCounter(cache.Name())
}
return nil
}
func (s *LocalCacheStore) doClearCacheCluster(cache *utils.Cache) {
cache.Purge()
if s.cluster != nil {
msg := &model.ClusterMessage{
Event: cache.GetInvalidateClusterEvent(),
SendType: model.CLUSTER_SEND_BEST_EFFORT,
Data: CLEAR_CACHE_MESSAGE_DATA,
}
s.cluster.SendClusterMessage(msg)
}
}
func (s *LocalCacheStore) Invalidate() {
s.doClearCacheCluster(s.reactionCache)
}

View File

@@ -0,0 +1,99 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package localcachelayer
import (
"sync"
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/store/sqlstore"
"github.com/mattermost/mattermost-server/store/storetest"
)
type storeType struct {
Name string
SqlSettings *model.SqlSettings
SqlSupplier *sqlstore.SqlSupplier
Store store.Store
}
var storeTypes []*storeType
func StoreTest(t *testing.T, f func(*testing.T, store.Store)) {
defer func() {
if err := recover(); err != nil {
tearDownStores()
panic(err)
}
}()
for _, st := range storeTypes {
st := st
t.Run(st.Name, func(t *testing.T) { f(t, st.Store) })
}
}
func StoreTestWithSqlSupplier(t *testing.T, f func(*testing.T, store.Store, storetest.SqlSupplier)) {
defer func() {
if err := recover(); err != nil {
tearDownStores()
panic(err)
}
}()
for _, st := range storeTypes {
st := st
t.Run(st.Name, func(t *testing.T) { f(t, st.Store, st.SqlSupplier) })
}
}
func initStores() {
storeTypes = append(storeTypes, &storeType{
Name: "LocalCache+MySQL",
SqlSettings: storetest.MakeSqlSettings(model.DATABASE_DRIVER_MYSQL),
})
storeTypes = append(storeTypes, &storeType{
Name: "LocalCache+PostgreSQL",
SqlSettings: storetest.MakeSqlSettings(model.DATABASE_DRIVER_POSTGRES),
})
defer func() {
if err := recover(); err != nil {
tearDownStores()
panic(err)
}
}()
var wg sync.WaitGroup
for _, st := range storeTypes {
st := st
wg.Add(1)
go func() {
defer wg.Done()
st.SqlSupplier = sqlstore.NewSqlSupplier(*st.SqlSettings, nil)
st.Store = NewLocalCacheLayer(store.NewLayeredStore(st.SqlSupplier, nil, nil), nil, nil)
st.Store.DropAllTables()
st.Store.MarkSystemRanUnitTests()
}()
}
wg.Wait()
}
var tearDownStoresOnce sync.Once
func tearDownStores() {
tearDownStoresOnce.Do(func() {
var wg sync.WaitGroup
wg.Add(len(storeTypes))
for _, st := range storeTypes {
st := st
go func() {
if st.Store != nil {
st.Store.Close()
}
wg.Done()
}()
}
wg.Wait()
})
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package localcachelayer
import (
"testing"
"github.com/mattermost/mattermost-server/testlib"
)
var mainHelper *testlib.MainHelper
func TestMain(m *testing.M) {
mainHelper = testlib.NewMainHelperWithOptions(nil)
defer mainHelper.Close()
initStores()
mainHelper.Main(m)
tearDownStores()
}

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package localcachelayer
import (
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
type LocalCacheReactionStore struct {
store.ReactionStore
rootStore *LocalCacheStore
}
func (s *LocalCacheReactionStore) handleClusterInvalidateReaction(msg *model.ClusterMessage) {
if msg.Data == CLEAR_CACHE_MESSAGE_DATA {
s.rootStore.reactionCache.Purge()
} else {
s.rootStore.reactionCache.Remove(msg.Data)
}
}
func (s LocalCacheReactionStore) Save(reaction *model.Reaction) (*model.Reaction, *model.AppError) {
defer s.rootStore.doInvalidateCacheCluster(s.rootStore.reactionCache, reaction.PostId)
return s.ReactionStore.Save(reaction)
}
func (s LocalCacheReactionStore) Delete(reaction *model.Reaction) (*model.Reaction, *model.AppError) {
defer s.rootStore.doInvalidateCacheCluster(s.rootStore.reactionCache, reaction.PostId)
return s.ReactionStore.Delete(reaction)
}
func (s LocalCacheReactionStore) GetForPost(postId string, allowFromCache bool) ([]*model.Reaction, *model.AppError) {
if !allowFromCache {
return s.ReactionStore.GetForPost(postId, false)
}
if reaction := s.rootStore.doStandardReadCache(s.rootStore.reactionCache, postId); reaction != nil {
return reaction.([]*model.Reaction), nil
}
reaction, err := s.ReactionStore.GetForPost(postId, false)
if err != nil {
return nil, err
}
s.rootStore.doStandardAddToCache(s.rootStore.reactionCache, postId, reaction)
return reaction, nil
}
func (s LocalCacheReactionStore) DeleteAllWithEmojiName(emojiName string) *model.AppError {
// This could be improved. Right now we just clear the whole
// cache because we don't have a way find what post Ids have this emoji name.
defer s.rootStore.doClearCacheCluster(s.rootStore.reactionCache)
return s.ReactionStore.DeleteAllWithEmojiName(emojiName)
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package localcachelayer
import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest"
"github.com/mattermost/mattermost-server/store/storetest/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReactionStore(t *testing.T) {
StoreTest(t, storetest.TestReactionStore)
}
func TestReactionStoreCache(t *testing.T) {
fakeReaction := model.Reaction{PostId: "123"}
t.Run("first call not cached, second cached and returning same data", func(t *testing.T) {
mockStore := mocks.Store{}
mockReactionsStore := mocks.ReactionStore{}
mockReactionsStore.On("Save", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("Delete", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("GetForPost", "123", false).Return([]*model.Reaction{&fakeReaction}, nil)
mockReactionsStore.On("GetForPost", "123", true).Return([]*model.Reaction{&fakeReaction}, nil)
mockStore.On("Reaction").Return(&mockReactionsStore)
cachedStore := NewLocalCacheLayer(&mockStore, nil, nil)
reaction, err := cachedStore.Reaction().GetForPost("123", true)
require.Nil(t, err)
assert.Equal(t, reaction, []*model.Reaction{&fakeReaction})
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1)
require.Nil(t, err)
assert.Equal(t, reaction, []*model.Reaction{&fakeReaction})
cachedStore.Reaction().GetForPost("123", true)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1)
})
t.Run("first call not cached, second force no cached", func(t *testing.T) {
mockStore := mocks.Store{}
mockReactionsStore := mocks.ReactionStore{}
mockReactionsStore.On("Save", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("Delete", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("GetForPost", "123", false).Return([]*model.Reaction{&fakeReaction}, nil)
mockReactionsStore.On("GetForPost", "123", true).Return([]*model.Reaction{&fakeReaction}, nil)
mockStore.On("Reaction").Return(&mockReactionsStore)
cachedStore := NewLocalCacheLayer(&mockStore, nil, nil)
cachedStore.Reaction().GetForPost("123", true)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1)
cachedStore.Reaction().GetForPost("123", false)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 2)
})
t.Run("first call not cached, save, and then not cached again", func(t *testing.T) {
mockStore := mocks.Store{}
mockReactionsStore := mocks.ReactionStore{}
mockReactionsStore.On("Save", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("Delete", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("GetForPost", "123", false).Return([]*model.Reaction{&fakeReaction}, nil)
mockReactionsStore.On("GetForPost", "123", true).Return([]*model.Reaction{&fakeReaction}, nil)
mockStore.On("Reaction").Return(&mockReactionsStore)
cachedStore := NewLocalCacheLayer(&mockStore, nil, nil)
cachedStore.Reaction().GetForPost("123", true)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1)
cachedStore.Reaction().Save(&fakeReaction)
cachedStore.Reaction().GetForPost("123", true)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 2)
})
t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) {
mockStore := mocks.Store{}
mockReactionsStore := mocks.ReactionStore{}
mockReactionsStore.On("Save", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("Delete", &fakeReaction).Return(&model.Reaction{}, nil)
mockReactionsStore.On("GetForPost", "123", false).Return([]*model.Reaction{&fakeReaction}, nil)
mockReactionsStore.On("GetForPost", "123", true).Return([]*model.Reaction{&fakeReaction}, nil)
mockStore.On("Reaction").Return(&mockReactionsStore)
cachedStore := NewLocalCacheLayer(&mockStore, nil, nil)
cachedStore.Reaction().GetForPost("123", true)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1)
cachedStore.Reaction().Delete(&fakeReaction)
cachedStore.Reaction().GetForPost("123", true)
mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 2)
})
}

View File

@@ -1,63 +0,0 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"context"
"github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
func (s *RedisSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) (*model.Reaction, *model.AppError) {
if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
mlog.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
}
return s.Next().ReactionSave(ctx, reaction, hints...)
}
func (s *RedisSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) (*model.Reaction, *model.AppError) {
if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
mlog.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
}
return s.Next().ReactionDelete(ctx, reaction, hints...)
}
func (s *RedisSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
var reaction []*model.Reaction
found, err := s.load("reactions:"+postId, &reaction)
if found {
return reaction, nil
}
if err != nil {
mlog.Error("Redis encountered an error on read: " + err.Error())
}
reaction, appErr := s.Next().ReactionGetForPost(ctx, postId, hints...)
if appErr != nil {
return nil, appErr
}
if err := s.save("reactions:"+postId, reaction, REDIS_EXPIRY_TIME); err != nil {
mlog.Error("Redis encountered and error on write: " + err.Error())
}
return reaction, nil
}
func (s *RedisSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *model.AppError {
// Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
return s.Next().ReactionDeleteAllWithEmojiName(ctx, emojiName, hints...)
}
func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) (int64, *model.AppError) {
// Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...)
}
func (s *RedisSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
// Ignoring this.
return s.Next().ReactionsBulkGetForPosts(ctx, postIds, hints...)
}

View File

@@ -151,9 +151,9 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
supplier.oldStores.TermsOfService = NewSqlTermsOfServiceStore(supplier, metrics)
supplier.oldStores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier)
supplier.oldStores.linkMetadata = NewSqlLinkMetadataStore(supplier)
supplier.oldStores.reaction = NewSqlReactionStore(supplier)
supplier.oldStores.group = NewSqlGroupStore(supplier)
initSqlSupplierReactions(supplier)
initSqlSupplierRoles(supplier)
initSqlSupplierSchemes(supplier)

View File

@@ -4,7 +4,6 @@
package sqlstore
import (
"context"
"fmt"
"net/http"
@@ -14,16 +13,27 @@ import (
"github.com/mattermost/mattermost-server/store"
)
func initSqlSupplierReactions(sqlStore SqlStore) {
type SqlReactionStore struct {
SqlStore
}
func NewSqlReactionStore(sqlStore SqlStore) store.ReactionStore {
s := &SqlReactionStore{sqlStore}
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.Reaction{}, "Reactions").SetKeys(false, "UserId", "PostId", "EmojiName")
table.ColMap("UserId").SetMaxSize(26)
table.ColMap("PostId").SetMaxSize(26)
table.ColMap("EmojiName").SetMaxSize(64)
}
return s
}
func (s *SqlSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) (*model.Reaction, *model.AppError) {
func (s SqlReactionStore) CreateIndexesIfNotExists() {
}
func (s *SqlReactionStore) Save(reaction *model.Reaction) (*model.Reaction, *model.AppError) {
reaction.PreSave()
if err := reaction.IsValid(); err != nil {
return nil, err
@@ -49,7 +59,7 @@ func (s *SqlSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction
return reaction, nil
}
func (s *SqlSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) (*model.Reaction, *model.AppError) {
func (s *SqlReactionStore) Delete(reaction *model.Reaction) (*model.Reaction, *model.AppError) {
transaction, err := s.GetMaster().Begin()
if err != nil {
return nil, model.NewAppError("SqlReactionStore.Delete", "store.sql_reaction.delete.begin.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -68,7 +78,7 @@ func (s *SqlSupplier) ReactionDelete(ctx context.Context, reaction *model.Reacti
return reaction, nil
}
func (s *SqlSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...store.LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
func (s *SqlReactionStore) GetForPost(postId string, allowFromCache bool) ([]*model.Reaction, *model.AppError) {
var reactions []*model.Reaction
if _, err := s.GetReplica().Select(&reactions,
@@ -86,7 +96,7 @@ func (s *SqlSupplier) ReactionGetForPost(ctx context.Context, postId string, hin
return reactions, nil
}
func (s *SqlSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...store.LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
func (s *SqlReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, *model.AppError) {
keys, params := MapStringsToQueryParams(postIds, "postId")
var reactions []*model.Reaction
@@ -103,7 +113,7 @@ func (s *SqlSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []st
return reactions, nil
}
func (s *SqlSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...store.LayeredStoreHint) *model.AppError {
func (s *SqlReactionStore) DeleteAllWithEmojiName(emojiName string) *model.AppError {
var reactions []*model.Reaction
if _, err := s.GetReplica().Select(&reactions,
@@ -138,7 +148,7 @@ func (s *SqlSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiN
return nil
}
func (s *SqlSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...store.LayeredStoreHint) (int64, *model.AppError) {
func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) {
var query string
if s.DriverName() == "postgres" {
query = "DELETE from Reactions WHERE CreateAt = any (array (SELECT CreateAt FROM Reactions WHERE CreateAt < :EndTime LIMIT :Limit))"

View File

@@ -368,187 +368,6 @@ func (_m *LayeredStoreDatabaseLayer) Reaction() store.ReactionStore {
return r0
}
// ReactionDelete provides a mock function with given fields: ctx, reaction, hints
func (_m *LayeredStoreDatabaseLayer) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) (*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, reaction)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.Reaction); ok {
r0 = rf(ctx, reaction, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, reaction, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionDeleteAllWithEmojiName provides a mock function with given fields: ctx, emojiName, hints
func (_m *LayeredStoreDatabaseLayer) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...store.LayeredStoreHint) *model.AppError {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, emojiName)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *model.AppError); ok {
r0 = rf(ctx, emojiName, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// ReactionGetForPost provides a mock function with given fields: ctx, postId, hints
func (_m *LayeredStoreDatabaseLayer) ReactionGetForPost(ctx context.Context, postId string, hints ...store.LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, postId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 []*model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) []*model.Reaction); ok {
r0 = rf(ctx, postId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, string, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, postId, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionPermanentDeleteBatch provides a mock function with given fields: ctx, endTime, limit, hints
func (_m *LayeredStoreDatabaseLayer) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...store.LayeredStoreHint) (int64, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, endTime, limit)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, int64, int64, ...store.LayeredStoreHint) int64); ok {
r0 = rf(ctx, endTime, limit, hints...)
} else {
r0 = ret.Get(0).(int64)
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, int64, int64, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, endTime, limit, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionSave provides a mock function with given fields: ctx, reaction, hints
func (_m *LayeredStoreDatabaseLayer) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) (*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, reaction)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.Reaction); ok {
r0 = rf(ctx, reaction, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, reaction, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionsBulkGetForPosts provides a mock function with given fields: ctx, postIds, hints
func (_m *LayeredStoreDatabaseLayer) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...store.LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, postIds)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 []*model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) []*model.Reaction); ok {
r0 = rf(ctx, postIds, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, []string, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, postIds, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// Role provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore {
ret := _m.Called()

View File

@@ -30,187 +30,6 @@ func (_m *LayeredStoreSupplier) Next() store.LayeredStoreSupplier {
return r0
}
// ReactionDelete provides a mock function with given fields: ctx, reaction, hints
func (_m *LayeredStoreSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) (*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, reaction)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.Reaction); ok {
r0 = rf(ctx, reaction, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, reaction, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionDeleteAllWithEmojiName provides a mock function with given fields: ctx, emojiName, hints
func (_m *LayeredStoreSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...store.LayeredStoreHint) *model.AppError {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, emojiName)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *model.AppError); ok {
r0 = rf(ctx, emojiName, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// ReactionGetForPost provides a mock function with given fields: ctx, postId, hints
func (_m *LayeredStoreSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...store.LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, postId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 []*model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) []*model.Reaction); ok {
r0 = rf(ctx, postId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, string, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, postId, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionPermanentDeleteBatch provides a mock function with given fields: ctx, endTime, limit, hints
func (_m *LayeredStoreSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...store.LayeredStoreHint) (int64, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, endTime, limit)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, int64, int64, ...store.LayeredStoreHint) int64); ok {
r0 = rf(ctx, endTime, limit, hints...)
} else {
r0 = ret.Get(0).(int64)
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, int64, int64, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, endTime, limit, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionSave provides a mock function with given fields: ctx, reaction, hints
func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...store.LayeredStoreHint) (*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, reaction)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.Reaction); ok {
r0 = rf(ctx, reaction, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, *model.Reaction, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, reaction, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// ReactionsBulkGetForPosts provides a mock function with given fields: ctx, postIds, hints
func (_m *LayeredStoreSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...store.LayeredStoreHint) ([]*model.Reaction, *model.AppError) {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, postIds)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 []*model.Reaction
if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) []*model.Reaction); ok {
r0 = rf(ctx, postIds, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Reaction)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(context.Context, []string, ...store.LayeredStoreHint) *model.AppError); ok {
r1 = rf(ctx, postIds, hints...)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
func (_m *LayeredStoreSupplier) RoleDelete(ctx context.Context, roldId string, hints ...store.LayeredStoreHint) (*model.Role, *model.AppError) {
_va := make([]interface{}, len(hints))