diff --git a/store/layered_store.go b/store/layered_store.go index 9d9f3c9a47..6316b2b3e2 100644 --- a/store/layered_store.go +++ b/store/layered_store.go @@ -8,7 +8,6 @@ import ( "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/mlog" - "github.com/mattermost/mattermost-server/model" ) const ( @@ -22,8 +21,6 @@ type LayeredStoreDatabaseLayer interface { type LayeredStore struct { TmpContext context.Context - RoleStore RoleStore - SchemeStore SchemeStore DatabaseLayer LayeredStoreDatabaseLayer LocalCacheLayer *LocalCacheSupplier RedisLayer *RedisSupplier @@ -37,9 +34,6 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn LocalCacheLayer: NewLocalCacheSupplier(metrics, cluster), } - store.RoleStore = &LayeredRoleStore{store} - store.SchemeStore = &LayeredSchemeStore{store} - // Setup the chain if ENABLE_EXPERIMENTAL_REDIS { mlog.Debug("Experimental redis enabled.") @@ -161,7 +155,7 @@ func (s *LayeredStore) Plugin() PluginStore { } func (s *LayeredStore) Role() RoleStore { - return s.RoleStore + return s.DatabaseLayer.Role() } func (s *LayeredStore) TermsOfService() TermsOfServiceStore { @@ -173,7 +167,7 @@ func (s *LayeredStore) UserTermsOfService() UserTermsOfServiceStore { } func (s *LayeredStore) Scheme() SchemeStore { - return s.SchemeStore + return s.DatabaseLayer.Scheme() } func (s *LayeredStore) Group() GroupStore { @@ -220,63 +214,3 @@ func (s *LayeredStore) TotalSearchDbConnections() int { func (s *LayeredStore) CheckIntegrity() <-chan IntegrityCheckResult { return s.DatabaseLayer.CheckIntegrity() } - -type LayeredRoleStore struct { - *LayeredStore -} - -func (s *LayeredRoleStore) Save(role *model.Role) (*model.Role, *model.AppError) { - return s.LayerChainHead.RoleSave(s.TmpContext, role) -} - -func (s *LayeredRoleStore) Get(roleId string) (*model.Role, *model.AppError) { - return s.LayerChainHead.RoleGet(s.TmpContext, roleId) -} - -func (s *LayeredRoleStore) GetAll() ([]*model.Role, *model.AppError) { - return s.LayerChainHead.RoleGetAll(s.TmpContext) -} - -func (s *LayeredRoleStore) GetByName(name string) (*model.Role, *model.AppError) { - return s.LayerChainHead.RoleGetByName(s.TmpContext, name) -} - -func (s *LayeredRoleStore) GetByNames(names []string) ([]*model.Role, *model.AppError) { - return s.LayerChainHead.RoleGetByNames(s.TmpContext, names) -} - -func (s *LayeredRoleStore) Delete(roldId string) (*model.Role, *model.AppError) { - return s.LayerChainHead.RoleDelete(s.TmpContext, roldId) -} - -func (s *LayeredRoleStore) PermanentDeleteAll() *model.AppError { - return s.LayerChainHead.RolePermanentDeleteAll(s.TmpContext) -} - -type LayeredSchemeStore struct { - *LayeredStore -} - -func (s *LayeredSchemeStore) Save(scheme *model.Scheme) (*model.Scheme, *model.AppError) { - return s.LayerChainHead.SchemeSave(s.TmpContext, scheme) -} - -func (s *LayeredSchemeStore) Get(schemeId string) (*model.Scheme, *model.AppError) { - return s.LayerChainHead.SchemeGet(s.TmpContext, schemeId) -} - -func (s *LayeredSchemeStore) GetByName(schemeName string) (*model.Scheme, *model.AppError) { - return s.LayerChainHead.SchemeGetByName(s.TmpContext, schemeName) -} - -func (s *LayeredSchemeStore) Delete(schemeId string) (*model.Scheme, *model.AppError) { - return s.LayerChainHead.SchemeDelete(s.TmpContext, schemeId) -} - -func (s *LayeredSchemeStore) GetAllPage(scope string, offset int, limit int) ([]*model.Scheme, *model.AppError) { - return s.LayerChainHead.SchemeGetAllPage(s.TmpContext, scope, offset, limit) -} - -func (s *LayeredSchemeStore) PermanentDeleteAll() *model.AppError { - return s.LayerChainHead.SchemePermanentDeleteAll(s.TmpContext) -} diff --git a/store/layered_store_supplier.go b/store/layered_store_supplier.go index 1bba88ca0e..0bccad868d 100644 --- a/store/layered_store_supplier.go +++ b/store/layered_store_supplier.go @@ -3,9 +3,6 @@ package store -import "github.com/mattermost/mattermost-server/model" -import "context" - type LayeredStoreSupplierResult struct { StoreResult } @@ -20,21 +17,4 @@ type LayeredStoreSupplier interface { // SetChainNext(LayeredStoreSupplier) Next() LayeredStoreSupplier - - // 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) - RoleGetAll(ctx context.Context, hints ...LayeredStoreHint) ([]*model.Role, *model.AppError) - RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) - RoleGetByNames(ctx context.Context, names []string, hints ...LayeredStoreHint) ([]*model.Role, *model.AppError) - RoleDelete(ctx context.Context, roldId string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) - RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *model.AppError - - // Schemes - SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) - SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) - SchemeGetByName(ctx context.Context, schemeName string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) - SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) - SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) ([]*model.Scheme, *model.AppError) - SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *model.AppError } diff --git a/store/local_cache_supplier.go b/store/local_cache_supplier.go index 4f5667e5a8..6dc971c226 100644 --- a/store/local_cache_supplier.go +++ b/store/local_cache_supplier.go @@ -8,28 +8,16 @@ import ( "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/utils" ) const ( - ROLE_CACHE_SIZE = 20000 - ROLE_CACHE_SEC = 30 * 60 - - SCHEME_CACHE_SIZE = 20000 - SCHEME_CACHE_SEC = 30 * 60 - - GROUP_CACHE_SIZE = 20000 - GROUP_CACHE_SEC = 30 * 60 - CLEAR_CACHE_MESSAGE_DATA = "" ) type LocalCacheSupplier struct { - next LayeredStoreSupplier - roleCache *utils.Cache - schemeCache *utils.Cache - metrics einterfaces.MetricsInterface - cluster einterfaces.ClusterInterface + next LayeredStoreSupplier + metrics einterfaces.MetricsInterface + cluster einterfaces.ClusterInterface } // Caching Interface @@ -46,14 +34,8 @@ type ObjectCache interface { func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) *LocalCacheSupplier { supplier := &LocalCacheSupplier{ - 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_ROLES, supplier.handleClusterInvalidateRole) + metrics: metrics, + cluster: cluster, } return supplier @@ -122,6 +104,4 @@ func (s *LocalCacheSupplier) doClearCacheCluster(cache ObjectCache) { } func (s *LocalCacheSupplier) Invalidate() { - s.doClearCacheCluster(s.roleCache) - s.doClearCacheCluster(s.schemeCache) } diff --git a/store/local_cache_supplier_roles.go b/store/local_cache_supplier_roles.go deleted file mode 100644 index 973ea6d1da..0000000000 --- a/store/local_cache_supplier_roles.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2017-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) handleClusterInvalidateRole(msg *model.ClusterMessage) { - if msg.Data == CLEAR_CACHE_MESSAGE_DATA { - s.roleCache.Purge() - } else { - s.roleCache.Remove(msg.Data) - } -} - -func (s *LocalCacheSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - if len(role.Name) != 0 { - defer s.doInvalidateCacheCluster(s.roleCache, role.Name) - } - return s.Next().RoleSave(ctx, role, hints...) -} - -func (s *LocalCacheSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - // Roles are cached by name, as that is most commonly how they are looked up. - // This means that no caching is supported on roles being looked up by ID. - return s.Next().RoleGet(ctx, roleId, hints...) -} - -func (s *LocalCacheSupplier) RoleGetAll(ctx context.Context, hints ...LayeredStoreHint) ([]*model.Role, *model.AppError) { - // Roles are cached by name, as that is most commonly how they are looked up. - // This means that no caching is supported on roles being listed. - return s.Next().RoleGetAll(ctx, hints...) -} - -func (s *LocalCacheSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - if result := s.doStandardReadCache(ctx, s.roleCache, name, hints...); result != nil { - return result.Data.(*model.Role), nil - } - - role, err := s.Next().RoleGetByName(ctx, name, hints...) - if err != nil { - return nil, err - } - - result := NewSupplierResult() - result.Data = role - s.doStandardAddToCache(ctx, s.roleCache, name, result, hints...) - - return role, nil -} - -func (s *LocalCacheSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) ([]*model.Role, *model.AppError) { - var foundRoles []*model.Role - var rolesToQuery []string - - for _, roleName := range roleNames { - if result := s.doStandardReadCache(ctx, s.roleCache, roleName, hints...); result != nil { - foundRoles = append(foundRoles, result.Data.(*model.Role)) - } else { - rolesToQuery = append(rolesToQuery, roleName) - } - } - - rolesFound, err := s.Next().RoleGetByNames(ctx, rolesToQuery, hints...) - - for _, role := range rolesFound { - res := NewSupplierResult() - res.Data = role - s.doStandardAddToCache(ctx, s.roleCache, role.Name, res, hints...) - } - foundRoles = append(foundRoles, rolesFound...) - - return foundRoles, err -} - -func (s *LocalCacheSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - role, err := s.Next().RoleDelete(ctx, roleId, hints...) - if err != nil { - return nil, err - } - - s.doInvalidateCacheCluster(s.roleCache, role.Name) - - return role, nil -} - -func (s *LocalCacheSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *model.AppError { - defer s.roleCache.Purge() - defer s.doClearCacheCluster(s.roleCache) - - return s.Next().RolePermanentDeleteAll(ctx, hints...) -} diff --git a/store/local_cache_supplier_schemes.go b/store/local_cache_supplier_schemes.go deleted file mode 100644 index b7a9a12607..0000000000 --- a/store/local_cache_supplier_schemes.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2018-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) handleClusterInvalidateScheme(msg *model.ClusterMessage) { - if msg.Data == CLEAR_CACHE_MESSAGE_DATA { - s.schemeCache.Purge() - } else { - s.schemeCache.Remove(msg.Data) - } -} - -func (s *LocalCacheSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - if len(scheme.Id) != 0 { - defer s.doInvalidateCacheCluster(s.schemeCache, scheme.Id) - } - return s.Next().SchemeSave(ctx, scheme, hints...) -} - -func (s *LocalCacheSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - if result := s.doStandardReadCache(ctx, s.schemeCache, schemeId, hints...); result != nil { - return result.Data.(*model.Scheme), nil - } - - scheme, err := s.Next().SchemeGet(ctx, schemeId, hints...) - if err != nil { - return nil, err - } - - result := NewSupplierResult() - result.Data = scheme - s.doStandardAddToCache(ctx, s.schemeCache, schemeId, result, hints...) - - return scheme, nil -} - -func (s *LocalCacheSupplier) SchemeGetByName(ctx context.Context, schemeName string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - return s.Next().SchemeGetByName(ctx, schemeName, hints...) -} - -func (s *LocalCacheSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - defer s.doInvalidateCacheCluster(s.schemeCache, schemeId) - defer s.doClearCacheCluster(s.roleCache) - - return s.Next().SchemeDelete(ctx, schemeId, hints...) -} - -func (s *LocalCacheSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) ([]*model.Scheme, *model.AppError) { - return s.Next().SchemeGetAllPage(ctx, scope, offset, limit, hints...) -} - -func (s *LocalCacheSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *model.AppError { - defer s.doClearCacheCluster(s.schemeCache) - defer s.doClearCacheCluster(s.roleCache) - - return s.Next().SchemePermanentDeleteAll(ctx, hints...) -} diff --git a/store/localcachelayer/layer.go b/store/localcachelayer/layer.go index 9f369e1cd6..b799d5d163 100644 --- a/store/localcachelayer/layer.go +++ b/store/localcachelayer/layer.go @@ -14,6 +14,12 @@ const ( REACTION_CACHE_SIZE = 20000 REACTION_CACHE_SEC = 30 * 60 + ROLE_CACHE_SIZE = 20000 + ROLE_CACHE_SEC = 30 * 60 + + SCHEME_CACHE_SIZE = 20000 + SCHEME_CACHE_SEC = 30 * 60 + CLEAR_CACHE_MESSAGE_DATA = "" ) @@ -23,6 +29,10 @@ type LocalCacheStore struct { cluster einterfaces.ClusterInterface reaction LocalCacheReactionStore reactionCache *utils.Cache + role LocalCacheRoleStore + roleCache *utils.Cache + scheme LocalCacheSchemeStore + schemeCache *utils.Cache } func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) LocalCacheStore { @@ -33,9 +43,15 @@ func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterf } 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} + localCacheStore.roleCache = utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES) + localCacheStore.role = LocalCacheRoleStore{RoleStore: baseStore.Role(), rootStore: &localCacheStore} + localCacheStore.schemeCache = utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES) + localCacheStore.scheme = LocalCacheSchemeStore{SchemeStore: baseStore.Scheme(), rootStore: &localCacheStore} if cluster != nil { cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, localCacheStore.reaction.handleClusterInvalidateReaction) + cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, localCacheStore.role.handleClusterInvalidateRole) + cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES, localCacheStore.scheme.handleClusterInvalidateScheme) } return localCacheStore } @@ -44,6 +60,14 @@ func (s LocalCacheStore) Reaction() store.ReactionStore { return s.reaction } +func (s LocalCacheStore) Role() store.RoleStore { + return s.role +} + +func (s LocalCacheStore) Scheme() store.SchemeStore { + return s.scheme +} + func (s LocalCacheStore) DropAllTables() { s.Invalidate() s.Store.DropAllTables() diff --git a/store/localcachelayer/main_test.go b/store/localcachelayer/main_test.go index cb0466c656..6022826e81 100644 --- a/store/localcachelayer/main_test.go +++ b/store/localcachelayer/main_test.go @@ -6,11 +6,44 @@ package localcachelayer import ( "testing" + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store/storetest/mocks" "github.com/mattermost/mattermost-server/testlib" ) var mainHelper *testlib.MainHelper +func getMockStore() *mocks.Store { + mockStore := mocks.Store{} + + fakeReaction := model.Reaction{PostId: "123"} + 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) + + fakeRole := model.Role{Id: "123", Name: "role-name"} + mockRolesStore := mocks.RoleStore{} + mockRolesStore.On("Save", &fakeRole).Return(&model.Role{}, nil) + mockRolesStore.On("Delete", "123").Return(&fakeRole, nil) + mockRolesStore.On("GetByName", "role-name").Return(&fakeRole, nil) + mockRolesStore.On("GetByNames", []string{"role-name"}).Return([]*model.Role{&fakeRole}, nil) + mockRolesStore.On("PermanentDeleteAll").Return(nil) + mockStore.On("Role").Return(&mockRolesStore) + + fakeScheme := model.Scheme{Id: "123", Name: "scheme-name"} + mockSchemesStore := mocks.SchemeStore{} + mockSchemesStore.On("Save", &fakeScheme).Return(&model.Scheme{}, nil) + mockSchemesStore.On("Delete", "123").Return(&model.Scheme{}, nil) + mockSchemesStore.On("Get", "123").Return(&fakeScheme, nil) + mockSchemesStore.On("PermanentDeleteAll").Return(nil) + mockStore.On("Scheme").Return(&mockSchemesStore) + + return &mockStore +} + func TestMain(m *testing.M) { mainHelper = testlib.NewMainHelperWithOptions(nil) defer mainHelper.Close() diff --git a/store/localcachelayer/reaction_layer_test.go b/store/localcachelayer/reaction_layer_test.go index b449b705dd..4f1437c717 100644 --- a/store/localcachelayer/reaction_layer_test.go +++ b/store/localcachelayer/reaction_layer_test.go @@ -21,72 +21,48 @@ 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) + mockStore := getMockStore() + 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) + mockStore.Reaction().(*mocks.ReactionStore).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) + mockStore.Reaction().(*mocks.ReactionStore).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) + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) cachedStore.Reaction().GetForPost("123", true) - mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1) + mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 1) cachedStore.Reaction().GetForPost("123", false) - mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 2) + mockStore.Reaction().(*mocks.ReactionStore).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) + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) cachedStore.Reaction().GetForPost("123", true) - mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1) + mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 1) cachedStore.Reaction().Save(&fakeReaction) cachedStore.Reaction().GetForPost("123", true) - mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 2) + mockStore.Reaction().(*mocks.ReactionStore).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) + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) cachedStore.Reaction().GetForPost("123", true) - mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 1) + mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 1) cachedStore.Reaction().Delete(&fakeReaction) cachedStore.Reaction().GetForPost("123", true) - mockReactionsStore.AssertNumberOfCalls(t, "GetForPost", 2) + mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 2) }) } diff --git a/store/localcachelayer/role_layer.go b/store/localcachelayer/role_layer.go new file mode 100644 index 0000000000..0cd03c1427 --- /dev/null +++ b/store/localcachelayer/role_layer.go @@ -0,0 +1,80 @@ +// 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 LocalCacheRoleStore struct { + store.RoleStore + rootStore *LocalCacheStore +} + +func (s *LocalCacheRoleStore) handleClusterInvalidateRole(msg *model.ClusterMessage) { + if msg.Data == CLEAR_CACHE_MESSAGE_DATA { + s.rootStore.roleCache.Purge() + } else { + s.rootStore.roleCache.Remove(msg.Data) + } +} + +func (s LocalCacheRoleStore) Save(role *model.Role) (*model.Role, *model.AppError) { + if len(role.Name) != 0 { + defer s.rootStore.doInvalidateCacheCluster(s.rootStore.roleCache, role.Name) + } + return s.RoleStore.Save(role) +} + +func (s LocalCacheRoleStore) GetByName(name string) (*model.Role, *model.AppError) { + if role := s.rootStore.doStandardReadCache(s.rootStore.roleCache, name); role != nil { + return role.(*model.Role), nil + } + + role, err := s.RoleStore.GetByName(name) + if err != nil { + return nil, err + } + s.rootStore.doStandardAddToCache(s.rootStore.roleCache, name, role) + return role, nil +} + +func (s LocalCacheRoleStore) GetByNames(names []string) ([]*model.Role, *model.AppError) { + var foundRoles []*model.Role + var rolesToQuery []string + + for _, roleName := range names { + if role := s.rootStore.doStandardReadCache(s.rootStore.roleCache, roleName); role != nil { + foundRoles = append(foundRoles, role.(*model.Role)) + } else { + rolesToQuery = append(rolesToQuery, roleName) + } + } + + roles, _ := s.RoleStore.GetByNames(rolesToQuery) + + if roles != nil { + for _, role := range roles { + s.rootStore.doStandardAddToCache(s.rootStore.roleCache, role.Name, role) + } + } + return append(foundRoles, roles...), nil +} + +func (s LocalCacheRoleStore) Delete(roleId string) (*model.Role, *model.AppError) { + role, err := s.RoleStore.Delete(roleId) + + if err == nil { + s.rootStore.doInvalidateCacheCluster(s.rootStore.roleCache, role.Name) + } + return role, err +} + +func (s LocalCacheRoleStore) PermanentDeleteAll() *model.AppError { + defer s.rootStore.roleCache.Purge() + defer s.rootStore.doClearCacheCluster(s.rootStore.roleCache) + + return s.RoleStore.PermanentDeleteAll() +} diff --git a/store/localcachelayer/role_layer_test.go b/store/localcachelayer/role_layer_test.go new file mode 100644 index 0000000000..c2e69a9223 --- /dev/null +++ b/store/localcachelayer/role_layer_test.go @@ -0,0 +1,69 @@ +// Copyright (c) 2015-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 TestRoleStore(t *testing.T) { + StoreTest(t, storetest.TestRoleStore) +} + +func TestRoleStoreCache(t *testing.T) { + fakeRole := model.Role{Id: "123", Name: "role-name"} + + t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + role, err := cachedStore.Role().GetByName("role-name") + require.Nil(t, err) + assert.Equal(t, role, &fakeRole) + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) + require.Nil(t, err) + assert.Equal(t, role, &fakeRole) + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) + }) + + t.Run("first call not cached, save, and then not cached again", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) + cachedStore.Role().Save(&fakeRole) + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2) + }) + + t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) + cachedStore.Role().Delete("123") + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2) + }) + + t.Run("first call not cached, permanent delete all, and then not cached again", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) + cachedStore.Role().PermanentDeleteAll() + cachedStore.Role().GetByName("role-name") + mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2) + }) +} diff --git a/store/localcachelayer/scheme_layer.go b/store/localcachelayer/scheme_layer.go new file mode 100644 index 0000000000..cf274e8ad5 --- /dev/null +++ b/store/localcachelayer/scheme_layer.go @@ -0,0 +1,58 @@ +// Copyright (c) 2015-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 LocalCacheSchemeStore struct { + store.SchemeStore + rootStore *LocalCacheStore +} + +func (s *LocalCacheSchemeStore) handleClusterInvalidateScheme(msg *model.ClusterMessage) { + if msg.Data == CLEAR_CACHE_MESSAGE_DATA { + s.rootStore.schemeCache.Purge() + } else { + s.rootStore.schemeCache.Remove(msg.Data) + } +} + +func (s LocalCacheSchemeStore) Save(scheme *model.Scheme) (*model.Scheme, *model.AppError) { + if len(scheme.Id) != 0 { + defer s.rootStore.doInvalidateCacheCluster(s.rootStore.schemeCache, scheme.Id) + } + return s.SchemeStore.Save(scheme) +} + +func (s LocalCacheSchemeStore) Get(schemeId string) (*model.Scheme, *model.AppError) { + if scheme := s.rootStore.doStandardReadCache(s.rootStore.schemeCache, schemeId); scheme != nil { + return scheme.(*model.Scheme), nil + } + + scheme, err := s.SchemeStore.Get(schemeId) + if err != nil { + return nil, err + } + + s.rootStore.doStandardAddToCache(s.rootStore.schemeCache, schemeId, scheme) + + return scheme, nil +} + +func (s LocalCacheSchemeStore) Delete(schemeId string) (*model.Scheme, *model.AppError) { + defer s.rootStore.doInvalidateCacheCluster(s.rootStore.schemeCache, schemeId) + defer s.rootStore.doClearCacheCluster(s.rootStore.roleCache) + + return s.SchemeStore.Delete(schemeId) +} + +func (s LocalCacheSchemeStore) PermanentDeleteAll() *model.AppError { + defer s.rootStore.doClearCacheCluster(s.rootStore.schemeCache) + defer s.rootStore.doClearCacheCluster(s.rootStore.roleCache) + + return s.SchemeStore.PermanentDeleteAll() +} diff --git a/store/localcachelayer/scheme_layer_test.go b/store/localcachelayer/scheme_layer_test.go new file mode 100644 index 0000000000..e592358a8d --- /dev/null +++ b/store/localcachelayer/scheme_layer_test.go @@ -0,0 +1,69 @@ +// Copyright (c) 2015-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 TestSchemeStore(t *testing.T) { + StoreTest(t, storetest.TestSchemeStore) +} + +func TestSchemeStoreCache(t *testing.T) { + fakeScheme := model.Scheme{Id: "123", Name: "scheme-name"} + + t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + scheme, err := cachedStore.Scheme().Get("123") + require.Nil(t, err) + assert.Equal(t, scheme, &fakeScheme) + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) + require.Nil(t, err) + assert.Equal(t, scheme, &fakeScheme) + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) + }) + + t.Run("first call not cached, save, and then not cached again", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) + cachedStore.Scheme().Save(&fakeScheme) + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 2) + }) + + t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) + cachedStore.Scheme().Delete("123") + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 2) + }) + + t.Run("first call not cached, permanent delete all, and then not cached again", func(t *testing.T) { + mockStore := getMockStore() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) + cachedStore.Scheme().PermanentDeleteAll() + cachedStore.Scheme().Get("123") + mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 2) + }) +} diff --git a/store/redis_supplier_roles.go b/store/redis_supplier_roles.go deleted file mode 100644 index 8b7175b86f..0000000000 --- a/store/redis_supplier_roles.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -import ( - "context" - "fmt" - - "github.com/mattermost/mattermost-server/mlog" - "github.com/mattermost/mattermost-server/model" -) - -func (s *RedisSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - key := buildRedisKeyForRoleName(role.Name) - - defer func() { - if err := s.client.Del(key).Err(); err != nil { - mlog.Error("Redis failed to remove key " + key + " Error: " + err.Error()) - } - }() - - return s.Next().RoleSave(ctx, role, hints...) -} - -func (s *RedisSupplier) RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - // Roles are cached by name, as that is most commonly how they are looked up. - // This means that no caching is supported on roles being looked up by ID. - return s.Next().RoleGet(ctx, roleId, hints...) -} - -func (s *RedisSupplier) RoleGetAll(ctx context.Context, hints ...LayeredStoreHint) ([]*model.Role, *model.AppError) { - // Roles are cached by name, as that is most commonly how they are looked up. - // This means that no caching is supported on roles being listed. - return s.Next().RoleGetAll(ctx, hints...) -} - -func (s *RedisSupplier) RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - key := buildRedisKeyForRoleName(name) - - var role *model.Role - found, err := s.load(key, &role) - if err != nil { - mlog.Error("Redis encountered an error on read: " + err.Error()) - } else if found { - return role, nil - } - - role, appErr := s.Next().RoleGetByName(ctx, name, hints...) - - if appErr == nil { - if err := s.save(key, role, REDIS_EXPIRY_TIME); err != nil { - mlog.Error("Redis encountered and error on write: " + err.Error()) - } - } - - return role, appErr -} - -func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string, hints ...LayeredStoreHint) ([]*model.Role, *model.AppError) { - var foundRoles []*model.Role - var rolesToQuery []string - - for _, roleName := range roleNames { - var role *model.Role - found, err := s.load(buildRedisKeyForRoleName(roleName), &role) - if err == nil && found { - foundRoles = append(foundRoles, role) - } else { - rolesToQuery = append(rolesToQuery, roleName) - if err != nil { - mlog.Error("Redis encountered an error on read: " + err.Error()) - } - } - } - - rolesFound, appErr := s.Next().RoleGetByNames(ctx, rolesToQuery, hints...) - - if appErr == nil { - for _, role := range rolesFound { - if err := s.save(buildRedisKeyForRoleName(role.Name), role, REDIS_EXPIRY_TIME); err != nil { - mlog.Error("Redis encountered and error on write: " + err.Error()) - } - } - foundRoles = append(foundRoles, rolesFound...) - } - - return foundRoles, appErr -} - -func (s *RedisSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) (*model.Role, *model.AppError) { - role, appErr := s.Next().RoleGet(ctx, roleId, hints...) - - if appErr == nil { - defer func() { - key := buildRedisKeyForRoleName(role.Name) - - if err := s.client.Del(key).Err(); err != nil { - mlog.Error("Redis failed to remove key " + key + " Error: " + err.Error()) - } - }() - } - - return s.Next().RoleDelete(ctx, roleId, hints...) -} - -func (s *RedisSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *model.AppError { - defer func() { - if keys, err := s.client.Keys("roles:*").Result(); err != nil { - mlog.Error("Redis encountered an error on read: " + err.Error()) - } else { - if err := s.client.Del(keys...).Err(); err != nil { - mlog.Error("Redis encountered an error on delete: " + err.Error()) - } - } - }() - - return s.Next().RolePermanentDeleteAll(ctx, hints...) -} - -func buildRedisKeyForRoleName(roleName string) string { - return fmt.Sprintf("roles:%s", roleName) -} diff --git a/store/redis_supplier_schemes.go b/store/redis_supplier_schemes.go deleted file mode 100644 index 81839197d4..0000000000 --- a/store/redis_supplier_schemes.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -import ( - "context" - - "github.com/mattermost/mattermost-server/model" -) - -func (s *RedisSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - // TODO: Redis caching. - return s.Next().SchemeSave(ctx, scheme, hints...) -} - -func (s *RedisSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - // TODO: Redis caching. - return s.Next().SchemeGet(ctx, schemeId, hints...) -} - -func (s *RedisSupplier) SchemeGetByName(ctx context.Context, schemeName string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - // TODO: Redis caching. - return s.Next().SchemeGetByName(ctx, schemeName, hints...) -} - -func (s *RedisSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) (*model.Scheme, *model.AppError) { - // TODO: Redis caching. - return s.Next().SchemeDelete(ctx, schemeId, hints...) -} - -func (s *RedisSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) ([]*model.Scheme, *model.AppError) { - // TODO: Redis caching. - return s.Next().SchemeGetAllPage(ctx, scope, offset, limit, hints...) -} - -func (s *RedisSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *model.AppError { - // TODO: Redis caching. - return s.Next().SchemePermanentDeleteAll(ctx, hints...) -} diff --git a/store/sqlstore/role_supplier.go b/store/sqlstore/role_supplier.go index e678fb3711..e6fc048726 100644 --- a/store/sqlstore/role_supplier.go +++ b/store/sqlstore/role_supplier.go @@ -4,7 +4,6 @@ package sqlstore import ( - "context" "database/sql" "fmt" "net/http" @@ -15,6 +14,10 @@ import ( "github.com/mattermost/mattermost-server/store" ) +type SqlRoleStore struct { + SqlStore +} + type Role struct { Id string Name string @@ -68,7 +71,9 @@ func (role Role) ToModel() *model.Role { } } -func initSqlSupplierRoles(sqlStore SqlStore) { +func NewSqlRoleStore(sqlStore SqlStore) store.RoleStore { + s := &SqlRoleStore{sqlStore} + for _, db := range sqlStore.GetAllConns() { table := db.AddTableWithName(Role{}, "Roles").SetKeys(false, "Id") table.ColMap("Id").SetMaxSize(26) @@ -77,9 +82,13 @@ func initSqlSupplierRoles(sqlStore SqlStore) { table.ColMap("Description").SetMaxSize(1024) table.ColMap("Permissions").SetMaxSize(4096) } + return s } -func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...store.LayeredStoreHint) (*model.Role, *model.AppError) { +func (s SqlRoleStore) CreateIndexesIfNotExists() { +} + +func (s *SqlRoleStore) Save(role *model.Role) (*model.Role, *model.AppError) { // Check the role is valid before proceeding. if !role.IsValidWithoutId() { return nil, model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.invalid_role.app_error", nil, "", http.StatusBadRequest) @@ -91,7 +100,7 @@ func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...s return nil, model.NewAppError("SqlRoleStore.RoleSave", "store.sql_role.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) } defer finalizeTransaction(transaction) - createdRole, appErr := s.createRole(ctx, role, transaction, hints...) + createdRole, appErr := s.createRole(role, transaction) if appErr != nil { transaction.Rollback() return nil, appErr @@ -112,7 +121,7 @@ func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...s return dbRole.ToModel(), nil } -func (s *SqlSupplier) createRole(ctx context.Context, role *model.Role, transaction *gorp.Transaction, hints ...store.LayeredStoreHint) (*model.Role, *model.AppError) { +func (s *SqlRoleStore) createRole(role *model.Role, transaction *gorp.Transaction) (*model.Role, *model.AppError) { // Check the role is valid before proceeding. if !role.IsValidWithoutId() { return nil, model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.invalid_role.app_error", nil, "", http.StatusBadRequest) @@ -131,7 +140,7 @@ func (s *SqlSupplier) createRole(ctx context.Context, role *model.Role, transact return dbRole.ToModel(), nil } -func (s *SqlSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) (*model.Role, *model.AppError) { +func (s *SqlRoleStore) Get(roleId string) (*model.Role, *model.AppError) { var dbRole Role if err := s.GetReplica().SelectOne(&dbRole, "SELECT * from Roles WHERE Id = :Id", map[string]interface{}{"Id": roleId}); err != nil { @@ -144,7 +153,7 @@ func (s *SqlSupplier) RoleGet(ctx context.Context, roleId string, hints ...store return dbRole.ToModel(), nil } -func (s *SqlSupplier) RoleGetAll(ctx context.Context, hints ...store.LayeredStoreHint) ([]*model.Role, *model.AppError) { +func (s *SqlRoleStore) GetAll() ([]*model.Role, *model.AppError) { var dbRoles []Role if _, err := s.GetReplica().Select(&dbRoles, "SELECT * from Roles", map[string]interface{}{}); err != nil { @@ -161,7 +170,7 @@ func (s *SqlSupplier) RoleGetAll(ctx context.Context, hints ...store.LayeredStor return roles, nil } -func (s *SqlSupplier) RoleGetByName(ctx context.Context, name string, hints ...store.LayeredStoreHint) (*model.Role, *model.AppError) { +func (s *SqlRoleStore) GetByName(name string) (*model.Role, *model.AppError) { var dbRole Role if err := s.GetReplica().SelectOne(&dbRole, "SELECT * from Roles WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil { @@ -174,7 +183,7 @@ func (s *SqlSupplier) RoleGetByName(ctx context.Context, name string, hints ...s return dbRole.ToModel(), nil } -func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints ...store.LayeredStoreHint) ([]*model.Role, *model.AppError) { +func (s *SqlRoleStore) GetByNames(names []string) ([]*model.Role, *model.AppError) { var dbRoles []*Role if len(names) == 0 { @@ -202,7 +211,7 @@ func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints return roles, nil } -func (s *SqlSupplier) RoleDelete(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) (*model.Role, *model.AppError) { +func (s *SqlRoleStore) Delete(roleId string) (*model.Role, *model.AppError) { // Get the role. var role *Role if err := s.GetReplica().SelectOne(&role, "SELECT * from Roles WHERE Id = :Id", map[string]interface{}{"Id": roleId}); err != nil { @@ -224,7 +233,7 @@ func (s *SqlSupplier) RoleDelete(ctx context.Context, roleId string, hints ...st return role.ToModel(), nil } -func (s *SqlSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *model.AppError { +func (s *SqlRoleStore) PermanentDeleteAll() *model.AppError { if _, err := s.GetMaster().Exec("DELETE FROM Roles"); err != nil { return model.NewAppError("SqlRoleStore.PermanentDeleteAll", "store.sql_role.permanent_delete_all.app_error", nil, err.Error(), http.StatusInternalServerError) } diff --git a/store/sqlstore/scheme_supplier.go b/store/sqlstore/scheme_supplier.go index f125f8b27a..f0ec1830c6 100644 --- a/store/sqlstore/scheme_supplier.go +++ b/store/sqlstore/scheme_supplier.go @@ -4,7 +4,6 @@ package sqlstore import ( - "context" "database/sql" "fmt" "net/http" @@ -16,7 +15,13 @@ import ( "github.com/mattermost/mattermost-server/store" ) -func initSqlSupplierSchemes(sqlStore SqlStore) { +type SqlSchemeStore struct { + SqlStore +} + +func NewSqlSchemeStore(sqlStore SqlStore) store.SchemeStore { + s := &SqlSchemeStore{sqlStore} + for _, db := range sqlStore.GetAllConns() { table := db.AddTableWithName(model.Scheme{}, "Schemes").SetKeys(false, "Id") table.ColMap("Id").SetMaxSize(26) @@ -31,9 +36,14 @@ func initSqlSupplierSchemes(sqlStore SqlStore) { table.ColMap("DefaultChannelUserRole").SetMaxSize(64) table.ColMap("DefaultChannelGuestRole").SetMaxSize(64) } + + return s } -func (s *SqlSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) (*model.Scheme, *model.AppError) { +func (s SqlSchemeStore) CreateIndexesIfNotExists() { +} + +func (s *SqlSchemeStore) Save(scheme *model.Scheme) (*model.Scheme, *model.AppError) { if len(scheme.Id) == 0 { transaction, err := s.GetMaster().Begin() if err != nil { @@ -41,7 +51,7 @@ func (s *SqlSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hint } defer finalizeTransaction(transaction) - newScheme, appErr := s.createScheme(ctx, scheme, transaction, hints...) + newScheme, appErr := s.createScheme(scheme, transaction) if appErr != nil { return nil, appErr } @@ -68,11 +78,11 @@ func (s *SqlSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hint return scheme, nil } -func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, transaction *gorp.Transaction, hints ...store.LayeredStoreHint) (*model.Scheme, *model.AppError) { +func (s *SqlSchemeStore) createScheme(scheme *model.Scheme, transaction *gorp.Transaction) (*model.Scheme, *model.AppError) { // Fetch the default system scheme roles to populate default permissions. defaultRoleNames := []string{model.TEAM_ADMIN_ROLE_ID, model.TEAM_USER_ROLE_ID, model.TEAM_GUEST_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID, model.CHANNEL_USER_ROLE_ID, model.CHANNEL_GUEST_ROLE_ID} defaultRoles := make(map[string]*model.Role) - roles, err := s.RoleGetByNames(ctx, defaultRoleNames) + roles, err := s.SqlStore.Role().GetByNames(defaultRoleNames) if err != nil { return nil, err } @@ -108,7 +118,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr SchemeManaged: true, } - savedRole, err := s.createRole(ctx, teamAdminRole, transaction) + savedRole, err := s.SqlStore.Role().(*SqlRoleStore).createRole(teamAdminRole, transaction) if err != nil { return nil, err } @@ -122,7 +132,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr SchemeManaged: true, } - savedRole, err = s.createRole(ctx, teamUserRole, transaction) + savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(teamUserRole, transaction) if err != nil { return nil, err } @@ -136,7 +146,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr SchemeManaged: true, } - savedRole, err = s.createRole(ctx, teamGuestRole, transaction) + savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(teamGuestRole, transaction) if err != nil { return nil, err } @@ -151,7 +161,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr SchemeManaged: true, } - savedRole, err := s.createRole(ctx, channelAdminRole, transaction) + savedRole, err := s.SqlStore.Role().(*SqlRoleStore).createRole(channelAdminRole, transaction) if err != nil { return nil, err } @@ -165,7 +175,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr SchemeManaged: true, } - savedRole, err = s.createRole(ctx, channelUserRole, transaction) + savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(channelUserRole, transaction) if err != nil { return nil, err } @@ -179,7 +189,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr SchemeManaged: true, } - savedRole, err = s.createRole(ctx, channelGuestRole, transaction) + savedRole, err = s.SqlStore.Role().(*SqlRoleStore).createRole(channelGuestRole, transaction) if err != nil { return nil, err } @@ -205,7 +215,7 @@ func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, tr return scheme, nil } -func (s *SqlSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) (*model.Scheme, *model.AppError) { +func (s *SqlSchemeStore) Get(schemeId string) (*model.Scheme, *model.AppError) { var scheme model.Scheme if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Id = :Id", map[string]interface{}{"Id": schemeId}); err != nil { if err == sql.ErrNoRows { @@ -217,7 +227,7 @@ func (s *SqlSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...s return &scheme, nil } -func (s *SqlSupplier) SchemeGetByName(ctx context.Context, schemeName string, hints ...store.LayeredStoreHint) (*model.Scheme, *model.AppError) { +func (s *SqlSchemeStore) GetByName(schemeName string) (*model.Scheme, *model.AppError) { var scheme model.Scheme if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Name = :Name", map[string]interface{}{"Name": schemeName}); err != nil { @@ -230,7 +240,7 @@ func (s *SqlSupplier) SchemeGetByName(ctx context.Context, schemeName string, hi return &scheme, nil } -func (s *SqlSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) (*model.Scheme, *model.AppError) { +func (s *SqlSchemeStore) Delete(schemeId string) (*model.Scheme, *model.AppError) { // Get the scheme var scheme model.Scheme if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Id = :Id", map[string]interface{}{"Id": schemeId}); err != nil { @@ -290,7 +300,7 @@ func (s *SqlSupplier) SchemeDelete(ctx context.Context, schemeId string, hints . return &scheme, nil } -func (s *SqlSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...store.LayeredStoreHint) ([]*model.Scheme, *model.AppError) { +func (s *SqlSchemeStore) GetAllPage(scope string, offset int, limit int) ([]*model.Scheme, *model.AppError) { var schemes []*model.Scheme scopeClause := "" @@ -305,7 +315,7 @@ func (s *SqlSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset return schemes, nil } -func (s *SqlSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *model.AppError { +func (s *SqlSchemeStore) PermanentDeleteAll() *model.AppError { if _, err := s.GetMaster().Exec("DELETE from Schemes"); err != nil { return model.NewAppError("SqlSchemeStore.PermanentDeleteAll", "store.sql_scheme.permanent_delete_all.app_error", nil, err.Error(), http.StatusInternalServerError) } diff --git a/store/sqlstore/supplier.go b/store/sqlstore/supplier.go index d6a5cf9f83..9d04cedf7b 100644 --- a/store/sqlstore/supplier.go +++ b/store/sqlstore/supplier.go @@ -152,11 +152,10 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter supplier.oldStores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier) supplier.oldStores.linkMetadata = NewSqlLinkMetadataStore(supplier) supplier.oldStores.reaction = NewSqlReactionStore(supplier) + supplier.oldStores.role = NewSqlRoleStore(supplier) + supplier.oldStores.scheme = NewSqlSchemeStore(supplier) supplier.oldStores.group = NewSqlGroupStore(supplier) - initSqlSupplierRoles(supplier) - initSqlSupplierSchemes(supplier) - err := supplier.GetMaster().CreateTablesIfNotExists() if err != nil { mlog.Critical("Error creating database tables.", mlog.Err(err)) diff --git a/testlib/cluster.go b/testlib/cluster.go index 244c8e5b74..c8a75e28e4 100644 --- a/testlib/cluster.go +++ b/testlib/cluster.go @@ -48,9 +48,11 @@ func (c *FakeClusterInterface) ConfigChanged(previousConfig *model.Config, newCo } func (c *FakeClusterInterface) SendClearRoleCacheMessage() { - c.clusterMessageHandler(&model.ClusterMessage{ - Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, - }) + if c.clusterMessageHandler != nil { + c.clusterMessageHandler(&model.ClusterMessage{ + Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, + }) + } } func (c *FakeClusterInterface) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {