From 62b57143c8d00d126e0c3a78895edb0fd5e551aa Mon Sep 17 00:00:00 2001 From: Hector Date: Thu, 9 Jan 2020 09:57:28 +0100 Subject: [PATCH] [MM 19840] Create a Cache Interface abstraction to support multiple cache backends (#13384) * Refactor to use structured logging * Properly formatted with gofmt * created interface Cache, but construction of cache is still coupled. * Implementing cache factories to build caches * Simple redis implementation without error handling. Keys and values by default are string * refactor NewLocalCacheLayer to inject cache factory * Removed redis impl to focus on cache abstraction * CacheFactory injected on sqlsupplier and saved in Store struct * remove useless private method * replace concrete declaration of lru cache to cache abstraction * discard spaces * Renamed factory to provider because concrete implementations of factories may hold an state (such as a redis client for example) * refactor to include all caches in the same package cache and subpackages * method close cache. This method will be used for concrete implementations that need to release resources (close a connection, etc) * closing cacheprovider and releasing resources while closing sql store * fixed merge conflict fail * gofmt files * remove unused property from post_store * naming refactor to avoid stutter. Added godocs on interface * Store doesnt know anything about the cache and provider. Cache provider will be built after loading config and injected in localCacheLayer * fixed broken test * cache provider initialized before RunOldAppInitialization which initializes the localcachelayer * move statusCache to server to initialize it with the new cache provider * update terms_service and channel_layer to have new cacheProvider * gofmt * Add Connect method to the cache provider * mock cacheprovider in user_layer_test Co-authored-by: Ben Schumacher Co-authored-by: mattermod --- api4/openGraph.go | 4 +- api4/system.go | 4 +- app/admin.go | 2 +- app/notification.go | 2 +- app/post_metadata.go | 4 +- app/server.go | 33 +++- app/server_app_adapters.go | 2 +- app/status.go | 23 +-- go.mod | 2 - services/cache/cache.go | 60 ++++++++ {utils => services/cache/lru}/lru.go | 37 ++++- {utils => services/cache/lru}/lru_test.go | 8 +- store/localcachelayer/channel_layer_test.go | 63 +++++--- store/localcachelayer/emoji_layer_test.go | 30 ++-- store/localcachelayer/layer.go | 105 +++++++------ store/localcachelayer/layer_test.go | 2 +- store/localcachelayer/main_test.go | 16 ++ store/localcachelayer/post_layer_test.go | 33 ++-- store/localcachelayer/reaction_layer_test.go | 12 +- store/localcachelayer/role_layer_test.go | 12 +- store/localcachelayer/scheme_layer_test.go | 12 +- store/localcachelayer/team_layer_test.go | 9 +- .../terms_of_service_layer_test.go | 27 ++-- store/localcachelayer/user_layer_test.go | 15 +- store/localcachelayer/webhook_layer_test.go | 9 +- store/sqlstore/channel_store.go | 8 +- store/sqlstore/file_info_store.go | 5 +- store/sqlstore/user_store.go | 5 +- store/storetest/mocks/Cache.go | 145 ++++++++++++++++++ store/storetest/mocks/CacheProvider.go | 57 +++++++ store/storetest/store.go | 3 +- 31 files changed, 572 insertions(+), 177 deletions(-) create mode 100644 services/cache/cache.go rename {utils => services/cache/lru}/lru.go (81%) rename {utils => services/cache/lru}/lru_test.go (97%) create mode 100644 store/storetest/mocks/Cache.go create mode 100644 store/storetest/mocks/CacheProvider.go diff --git a/api4/openGraph.go b/api4/openGraph.go index 97d66114a0..95e4d484d0 100644 --- a/api4/openGraph.go +++ b/api4/openGraph.go @@ -7,12 +7,12 @@ import ( "net/http" "github.com/mattermost/mattermost-server/v5/model" - "github.com/mattermost/mattermost-server/v5/utils" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" ) const OPEN_GRAPH_METADATA_CACHE_SIZE = 10000 -var openGraphDataCache = utils.NewLru(OPEN_GRAPH_METADATA_CACHE_SIZE) +var openGraphDataCache = lru.New(OPEN_GRAPH_METADATA_CACHE_SIZE) func (api *API) InitOpenGraph() { api.BaseRoutes.OpenGraph.Handle("", api.ApiSessionRequired(getOpenGraphMetadata)).Methods("POST") diff --git a/api4/system.go b/api4/system.go index a8fb5db077..86b3f8b3d0 100644 --- a/api4/system.go +++ b/api4/system.go @@ -13,8 +13,8 @@ import ( "github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/services/filesstore" - "github.com/mattermost/mattermost-server/v5/utils" ) const ( @@ -23,7 +23,7 @@ const ( MAX_SERVER_BUSY_SECONDS = 86400 ) -var redirectLocationDataCache = utils.NewLru(REDIRECT_LOCATION_CACHE_SIZE) +var redirectLocationDataCache = lru.New(REDIRECT_LOCATION_CACHE_SIZE) func (api *API) InitSystem() { api.BaseRoutes.System.Handle("/ping", api.ApiHandler(getSystemPing)).Methods("GET") diff --git a/app/admin.go b/app/admin.go index fa71a7180b..fa6a75893e 100644 --- a/app/admin.go +++ b/app/admin.go @@ -150,7 +150,7 @@ func (a *App) InvalidateAllCaches() *model.AppError { func (a *App) InvalidateAllCachesSkipSend() { mlog.Info("Purging all caches") a.Srv.sessionCache.Purge() - ClearStatusCache() + a.Srv.statusCache.Purge() a.Srv.Store.Team().ClearCaches() a.Srv.Store.Channel().ClearCaches() a.Srv.Store.User().ClearCaches() diff --git a/app/notification.go b/app/notification.go index 925b9c239e..0f69282700 100644 --- a/app/notification.go +++ b/app/notification.go @@ -645,7 +645,7 @@ func (a *App) getMentionKeywordsInChannel(profiles map[string]*model.User, allow keywords, profile, channelMemberNotifyPropsMap[profile.Id], - GetStatusFromCache(profile.Id), + a.GetStatusFromCache(profile.Id), allowChannelMentions, ) } diff --git a/app/post_metadata.go b/app/post_metadata.go index a48457f096..561e2885f1 100644 --- a/app/post_metadata.go +++ b/app/post_metadata.go @@ -15,7 +15,7 @@ import ( "github.com/dyatlov/go-opengraph/opengraph" "github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/model" - "github.com/mattermost/mattermost-server/v5/utils" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/utils/imgutils" "github.com/mattermost/mattermost-server/v5/utils/markdown" ) @@ -24,7 +24,7 @@ const LINK_CACHE_SIZE = 10000 const LINK_CACHE_DURATION = 3600 const MaxMetadataImageSize = MaxOpenGraphResponseSize -var linkCache = utils.NewLru(LINK_CACHE_SIZE) +var linkCache = lru.New(LINK_CACHE_SIZE) func (a *App) InitPostMetadata() { // Dump any cached links if the proxy settings have changed so image URLs can be updated diff --git a/app/server.go b/app/server.go index 815ce27830..8d67239c78 100644 --- a/app/server.go +++ b/app/server.go @@ -30,6 +30,8 @@ import ( "github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/plugin" + "github.com/mattermost/mattermost-server/v5/services/cache" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/services/httpservice" "github.com/mattermost/mattermost-server/v5/services/imageproxy" "github.com/mattermost/mattermost-server/v5/services/timezones" @@ -86,8 +88,9 @@ type Server struct { newStore func() store.Store htmlTemplateWatcher *utils.HTMLTemplateWatcher - sessionCache *utils.Cache - seenPendingPostIdsCache *utils.Cache + sessionCache cache.Cache + seenPendingPostIdsCache cache.Cache + statusCache cache.Cache configListenerId string licenseListenerId string logListenerId string @@ -129,18 +132,18 @@ type Server struct { Metrics einterfaces.MetricsInterface Notification einterfaces.NotificationInterface Saml einterfaces.SamlInterface + + CacheProvider cache.Provider } func NewServer(options ...Option) (*Server, error) { rootRouter := mux.NewRouter() s := &Server{ - goroutineExitSignal: make(chan struct{}, 1), - RootRouter: rootRouter, - licenseListeners: map[string]func(){}, - sessionCache: utils.NewLru(model.SESSION_CACHE_SIZE), - seenPendingPostIdsCache: utils.NewLru(PENDING_POST_IDS_CACHE_SIZE), - clientConfig: make(map[string]string), + goroutineExitSignal: make(chan struct{}, 1), + RootRouter: rootRouter, + licenseListeners: map[string]func(){}, + clientConfig: make(map[string]string), } for _, option := range options { if err := option(s); err != nil { @@ -188,6 +191,16 @@ func NewServer(options ...Option) (*Server, error) { return nil, errors.Wrapf(err, "unable to load Mattermost translation files") } + // at the moment we only have this implementation + // in the future the cache provider will be built based on the loaded config + s.CacheProvider = new(lru.CacheProvider) + + s.CacheProvider.Connect() + + s.sessionCache = s.CacheProvider.NewCache(model.SESSION_CACHE_SIZE) + s.seenPendingPostIdsCache = s.CacheProvider.NewCache(PENDING_POST_IDS_CACHE_SIZE) + s.statusCache = s.CacheProvider.NewCache(model.STATUS_CACHE_SIZE) + err := s.RunOldAppInitialization() if err != nil { return nil, err @@ -394,6 +407,10 @@ func (s *Server) Shutdown() error { s.Store.Close() } + if s.CacheProvider != nil { + s.CacheProvider.Close() + } + mlog.Info("Server stopped") return nil } diff --git a/app/server_app_adapters.go b/app/server_app_adapters.go index 32db15e7f1..6e29d6fc1c 100644 --- a/app/server_app_adapters.go +++ b/app/server_app_adapters.go @@ -64,7 +64,7 @@ func (s *Server) RunOldAppInitialization() error { return store.NewTimerLayer( localcachelayer.NewLocalCacheLayer( sqlstore.NewSqlSupplier(s.FakeApp().Config().SqlSettings, s.Metrics), - s.Metrics, s.Cluster), + s.Metrics, s.Cluster, s.CacheProvider), s.Metrics) } } diff --git a/app/status.go b/app/status.go index 8553b64767..545f80e8dd 100644 --- a/app/status.go +++ b/app/status.go @@ -6,17 +6,10 @@ package app import ( "github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/model" - "github.com/mattermost/mattermost-server/v5/utils" ) -var statusCache *utils.Cache = utils.NewLru(model.STATUS_CACHE_SIZE) - -func ClearStatusCache() { - statusCache.Purge() -} - func (a *App) AddStatusCacheSkipClusterSend(status *model.Status) { - statusCache.Add(status.UserId, status) + a.Srv.statusCache.Add(status.UserId, status) } func (a *App) AddStatusCache(status *model.Status) { @@ -37,12 +30,12 @@ func (a *App) GetAllStatuses() map[string]*model.Status { return map[string]*model.Status{} } - userIds := statusCache.Keys() + userIds := a.Srv.statusCache.Keys() statusMap := map[string]*model.Status{} for _, userId := range userIds { if id, ok := userId.(string); ok { - status := GetStatusFromCache(id) + status := a.GetStatusFromCache(id) if status != nil { statusMap[id] = status } @@ -62,7 +55,7 @@ func (a *App) GetStatusesByIds(userIds []string) (map[string]interface{}, *model missingUserIds := []string{} for _, userId := range userIds { - if result, ok := statusCache.Get(userId); ok { + if result, ok := a.Srv.statusCache.Get(userId); ok { statusMap[userId] = result.(*model.Status).Status if metrics != nil { metrics.IncrementMemCacheHitCounter("Status") @@ -109,7 +102,7 @@ func (a *App) GetUserStatusesByIds(userIds []string) ([]*model.Status, *model.Ap missingUserIds := []string{} for _, userId := range userIds { - if result, ok := statusCache.Get(userId); ok { + if result, ok := a.Srv.statusCache.Get(userId); ok { statusMap = append(statusMap, result.(*model.Status)) if metrics != nil { metrics.IncrementMemCacheHitCounter("Status") @@ -329,8 +322,8 @@ func (a *App) SetStatusOutOfOffice(userId string) { a.SaveAndBroadcastStatus(status) } -func GetStatusFromCache(userId string) *model.Status { - if result, ok := statusCache.Get(userId); ok { +func (a *App) GetStatusFromCache(userId string) *model.Status { + if result, ok := a.Srv.statusCache.Get(userId); ok { status := result.(*model.Status) statusCopy := &model.Status{} *statusCopy = *status @@ -345,7 +338,7 @@ func (a *App) GetStatus(userId string) (*model.Status, *model.AppError) { return &model.Status{}, nil } - status := GetStatusFromCache(userId) + status := a.GetStatusFromCache(userId) if status != nil { return status, nil } diff --git a/go.mod b/go.mod index 9b974ef4ce..916e8049b7 100644 --- a/go.mod +++ b/go.mod @@ -58,8 +58,6 @@ require ( github.com/muesli/smartcrop v0.3.0 // indirect github.com/olekukonko/tablewriter v0.0.1 // indirect github.com/olivere/elastic v6.2.23+incompatible // indirect - github.com/onsi/ginkgo v1.8.0 // indirect - github.com/onsi/gomega v1.5.0 // indirect github.com/pborman/uuid v1.2.0 github.com/pelletier/go-toml v1.4.0 // indirect github.com/pkg/errors v0.8.1 diff --git a/services/cache/cache.go b/services/cache/cache.go new file mode 100644 index 0000000000..b0f7335b98 --- /dev/null +++ b/services/cache/cache.go @@ -0,0 +1,60 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package cache + +import ( + "time" +) + +// Cache is a representation of any cache store that has keys and values +type Cache interface { + // Purge is used to completely clear the cache. + Purge() + + // Add adds the given key and value to the store without an expiry. + Add(key, value interface{}) + + // AddWithDefaultExpires adds the given key and value to the store with the default expiry. + AddWithDefaultExpires(key, value interface{}) + + // AddWithExpiresInSecs adds the given key and value to the cache with the given expiry. + AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) + + // Get returns the value stored in the cache for a key, or nil if no value is present. The ok result indicates whether value was found in the cache. + Get(key interface{}) (value interface{}, ok bool) + + // GetOrAdd returns the existing value for the key if present. Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored. + // This API intentionally deviates from the Add-only variants above for simplicity. We should simplify the entire API in the future. + GetOrAdd(key, value interface{}, ttl time.Duration) (actual interface{}, loaded bool) + + // Remove deletes the value for a key. + Remove(key interface{}) + + // Keys returns a slice of the keys in the cache. + Keys() []interface{} + + // Len returns the number of items in the cache. + Len() int + + // Name identifies this cache instance among others in the system. + Name() string + + // GetInvalidateClusterEvent returns the cluster event configured when this cache was created. + GetInvalidateClusterEvent() string +} + +// Provider defines how to create new caches +type Provider interface { + // Connect opens a new connection to the cache using specific provider parameters. + Connect() + + // NewCache creates a new cache with given size. + NewCache(size int) Cache + + // NewCacheWithParams creates a new cache with the given parameters. + NewCacheWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) Cache + + // Close releases any resources used by the cache provider. + Close() +} diff --git a/utils/lru.go b/services/cache/lru/lru.go similarity index 81% rename from utils/lru.go rename to services/cache/lru/lru.go index 7a5027b192..0292a2a9ae 100644 --- a/utils/lru.go +++ b/services/cache/lru/lru.go @@ -8,12 +8,14 @@ // LRU implementation in groupcache: // https://github.com/golang/groupcache/tree/master/lru -package utils +package lru import ( "container/list" "sync" "time" + + "github.com/mattermost/mattermost-server/v5/services/cache" ) // Cache is a thread-safe fixed size LRU cache. @@ -29,6 +31,29 @@ type Cache struct { len int } +// CacheProvider is an implementation of cache.Provider to create a new Lru Cache +type CacheProvider struct{} + +// NewCache creates a new lru.Cache with given size. +func (c *CacheProvider) NewCache(size int) cache.Cache { + return New(size) +} + +// NewCacheWithParams creates a new lru.Cache with the given parameters. +func (c *CacheProvider) NewCacheWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) cache.Cache { + return NewWithParams(size, name, defaultExpiry, invalidateClusterEvent) +} + +// Connect opens a new connection to the cache using specific provider parameters. +func (c *CacheProvider) Connect() { + +} + +// Close releases any resources used by the cache provider. +func (c *CacheProvider) Close() { + +} + // entry is used to hold a value in the evictList. type entry struct { key interface{} @@ -38,7 +63,7 @@ type entry struct { } // New creates an LRU of the given size. -func NewLru(size int) *Cache { +func New(size int) *Cache { return &Cache{ size: size, evictList: list.New(), @@ -46,9 +71,9 @@ func NewLru(size int) *Cache { } } -// New creates an LRU with the given parameters. -func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { - lru := NewLru(size) +// NewWithParams creates an LRU with the given parameters. +func NewWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { + lru := New(size) lru.name = name lru.defaultExpiry = defaultExpiry lru.invalidateClusterEvent = invalidateClusterEvent @@ -69,7 +94,7 @@ func (c *Cache) Add(key, value interface{}) { c.AddWithExpiresInSecs(key, value, 0) } -// Add adds the given key and value to the store with the default expiry. +// AddWithDefaultExpires adds the given key and value to the store with the default expiry. func (c *Cache) AddWithDefaultExpires(key, value interface{}) { c.AddWithExpiresInSecs(key, value, c.defaultExpiry) } diff --git a/utils/lru_test.go b/services/cache/lru/lru_test.go similarity index 97% rename from utils/lru_test.go rename to services/cache/lru/lru_test.go index 578d41400d..3a995decd4 100644 --- a/utils/lru_test.go +++ b/services/cache/lru/lru_test.go @@ -8,7 +8,7 @@ // LRU implementation in groupcache: // https://github.com/golang/groupcache/tree/master/lru -package utils +package lru import ( "testing" @@ -19,7 +19,7 @@ import ( ) func TestLRU(t *testing.T) { - l := NewLru(128) + l := New(128) for i := 0; i < 256; i++ { l.Add(i, i) @@ -60,7 +60,7 @@ func TestLRU(t *testing.T) { } func TestLRUExpire(t *testing.T) { - l := NewLru(128) + l := New(128) l.AddWithExpiresInSecs(1, 1, 1) l.AddWithExpiresInSecs(2, 2, 1) @@ -76,7 +76,7 @@ func TestLRUExpire(t *testing.T) { } func TestLRUGetOrAdd(t *testing.T) { - l := NewLru(128) + l := New(128) // First GetOrAdd should save value, loaded := l.GetOrAdd(1, 1, 0) diff --git a/store/localcachelayer/channel_layer_test.go b/store/localcachelayer/channel_layer_test.go index d2b74bc96a..ef3046d2ac 100644 --- a/store/localcachelayer/channel_layer_test.go +++ b/store/localcachelayer/channel_layer_test.go @@ -23,7 +23,8 @@ func TestChannelStoreChannelMemberCountsCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) count, err := cachedStore.Channel().GetMemberCount("id", true) require.Nil(t, err) @@ -37,7 +38,8 @@ func TestChannelStoreChannelMemberCountsCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) @@ -47,7 +49,8 @@ func TestChannelStoreChannelMemberCountsCache(t *testing.T) { t.Run("first call force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetMemberCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) @@ -59,7 +62,8 @@ func TestChannelStoreChannelMemberCountsCache(t *testing.T) { t.Run("first call with GetMemberCountFromCache not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) count := cachedStore.Channel().GetMemberCountFromCache("id") assert.Equal(t, count, countResult) @@ -71,7 +75,8 @@ func TestChannelStoreChannelMemberCountsCache(t *testing.T) { t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) @@ -82,7 +87,8 @@ func TestChannelStoreChannelMemberCountsCache(t *testing.T) { t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) @@ -97,7 +103,8 @@ func TestChannelStoreChannelPinnedPostsCountsCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) count, err := cachedStore.Channel().GetPinnedPostCount("id", true) require.Nil(t, err) @@ -111,7 +118,8 @@ func TestChannelStoreChannelPinnedPostsCountsCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) @@ -121,7 +129,8 @@ func TestChannelStoreChannelPinnedPostsCountsCache(t *testing.T) { t.Run("first call force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetPinnedPostCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) @@ -133,7 +142,8 @@ func TestChannelStoreChannelPinnedPostsCountsCache(t *testing.T) { t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) @@ -144,7 +154,8 @@ func TestChannelStoreChannelPinnedPostsCountsCache(t *testing.T) { t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) @@ -159,7 +170,8 @@ func TestChannelStoreGuestCountCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) count, err := cachedStore.Channel().GetGuestCount("id", true) require.Nil(t, err) @@ -173,7 +185,8 @@ func TestChannelStoreGuestCountCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) @@ -183,7 +196,8 @@ func TestChannelStoreGuestCountCache(t *testing.T) { t.Run("first call force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetGuestCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) @@ -195,7 +209,8 @@ func TestChannelStoreGuestCountCache(t *testing.T) { t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) @@ -206,7 +221,8 @@ func TestChannelStoreGuestCountCache(t *testing.T) { t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) @@ -221,7 +237,8 @@ func TestChannelStoreChannel(t *testing.T) { fakeChannel := model.Channel{Id: channelId} t.Run("first call by id not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) channel, err := cachedStore.Channel().Get(channelId, true) require.Nil(t, err) @@ -235,7 +252,8 @@ func TestChannelStoreChannel(t *testing.T) { t.Run("first call not cached, second force no cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) @@ -245,7 +263,8 @@ func TestChannelStoreChannel(t *testing.T) { t.Run("first call force no cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().Get(channelId, false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) cachedStore.Channel().Get(channelId, true) @@ -256,7 +275,8 @@ func TestChannelStoreChannel(t *testing.T) { t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) @@ -267,7 +287,8 @@ func TestChannelStoreChannel(t *testing.T) { t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) cachedStore.Channel().InvalidateChannel(channelId) diff --git a/store/localcachelayer/emoji_layer_test.go b/store/localcachelayer/emoji_layer_test.go index be7044367e..5f591093d4 100644 --- a/store/localcachelayer/emoji_layer_test.go +++ b/store/localcachelayer/emoji_layer_test.go @@ -22,7 +22,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by id not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) emoji, err := cachedStore.Emoji().Get("123", true) require.Nil(t, err) @@ -36,7 +37,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by name not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) emoji, err := cachedStore.Emoji().GetByName("name123", true) require.Nil(t, err) @@ -50,7 +52,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by id not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().Get("123", true) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) @@ -60,7 +63,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by name not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().GetByName("name123", true) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) @@ -70,7 +74,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by id force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().Get("123", false) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) @@ -82,7 +87,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by name force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().GetByName("name123", false) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) @@ -94,7 +100,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by id, second call by name cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().Get("123", true) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) @@ -104,7 +111,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by name, second call by id cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().GetByName("name123", true) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) @@ -114,7 +122,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by id not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().Get("123", true) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "Get", 1) @@ -125,7 +134,8 @@ func TestEmojiStoreCache(t *testing.T) { t.Run("first call by name not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Emoji().GetByName("name123", true) mockStore.Emoji().(*mocks.EmojiStore).AssertNumberOfCalls(t, "GetByName", 1) diff --git a/store/localcachelayer/layer.go b/store/localcachelayer/layer.go index 7d8e81fe9e..f3490efb9a 100644 --- a/store/localcachelayer/layer.go +++ b/store/localcachelayer/layer.go @@ -6,8 +6,8 @@ package localcachelayer import ( "github.com/mattermost/mattermost-server/v5/einterfaces" "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/cache" "github.com/mattermost/mattermost-server/v5/store" - "github.com/mattermost/mattermost-server/v5/utils" ) const ( @@ -56,65 +56,74 @@ const ( type LocalCacheStore struct { store.Store - metrics einterfaces.MetricsInterface - cluster einterfaces.ClusterInterface - reaction LocalCacheReactionStore - reactionCache *utils.Cache - role LocalCacheRoleStore - roleCache *utils.Cache - scheme LocalCacheSchemeStore - schemeCache *utils.Cache - emoji LocalCacheEmojiStore - emojiCacheById *utils.Cache - emojiIdCacheByName *utils.Cache - channel LocalCacheChannelStore - channelMemberCountsCache *utils.Cache - channelGuestCountCache *utils.Cache - channelPinnedPostCountsCache *utils.Cache - channelByIdCache *utils.Cache - webhook LocalCacheWebhookStore - webhookCache *utils.Cache - post LocalCachePostStore - postLastPostsCache *utils.Cache - lastPostTimeCache *utils.Cache - user LocalCacheUserStore - userProfileByIdsCache *utils.Cache - team LocalCacheTeamStore - teamAllTeamIdsForUserCache *utils.Cache - termsOfService LocalCacheTermsOfServiceStore - termsOfServiceCache *utils.Cache + metrics einterfaces.MetricsInterface + cluster einterfaces.ClusterInterface + reaction LocalCacheReactionStore + reactionCache cache.Cache + role LocalCacheRoleStore + roleCache cache.Cache + scheme LocalCacheSchemeStore + schemeCache cache.Cache + emoji LocalCacheEmojiStore + emojiCacheById cache.Cache + emojiIdCacheByName cache.Cache + channel LocalCacheChannelStore + + channelMemberCountsCache cache.Cache + channelGuestCountCache cache.Cache + channelPinnedPostCountsCache cache.Cache + channelByIdCache cache.Cache + + webhook LocalCacheWebhookStore + webhookCache cache.Cache + post LocalCachePostStore + postLastPostsCache cache.Cache + lastPostTimeCache cache.Cache + user LocalCacheUserStore + userProfileByIdsCache cache.Cache + team LocalCacheTeamStore + teamAllTeamIdsForUserCache cache.Cache + termsOfService LocalCacheTermsOfServiceStore + termsOfServiceCache cache.Cache } -func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) LocalCacheStore { +func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface, cacheProvider cache.Provider) 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.reactionCache = cacheProvider.NewCacheWithParams(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.roleCache = cacheProvider.NewCacheWithParams(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.schemeCache = cacheProvider.NewCacheWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES) localCacheStore.scheme = LocalCacheSchemeStore{SchemeStore: baseStore.Scheme(), rootStore: &localCacheStore} - localCacheStore.webhookCache = utils.NewLruWithParams(WEBHOOK_CACHE_SIZE, "Webhook", WEBHOOK_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOKS) + localCacheStore.webhookCache = cacheProvider.NewCacheWithParams(WEBHOOK_CACHE_SIZE, "Webhook", WEBHOOK_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOKS) localCacheStore.webhook = LocalCacheWebhookStore{WebhookStore: baseStore.Webhook(), rootStore: &localCacheStore} - localCacheStore.emojiCacheById = utils.NewLruWithParams(EMOJI_CACHE_SIZE, "EmojiById", EMOJI_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_BY_ID) - localCacheStore.emojiIdCacheByName = utils.NewLruWithParams(EMOJI_CACHE_SIZE, "EmojiByName", EMOJI_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_ID_BY_NAME) + localCacheStore.emojiCacheById = cacheProvider.NewCacheWithParams(EMOJI_CACHE_SIZE, "EmojiById", EMOJI_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_BY_ID) + localCacheStore.emojiIdCacheByName = cacheProvider.NewCacheWithParams(EMOJI_CACHE_SIZE, "EmojiByName", EMOJI_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_ID_BY_NAME) localCacheStore.emoji = LocalCacheEmojiStore{EmojiStore: baseStore.Emoji(), rootStore: &localCacheStore} - localCacheStore.channelPinnedPostCountsCache = utils.NewLruWithParams(CHANNEL_PINNEDPOSTS_COUNTS_CACHE_SIZE, "ChannelPinnedPostsCounts", CHANNEL_PINNEDPOSTS_COUNTS_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_PINNEDPOSTS_COUNTS) - localCacheStore.channelMemberCountsCache = utils.NewLruWithParams(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE, "ChannelMemberCounts", CHANNEL_MEMBERS_COUNTS_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBER_COUNTS) - localCacheStore.channelGuestCountCache = utils.NewLruWithParams(CHANNEL_GUEST_COUNT_CACHE_SIZE, "ChannelGuestsCount", CHANNEL_GUEST_COUNT_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_GUEST_COUNT) - localCacheStore.channelByIdCache = utils.NewLruWithParams(model.CHANNEL_CACHE_SIZE, "channelById", CHANNEL_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL) + + localCacheStore.channelPinnedPostCountsCache = cacheProvider.NewCacheWithParams(CHANNEL_PINNEDPOSTS_COUNTS_CACHE_SIZE, "ChannelPinnedPostsCounts", CHANNEL_PINNEDPOSTS_COUNTS_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_PINNEDPOSTS_COUNTS) + localCacheStore.channelMemberCountsCache = cacheProvider.NewCacheWithParams(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE, "ChannelMemberCounts", CHANNEL_MEMBERS_COUNTS_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBER_COUNTS) + localCacheStore.channelGuestCountCache = cacheProvider.NewCacheWithParams(CHANNEL_GUEST_COUNT_CACHE_SIZE, "ChannelGuestsCount", CHANNEL_GUEST_COUNT_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_GUEST_COUNT) + localCacheStore.channelByIdCache = cacheProvider.NewCacheWithParams(model.CHANNEL_CACHE_SIZE, "channelById", CHANNEL_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL) + localCacheStore.channel = LocalCacheChannelStore{ChannelStore: baseStore.Channel(), rootStore: &localCacheStore} - localCacheStore.lastPostTimeCache = utils.NewLruWithParams(LAST_POST_TIME_CACHE_SIZE, "LastPostTime", LAST_POST_TIME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POST_TIME) - localCacheStore.postLastPostsCache = utils.NewLruWithParams(LAST_POSTS_CACHE_SIZE, "LastPost", LAST_POSTS_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POSTS) + + localCacheStore.postLastPostsCache = cacheProvider.NewCacheWithParams(LAST_POSTS_CACHE_SIZE, "LastPost", LAST_POSTS_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POSTS) + localCacheStore.lastPostTimeCache = cacheProvider.NewCacheWithParams(LAST_POST_TIME_CACHE_SIZE, "LastPostTime", LAST_POST_TIME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POST_TIME) + localCacheStore.post = LocalCachePostStore{PostStore: baseStore.Post(), rootStore: &localCacheStore} - localCacheStore.termsOfServiceCache = utils.NewLruWithParams(TERMS_OF_SERVICE_CACHE_SIZE, "TermsOfService", TERMS_OF_SERVICE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE) + + localCacheStore.termsOfServiceCache = cacheProvider.NewCacheWithParams(TERMS_OF_SERVICE_CACHE_SIZE, "TermsOfService", TERMS_OF_SERVICE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE) localCacheStore.termsOfService = LocalCacheTermsOfServiceStore{TermsOfServiceStore: baseStore.TermsOfService(), rootStore: &localCacheStore} - localCacheStore.userProfileByIdsCache = utils.NewLruWithParams(USER_PROFILE_BY_ID_CACHE_SIZE, "UserProfileByIds", USER_PROFILE_BY_ID_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_BY_IDS) + localCacheStore.userProfileByIdsCache = cacheProvider.NewCacheWithParams(USER_PROFILE_BY_ID_CACHE_SIZE, "UserProfileByIds", USER_PROFILE_BY_ID_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_BY_IDS) + localCacheStore.user = LocalCacheUserStore{UserStore: baseStore.User(), rootStore: &localCacheStore} - localCacheStore.teamAllTeamIdsForUserCache = utils.NewLruWithParams(TEAM_CACHE_SIZE, "Team", TEAM_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TEAMS) + localCacheStore.teamAllTeamIdsForUserCache = cacheProvider.NewCacheWithParams(TEAM_CACHE_SIZE, "Team", TEAM_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TEAMS) localCacheStore.team = LocalCacheTeamStore{TeamStore: baseStore.Team(), rootStore: &localCacheStore} if cluster != nil { @@ -182,7 +191,7 @@ func (s LocalCacheStore) DropAllTables() { s.Store.DropAllTables() } -func (s *LocalCacheStore) doInvalidateCacheCluster(cache *utils.Cache, key string) { +func (s *LocalCacheStore) doInvalidateCacheCluster(cache cache.Cache, key string) { cache.Remove(key) if s.cluster != nil { msg := &model.ClusterMessage{ @@ -194,11 +203,11 @@ func (s *LocalCacheStore) doInvalidateCacheCluster(cache *utils.Cache, key strin } } -func (s *LocalCacheStore) doStandardAddToCache(cache *utils.Cache, key string, value interface{}) { +func (s *LocalCacheStore) doStandardAddToCache(cache cache.Cache, key string, value interface{}) { cache.AddWithDefaultExpires(key, value) } -func (s *LocalCacheStore) doStandardReadCache(cache *utils.Cache, key string) interface{} { +func (s *LocalCacheStore) doStandardReadCache(cache cache.Cache, key string) interface{} { if cacheItem, ok := cache.Get(key); ok { if s.metrics != nil { s.metrics.IncrementMemCacheHitCounter(cache.Name()) @@ -213,7 +222,7 @@ func (s *LocalCacheStore) doStandardReadCache(cache *utils.Cache, key string) in return nil } -func (s *LocalCacheStore) doClearCacheCluster(cache *utils.Cache) { +func (s *LocalCacheStore) doClearCacheCluster(cache cache.Cache) { cache.Purge() if s.cluster != nil { msg := &model.ClusterMessage{ diff --git a/store/localcachelayer/layer_test.go b/store/localcachelayer/layer_test.go index a6c749f719..96278ff861 100644 --- a/store/localcachelayer/layer_test.go +++ b/store/localcachelayer/layer_test.go @@ -71,7 +71,7 @@ func initStores() { go func() { defer wg.Done() st.SqlSupplier = sqlstore.NewSqlSupplier(*st.SqlSettings, nil) - st.Store = NewLocalCacheLayer(st.SqlSupplier, nil, nil) + st.Store = NewLocalCacheLayer(st.SqlSupplier, nil, nil, getMockCacheProvider()) st.Store.DropAllTables() st.Store.MarkSystemRanUnitTests() }() diff --git a/store/localcachelayer/main_test.go b/store/localcachelayer/main_test.go index e306fe50a1..4fb61851c5 100644 --- a/store/localcachelayer/main_test.go +++ b/store/localcachelayer/main_test.go @@ -8,13 +8,29 @@ import ( "testing" "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/store" "github.com/mattermost/mattermost-server/v5/store/storetest/mocks" "github.com/mattermost/mattermost-server/v5/testlib" + "github.com/stretchr/testify/mock" ) var mainHelper *testlib.MainHelper +func getMockCacheProvider() *mocks.CacheProvider { + mockCacheProvider := mocks.CacheProvider{} + //todo: replace this line with mocks for all tests + mockCache := lru.New(128) + + mockCacheProvider.On("NewCacheWithParams", + mock.AnythingOfType("int"), + mock.AnythingOfType("string"), + mock.AnythingOfType("int64"), + mock.AnythingOfType("string")).Return(mockCache) + + return &mockCacheProvider +} + func getMockStore() *mocks.Store { mockStore := mocks.Store{} diff --git a/store/localcachelayer/post_layer_test.go b/store/localcachelayer/post_layer_test.go index a58f73b6ee..7d832be621 100644 --- a/store/localcachelayer/post_layer_test.go +++ b/store/localcachelayer/post_layer_test.go @@ -29,7 +29,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetEtag: first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) expectedResult := fmt.Sprintf("%v.%v", model.CurrentVersion, fakeLastTime) @@ -44,7 +45,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetEtag: first call not cached, second force no cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Post().GetEtag(channelId, true) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) @@ -54,7 +56,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetEtag: first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Post().GetEtag(channelId, true) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) @@ -65,7 +68,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetEtag: first call not cached, clear caches, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Post().GetEtag(channelId, true) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetEtag", 1) @@ -76,7 +80,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetPostsSince: first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) expectedResult := model.NewPostList() @@ -93,7 +98,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetPostsSince: first call not cached, second force no cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Post().GetPostsSince(fakeOptions, true) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetPostsSince", 1) @@ -103,7 +109,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetPostsSince: first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Post().GetPostsSince(fakeOptions, true) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetPostsSince", 1) @@ -114,7 +121,8 @@ func TestPostStoreLastPostTimeCache(t *testing.T) { t.Run("GetPostsSince: first call not cached, clear caches, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Post().GetPostsSince(fakeOptions, true) mockStore.Post().(*mocks.PostStore).AssertNumberOfCalls(t, "GetPostsSince", 1) @@ -130,7 +138,8 @@ func TestPostStoreCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotPosts, err := cachedStore.Post().GetPosts(fakeOptions, true) require.Nil(t, err) @@ -143,7 +152,8 @@ func TestPostStoreCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotPosts, err := cachedStore.Post().GetPosts(fakeOptions, true) require.Nil(t, err) @@ -156,7 +166,8 @@ func TestPostStoreCache(t *testing.T) { t.Run("first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotPosts, err := cachedStore.Post().GetPosts(fakeOptions, true) require.Nil(t, err) diff --git a/store/localcachelayer/reaction_layer_test.go b/store/localcachelayer/reaction_layer_test.go index 264da646bc..1895d933fb 100644 --- a/store/localcachelayer/reaction_layer_test.go +++ b/store/localcachelayer/reaction_layer_test.go @@ -22,7 +22,8 @@ func TestReactionStoreCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) reaction, err := cachedStore.Reaction().GetForPost("123", true) require.Nil(t, err) @@ -36,7 +37,8 @@ func TestReactionStoreCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Reaction().GetForPost("123", true) mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 1) @@ -46,7 +48,8 @@ func TestReactionStoreCache(t *testing.T) { t.Run("first call not cached, save, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Reaction().GetForPost("123", true) mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 1) @@ -57,7 +60,8 @@ func TestReactionStoreCache(t *testing.T) { t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Reaction().GetForPost("123", true) mockStore.Reaction().(*mocks.ReactionStore).AssertNumberOfCalls(t, "GetForPost", 1) diff --git a/store/localcachelayer/role_layer_test.go b/store/localcachelayer/role_layer_test.go index 75f2e17948..60af018dc1 100644 --- a/store/localcachelayer/role_layer_test.go +++ b/store/localcachelayer/role_layer_test.go @@ -22,7 +22,8 @@ func TestRoleStoreCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) role, err := cachedStore.Role().GetByName("role-name") require.Nil(t, err) @@ -36,7 +37,8 @@ func TestRoleStoreCache(t *testing.T) { t.Run("first call not cached, save, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Role().GetByName("role-name") mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) @@ -47,7 +49,8 @@ func TestRoleStoreCache(t *testing.T) { t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Role().GetByName("role-name") mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) @@ -58,7 +61,8 @@ func TestRoleStoreCache(t *testing.T) { 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) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Role().GetByName("role-name") mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1) diff --git a/store/localcachelayer/scheme_layer_test.go b/store/localcachelayer/scheme_layer_test.go index 0b694f398d..1cc94542f0 100644 --- a/store/localcachelayer/scheme_layer_test.go +++ b/store/localcachelayer/scheme_layer_test.go @@ -22,7 +22,8 @@ func TestSchemeStoreCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) scheme, err := cachedStore.Scheme().Get("123") require.Nil(t, err) @@ -36,7 +37,8 @@ func TestSchemeStoreCache(t *testing.T) { t.Run("first call not cached, save, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Scheme().Get("123") mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) @@ -47,7 +49,8 @@ func TestSchemeStoreCache(t *testing.T) { t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Scheme().Get("123") mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) @@ -58,7 +61,8 @@ func TestSchemeStoreCache(t *testing.T) { 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) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Scheme().Get("123") mockStore.Scheme().(*mocks.SchemeStore).AssertNumberOfCalls(t, "Get", 1) diff --git a/store/localcachelayer/team_layer_test.go b/store/localcachelayer/team_layer_test.go index 47f4654c7a..c6b1b294ae 100644 --- a/store/localcachelayer/team_layer_test.go +++ b/store/localcachelayer/team_layer_test.go @@ -22,7 +22,8 @@ func TestTeamStoreCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUserTeamIds, err := cachedStore.Team().GetUserTeamIds(fakeUserId, true) require.Nil(t, err) @@ -37,7 +38,8 @@ func TestTeamStoreCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUserTeamIds, err := cachedStore.Team().GetUserTeamIds(fakeUserId, true) require.Nil(t, err) @@ -52,7 +54,8 @@ func TestTeamStoreCache(t *testing.T) { t.Run("first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUserTeamIds, err := cachedStore.Team().GetUserTeamIds(fakeUserId, true) require.Nil(t, err) diff --git a/store/localcachelayer/terms_of_service_layer_test.go b/store/localcachelayer/terms_of_service_layer_test.go index 9036c6b4ee..466c267081 100644 --- a/store/localcachelayer/terms_of_service_layer_test.go +++ b/store/localcachelayer/terms_of_service_layer_test.go @@ -23,7 +23,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call by latest not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) termsOfService, err := cachedStore.TermsOfService().GetLatest(true) require.Nil(t, err) @@ -37,7 +38,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call by id not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) termsOfService, err := cachedStore.TermsOfService().Get("123", true) require.Nil(t, err) @@ -51,7 +53,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call by id not cached, second force no cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().Get("123", true) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "Get", 1) @@ -61,7 +64,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call latest not cached, second force no cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().GetLatest(true) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "GetLatest", 1) @@ -71,7 +75,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call by id force no cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().Get("123", false) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "Get", 1) @@ -83,7 +88,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call latest force no cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().GetLatest(false) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "GetLatest", 1) @@ -95,7 +101,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call latest, second call by id cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().GetLatest(true) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "GetLatest", 1) @@ -105,7 +112,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first call by id not cached, save, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().Get("123", false) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "Get", 1) @@ -116,7 +124,8 @@ func TestTermsOfServiceStoreTermsOfServiceCache(t *testing.T) { t.Run("first get latest not cached, save new, then get latest, returning different data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.TermsOfService().GetLatest(true) mockStore.TermsOfService().(*mocks.TermsOfServiceStore).AssertNumberOfCalls(t, "GetLatest", 1) diff --git a/store/localcachelayer/user_layer_test.go b/store/localcachelayer/user_layer_test.go index baebf5ee75..ff88ae9567 100644 --- a/store/localcachelayer/user_layer_test.go +++ b/store/localcachelayer/user_layer_test.go @@ -25,7 +25,8 @@ func TestUserStoreGetProfileByIdsCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUser, err := cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true) require.Nil(t, err) @@ -38,7 +39,8 @@ func TestUserStoreGetProfileByIdsCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUser, err := cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true) require.Nil(t, err) @@ -51,7 +53,8 @@ func TestUserStoreGetProfileByIdsCache(t *testing.T) { t.Run("first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUser, err := cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true) require.Nil(t, err) @@ -71,7 +74,8 @@ func TestUserStoreGetCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUser, err := cachedStore.User().Get(fakeUserId) require.Nil(t, err) @@ -84,7 +88,8 @@ func TestUserStoreGetCache(t *testing.T) { t.Run("first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) gotUser, err := cachedStore.User().Get(fakeUserId) require.Nil(t, err) diff --git a/store/localcachelayer/webhook_layer_test.go b/store/localcachelayer/webhook_layer_test.go index 224a5fd5e7..675135b8a2 100644 --- a/store/localcachelayer/webhook_layer_test.go +++ b/store/localcachelayer/webhook_layer_test.go @@ -23,7 +23,8 @@ func TestWebhookStoreCache(t *testing.T) { t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) incomingWebhook, err := cachedStore.Webhook().GetIncoming("123", true) require.Nil(t, err) @@ -37,7 +38,8 @@ func TestWebhookStoreCache(t *testing.T) { t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Webhook().GetIncoming("123", true) mockStore.Webhook().(*mocks.WebhookStore).AssertNumberOfCalls(t, "GetIncoming", 1) @@ -47,7 +49,8 @@ func TestWebhookStoreCache(t *testing.T) { t.Run("first call not cached, invalidate, and then not cached again", func(t *testing.T) { mockStore := getMockStore() - cachedStore := NewLocalCacheLayer(mockStore, nil, nil) + mockCacheProvider := getMockCacheProvider() + cachedStore := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider) cachedStore.Webhook().GetIncoming("123", true) mockStore.Webhook().(*mocks.WebhookStore).AssertNumberOfCalls(t, "GetIncoming", 1) diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index f66ff0ebb0..fc939d94c0 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -18,8 +18,8 @@ import ( "github.com/mattermost/mattermost-server/v5/einterfaces" "github.com/mattermost/mattermost-server/v5/mlog" "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/store" - "github.com/mattermost/mattermost-server/v5/utils" ) const ( @@ -274,9 +274,9 @@ type publicChannel struct { Purpose string `json:"purpose"` } -var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE) -var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE) -var channelByNameCache = utils.NewLru(model.CHANNEL_CACHE_SIZE) +var allChannelMembersForUserCache = lru.New(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE) +var allChannelMembersNotifyPropsForChannelCache = lru.New(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE) +var channelByNameCache = lru.New(model.CHANNEL_CACHE_SIZE) func (s SqlChannelStore) ClearCaches() { allChannelMembersForUserCache.Purge() diff --git a/store/sqlstore/file_info_store.go b/store/sqlstore/file_info_store.go index dd4d7cf65a..0189723552 100644 --- a/store/sqlstore/file_info_store.go +++ b/store/sqlstore/file_info_store.go @@ -11,8 +11,9 @@ import ( "github.com/mattermost/mattermost-server/v5/einterfaces" "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/cache" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/store" - "github.com/mattermost/mattermost-server/v5/utils" ) type SqlFileInfoStore struct { @@ -25,7 +26,7 @@ const ( FILE_INFO_CACHE_SEC = 1800 // 30 minutes ) -var fileInfoCache *utils.Cache = utils.NewLru(FILE_INFO_CACHE_SIZE) +var fileInfoCache cache.Cache = lru.New(FILE_INFO_CACHE_SIZE) func (fs SqlFileInfoStore) ClearCaches() { fileInfoCache.Purge() diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go index 445eb35b29..0b12f45d98 100644 --- a/store/sqlstore/user_store.go +++ b/store/sqlstore/user_store.go @@ -16,8 +16,9 @@ import ( "github.com/mattermost/mattermost-server/v5/einterfaces" "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/cache" + "github.com/mattermost/mattermost-server/v5/services/cache/lru" "github.com/mattermost/mattermost-server/v5/store" - "github.com/mattermost/mattermost-server/v5/utils" ) const ( @@ -41,7 +42,7 @@ type SqlUserStore struct { usersQuery sq.SelectBuilder } -var profilesInChannelCache *utils.Cache = utils.NewLru(PROFILES_IN_CHANNEL_CACHE_SIZE) +var profilesInChannelCache cache.Cache = lru.New(PROFILES_IN_CHANNEL_CACHE_SIZE) func (us SqlUserStore) ClearCaches() { profilesInChannelCache.Purge() diff --git a/store/storetest/mocks/Cache.go b/store/storetest/mocks/Cache.go new file mode 100644 index 0000000000..f900f2211a --- /dev/null +++ b/store/storetest/mocks/Cache.go @@ -0,0 +1,145 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +// Regenerate this file using `make store-mocks`. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// Cache is an autogenerated mock type for the Cache type +type Cache struct { + mock.Mock +} + +// Add provides a mock function with given fields: key, value +func (_m *Cache) Add(key interface{}, value interface{}) { + _m.Called(key, value) +} + +// AddWithDefaultExpires provides a mock function with given fields: key, value +func (_m *Cache) AddWithDefaultExpires(key interface{}, value interface{}) { + _m.Called(key, value) +} + +// AddWithExpiresInSecs provides a mock function with given fields: key, value, expireAtSecs +func (_m *Cache) AddWithExpiresInSecs(key interface{}, value interface{}, expireAtSecs int64) { + _m.Called(key, value, expireAtSecs) +} + +// Get provides a mock function with given fields: key +func (_m *Cache) Get(key interface{}) (interface{}, bool) { + ret := _m.Called(key) + + var r0 interface{} + if rf, ok := ret.Get(0).(func(interface{}) interface{}); ok { + r0 = rf(key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + var r1 bool + if rf, ok := ret.Get(1).(func(interface{}) bool); ok { + r1 = rf(key) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// GetInvalidateClusterEvent provides a mock function with given fields: +func (_m *Cache) GetInvalidateClusterEvent() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetOrAdd provides a mock function with given fields: key, value, ttl +func (_m *Cache) GetOrAdd(key interface{}, value interface{}, ttl time.Duration) (interface{}, bool) { + ret := _m.Called(key, value, ttl) + + var r0 interface{} + if rf, ok := ret.Get(0).(func(interface{}, interface{}, time.Duration) interface{}); ok { + r0 = rf(key, value, ttl) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + var r1 bool + if rf, ok := ret.Get(1).(func(interface{}, interface{}, time.Duration) bool); ok { + r1 = rf(key, value, ttl) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// Keys provides a mock function with given fields: +func (_m *Cache) Keys() []interface{} { + ret := _m.Called() + + var r0 []interface{} + if rf, ok := ret.Get(0).(func() []interface{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]interface{}) + } + } + + return r0 +} + +// Len provides a mock function with given fields: +func (_m *Cache) Len() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// Name provides a mock function with given fields: +func (_m *Cache) Name() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Purge provides a mock function with given fields: +func (_m *Cache) Purge() { + _m.Called() +} + +// Remove provides a mock function with given fields: key +func (_m *Cache) Remove(key interface{}) { + _m.Called(key) +} diff --git a/store/storetest/mocks/CacheProvider.go b/store/storetest/mocks/CacheProvider.go new file mode 100644 index 0000000000..b03cfc6e71 --- /dev/null +++ b/store/storetest/mocks/CacheProvider.go @@ -0,0 +1,57 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +// Regenerate this file using `make store-mocks`. + +package mocks + +import ( + cache "github.com/mattermost/mattermost-server/v5/services/cache" + mock "github.com/stretchr/testify/mock" +) + +// CacheProvider is an autogenerated mock type for the CacheProvider type +type CacheProvider struct { + mock.Mock +} + +// NewCache provides a mock function with given fields: size +func (_m *CacheProvider) NewCache(size int) cache.Cache { + ret := _m.Called(size) + + var r0 cache.Cache + if rf, ok := ret.Get(0).(func(int) cache.Cache); ok { + r0 = rf(size) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.Cache) + } + } + + return r0 +} + +// Connect provides a mock function with given fields: +func (_m *CacheProvider) Connect() { + _m.Called() +} + +// Close provides a mock function with given fields: +func (_m *CacheProvider) Close() { + _m.Called() +} + +// NewCacheWithParams provides a mock function with given fields: size, name, defaultExpiry, invalidateClusterEvent +func (_m *CacheProvider) NewCacheWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) cache.Cache { + ret := _m.Called(size, name, defaultExpiry, invalidateClusterEvent) + + var r0 cache.Cache + if rf, ok := ret.Get(0).(func(int, string, int64, string) cache.Cache); ok { + r0 = rf(size, name, defaultExpiry, invalidateClusterEvent) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.Cache) + } + } + + return r0 +} diff --git a/store/storetest/store.go b/store/storetest/store.go index 3ec2bc4f30..f29809ffbd 100644 --- a/store/storetest/store.go +++ b/store/storetest/store.go @@ -4,10 +4,9 @@ package storetest import ( - "github.com/stretchr/testify/mock" - "github.com/mattermost/mattermost-server/v5/store" "github.com/mattermost/mattermost-server/v5/store/storetest/mocks" + "github.com/stretchr/testify/mock" ) // Store can be used to provide mock stores for testing.