github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/localcachelayer/layer.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package localcachelayer
     5  
     6  import (
     7  	"runtime"
     8  	"time"
     9  
    10  	"github.com/mattermost/mattermost-server/v5/einterfaces"
    11  	"github.com/mattermost/mattermost-server/v5/model"
    12  	"github.com/mattermost/mattermost-server/v5/services/cache"
    13  	"github.com/mattermost/mattermost-server/v5/store"
    14  )
    15  
    16  const (
    17  	ReactionCacheSize = 20000
    18  	ReactionCacheSec  = 30 * 60
    19  
    20  	RoleCacheSize = 20000
    21  	RoleCacheSec  = 30 * 60
    22  
    23  	SchemeCacheSize = 20000
    24  	SchemeCacheSec  = 30 * 60
    25  
    26  	FileInfoCacheSize = 25000
    27  	FileInfoCacheSec  = 30 * 60
    28  
    29  	ChannelGuestCountCacheSize = model.CHANNEL_CACHE_SIZE
    30  	ChannelGuestCountCacheSec  = 30 * 60
    31  
    32  	WebhookCacheSize = 25000
    33  	WebhookCacheSec  = 15 * 60
    34  
    35  	EmojiCacheSize = 5000
    36  	EmojiCacheSec  = 30 * 60
    37  
    38  	ChannelPinnedPostsCounsCacheSize = model.CHANNEL_CACHE_SIZE
    39  	ChannelPinnedPostsCountsCacheSec = 30 * 60
    40  
    41  	ChannelMembersCountsCacheSize = model.CHANNEL_CACHE_SIZE
    42  	ChannelMembersCountsCacheSec  = 30 * 60
    43  
    44  	LastPostsCacheSize = 20000
    45  	LastPostsCacheSec  = 30 * 60
    46  
    47  	TermsOfServiceCacheSize = 20000
    48  	TermsOfServiceCacheSec  = 30 * 60
    49  	LastPostTimeCacheSize   = 25000
    50  	LastPostTimeCacheSec    = 15 * 60
    51  
    52  	UserProfileByIDCacheSize = 20000
    53  	UserProfileByIDSec       = 30 * 60
    54  
    55  	ProfilesInChannelCacheSize  = model.CHANNEL_CACHE_SIZE
    56  	PROFILES_IN_ChannelCacheSec = 15 * 60
    57  
    58  	TeamCacheSize = 20000
    59  	TeamCacheSec  = 30 * 60
    60  
    61  	ClearCacheMessageData = ""
    62  
    63  	ChannelCacheSec = 15 * 60 // 15 mins
    64  )
    65  
    66  type LocalCacheStore struct {
    67  	store.Store
    68  	metrics einterfaces.MetricsInterface
    69  	cluster einterfaces.ClusterInterface
    70  
    71  	reaction      LocalCacheReactionStore
    72  	reactionCache cache.Cache
    73  
    74  	fileInfo      LocalCacheFileInfoStore
    75  	fileInfoCache cache.Cache
    76  
    77  	role                 LocalCacheRoleStore
    78  	roleCache            cache.Cache
    79  	rolePermissionsCache cache.Cache
    80  
    81  	scheme      LocalCacheSchemeStore
    82  	schemeCache cache.Cache
    83  
    84  	emoji              LocalCacheEmojiStore
    85  	emojiCacheById     cache.Cache
    86  	emojiIdCacheByName cache.Cache
    87  
    88  	channel                      LocalCacheChannelStore
    89  	channelMemberCountsCache     cache.Cache
    90  	channelGuestCountCache       cache.Cache
    91  	channelPinnedPostCountsCache cache.Cache
    92  	channelByIdCache             cache.Cache
    93  
    94  	webhook      LocalCacheWebhookStore
    95  	webhookCache cache.Cache
    96  
    97  	post               LocalCachePostStore
    98  	postLastPostsCache cache.Cache
    99  	lastPostTimeCache  cache.Cache
   100  
   101  	user                   *LocalCacheUserStore
   102  	userProfileByIdsCache  cache.Cache
   103  	profilesInChannelCache cache.Cache
   104  
   105  	team                       LocalCacheTeamStore
   106  	teamAllTeamIdsForUserCache cache.Cache
   107  
   108  	termsOfService      LocalCacheTermsOfServiceStore
   109  	termsOfServiceCache cache.Cache
   110  }
   111  
   112  func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface, cacheProvider cache.Provider) (localCacheStore LocalCacheStore, err error) {
   113  	localCacheStore = LocalCacheStore{
   114  		Store:   baseStore,
   115  		cluster: cluster,
   116  		metrics: metrics,
   117  	}
   118  	// Reactions
   119  	if localCacheStore.reactionCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   120  		Size:                   ReactionCacheSize,
   121  		Name:                   "Reaction",
   122  		DefaultExpiry:          ReactionCacheSec * time.Second,
   123  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS,
   124  	}); err != nil {
   125  		return
   126  	}
   127  	localCacheStore.reaction = LocalCacheReactionStore{ReactionStore: baseStore.Reaction(), rootStore: &localCacheStore}
   128  
   129  	// Roles
   130  	if localCacheStore.roleCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   131  		Size:                   RoleCacheSize,
   132  		Name:                   "Role",
   133  		DefaultExpiry:          RoleCacheSec * time.Second,
   134  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES,
   135  		Striped:                true,
   136  		StripedBuckets:         maxInt(runtime.NumCPU()-1, 1),
   137  	}); err != nil {
   138  		return
   139  	}
   140  	if localCacheStore.rolePermissionsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   141  		Size:                   RoleCacheSize,
   142  		Name:                   "RolePermission",
   143  		DefaultExpiry:          RoleCacheSec * time.Second,
   144  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLE_PERMISSIONS,
   145  	}); err != nil {
   146  		return
   147  	}
   148  	localCacheStore.role = LocalCacheRoleStore{RoleStore: baseStore.Role(), rootStore: &localCacheStore}
   149  
   150  	// Schemes
   151  	if localCacheStore.schemeCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   152  		Size:                   SchemeCacheSize,
   153  		Name:                   "Scheme",
   154  		DefaultExpiry:          SchemeCacheSec * time.Second,
   155  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES,
   156  	}); err != nil {
   157  		return
   158  	}
   159  	localCacheStore.scheme = LocalCacheSchemeStore{SchemeStore: baseStore.Scheme(), rootStore: &localCacheStore}
   160  
   161  	// FileInfo
   162  	if localCacheStore.fileInfoCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   163  		Size:                   FileInfoCacheSize,
   164  		Name:                   "FileInfo",
   165  		DefaultExpiry:          FileInfoCacheSec * time.Second,
   166  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_FILE_INFOS,
   167  	}); err != nil {
   168  		return
   169  	}
   170  	localCacheStore.fileInfo = LocalCacheFileInfoStore{FileInfoStore: baseStore.FileInfo(), rootStore: &localCacheStore}
   171  
   172  	// Webhooks
   173  	if localCacheStore.webhookCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   174  		Size:                   WebhookCacheSize,
   175  		Name:                   "Webhook",
   176  		DefaultExpiry:          WebhookCacheSec * time.Second,
   177  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOKS,
   178  	}); err != nil {
   179  		return
   180  	}
   181  	localCacheStore.webhook = LocalCacheWebhookStore{WebhookStore: baseStore.Webhook(), rootStore: &localCacheStore}
   182  
   183  	// Emojis
   184  	if localCacheStore.emojiCacheById, err = cacheProvider.NewCache(&cache.CacheOptions{
   185  		Size:                   EmojiCacheSize,
   186  		Name:                   "EmojiById",
   187  		DefaultExpiry:          EmojiCacheSec * time.Second,
   188  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_BY_ID,
   189  	}); err != nil {
   190  		return
   191  	}
   192  	if localCacheStore.emojiIdCacheByName, err = cacheProvider.NewCache(&cache.CacheOptions{
   193  		Size:                   EmojiCacheSize,
   194  		Name:                   "EmojiByName",
   195  		DefaultExpiry:          EmojiCacheSec * time.Second,
   196  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_ID_BY_NAME,
   197  	}); err != nil {
   198  		return
   199  	}
   200  	localCacheStore.emoji = LocalCacheEmojiStore{EmojiStore: baseStore.Emoji(), rootStore: &localCacheStore}
   201  
   202  	// Channels
   203  	if localCacheStore.channelPinnedPostCountsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   204  		Size:                   ChannelPinnedPostsCounsCacheSize,
   205  		Name:                   "ChannelPinnedPostsCounts",
   206  		DefaultExpiry:          ChannelPinnedPostsCountsCacheSec * time.Second,
   207  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_PINNEDPOSTS_COUNTS,
   208  	}); err != nil {
   209  		return
   210  	}
   211  	if localCacheStore.channelMemberCountsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   212  		Size:                   ChannelMembersCountsCacheSize,
   213  		Name:                   "ChannelMemberCounts",
   214  		DefaultExpiry:          ChannelMembersCountsCacheSec * time.Second,
   215  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBER_COUNTS,
   216  	}); err != nil {
   217  		return
   218  	}
   219  	if localCacheStore.channelGuestCountCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   220  		Size:                   ChannelGuestCountCacheSize,
   221  		Name:                   "ChannelGuestsCount",
   222  		DefaultExpiry:          ChannelGuestCountCacheSec * time.Second,
   223  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_GUEST_COUNT,
   224  	}); err != nil {
   225  		return
   226  	}
   227  	if localCacheStore.channelByIdCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   228  		Size:                   model.CHANNEL_CACHE_SIZE,
   229  		Name:                   "channelById",
   230  		DefaultExpiry:          ChannelCacheSec * time.Second,
   231  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL,
   232  	}); err != nil {
   233  		return
   234  	}
   235  	localCacheStore.channel = LocalCacheChannelStore{ChannelStore: baseStore.Channel(), rootStore: &localCacheStore}
   236  
   237  	// Posts
   238  	if localCacheStore.postLastPostsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   239  		Size:                   LastPostsCacheSize,
   240  		Name:                   "LastPost",
   241  		DefaultExpiry:          LastPostsCacheSec * time.Second,
   242  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POSTS,
   243  	}); err != nil {
   244  		return
   245  	}
   246  	if localCacheStore.lastPostTimeCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   247  		Size:                   LastPostTimeCacheSize,
   248  		Name:                   "LastPostTime",
   249  		DefaultExpiry:          LastPostTimeCacheSec * time.Second,
   250  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POST_TIME,
   251  	}); err != nil {
   252  		return
   253  	}
   254  	localCacheStore.post = LocalCachePostStore{PostStore: baseStore.Post(), rootStore: &localCacheStore}
   255  
   256  	// TOS
   257  	if localCacheStore.termsOfServiceCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   258  		Size:                   TermsOfServiceCacheSize,
   259  		Name:                   "TermsOfService",
   260  		DefaultExpiry:          TermsOfServiceCacheSec * time.Second,
   261  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE,
   262  	}); err != nil {
   263  		return
   264  	}
   265  	localCacheStore.termsOfService = LocalCacheTermsOfServiceStore{TermsOfServiceStore: baseStore.TermsOfService(), rootStore: &localCacheStore}
   266  
   267  	// Users
   268  	if localCacheStore.userProfileByIdsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   269  		Size:                   UserProfileByIDCacheSize,
   270  		Name:                   "UserProfileByIds",
   271  		DefaultExpiry:          UserProfileByIDSec * time.Second,
   272  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_BY_IDS,
   273  		Striped:                true,
   274  		StripedBuckets:         maxInt(runtime.NumCPU()-1, 1),
   275  	}); err != nil {
   276  		return
   277  	}
   278  	if localCacheStore.profilesInChannelCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   279  		Size:                   ProfilesInChannelCacheSize,
   280  		Name:                   "ProfilesInChannel",
   281  		DefaultExpiry:          PROFILES_IN_ChannelCacheSec * time.Second,
   282  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_IN_CHANNEL,
   283  	}); err != nil {
   284  		return
   285  	}
   286  	localCacheStore.user = &LocalCacheUserStore{
   287  		UserStore:                     baseStore.User(),
   288  		rootStore:                     &localCacheStore,
   289  		userProfileByIdsInvalidations: make(map[string]bool),
   290  	}
   291  
   292  	// Teams
   293  	if localCacheStore.teamAllTeamIdsForUserCache, err = cacheProvider.NewCache(&cache.CacheOptions{
   294  		Size:                   TeamCacheSize,
   295  		Name:                   "Team",
   296  		DefaultExpiry:          TeamCacheSec * time.Second,
   297  		InvalidateClusterEvent: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TEAMS,
   298  	}); err != nil {
   299  		return
   300  	}
   301  	localCacheStore.team = LocalCacheTeamStore{TeamStore: baseStore.Team(), rootStore: &localCacheStore}
   302  
   303  	if cluster != nil {
   304  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS, localCacheStore.reaction.handleClusterInvalidateReaction)
   305  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, localCacheStore.role.handleClusterInvalidateRole)
   306  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLE_PERMISSIONS, localCacheStore.role.handleClusterInvalidateRolePermissions)
   307  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES, localCacheStore.scheme.handleClusterInvalidateScheme)
   308  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_FILE_INFOS, localCacheStore.fileInfo.handleClusterInvalidateFileInfo)
   309  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POST_TIME, localCacheStore.post.handleClusterInvalidateLastPostTime)
   310  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOKS, localCacheStore.webhook.handleClusterInvalidateWebhook)
   311  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_BY_ID, localCacheStore.emoji.handleClusterInvalidateEmojiById)
   312  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_ID_BY_NAME, localCacheStore.emoji.handleClusterInvalidateEmojiIdByName)
   313  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_PINNEDPOSTS_COUNTS, localCacheStore.channel.handleClusterInvalidateChannelPinnedPostCount)
   314  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBER_COUNTS, localCacheStore.channel.handleClusterInvalidateChannelMemberCounts)
   315  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_GUEST_COUNT, localCacheStore.channel.handleClusterInvalidateChannelGuestCounts)
   316  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL, localCacheStore.channel.handleClusterInvalidateChannelById)
   317  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_LAST_POSTS, localCacheStore.post.handleClusterInvalidateLastPosts)
   318  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE, localCacheStore.termsOfService.handleClusterInvalidateTermsOfService)
   319  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_BY_IDS, localCacheStore.user.handleClusterInvalidateScheme)
   320  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_PROFILE_IN_CHANNEL, localCacheStore.user.handleClusterInvalidateProfilesInChannel)
   321  		cluster.RegisterClusterMessageHandler(model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TEAMS, localCacheStore.team.handleClusterInvalidateTeam)
   322  	}
   323  	return
   324  }
   325  
   326  func maxInt(a, b int) int {
   327  	if a > b {
   328  		return a
   329  	}
   330  	return b
   331  }
   332  
   333  func (s LocalCacheStore) Reaction() store.ReactionStore {
   334  	return s.reaction
   335  }
   336  
   337  func (s LocalCacheStore) Role() store.RoleStore {
   338  	return s.role
   339  }
   340  
   341  func (s LocalCacheStore) Scheme() store.SchemeStore {
   342  	return s.scheme
   343  }
   344  
   345  func (s LocalCacheStore) FileInfo() store.FileInfoStore {
   346  	return s.fileInfo
   347  }
   348  
   349  func (s LocalCacheStore) Webhook() store.WebhookStore {
   350  	return s.webhook
   351  }
   352  
   353  func (s LocalCacheStore) Emoji() store.EmojiStore {
   354  	return s.emoji
   355  }
   356  
   357  func (s LocalCacheStore) Channel() store.ChannelStore {
   358  	return s.channel
   359  }
   360  
   361  func (s LocalCacheStore) Post() store.PostStore {
   362  	return s.post
   363  }
   364  
   365  func (s LocalCacheStore) TermsOfService() store.TermsOfServiceStore {
   366  	return s.termsOfService
   367  }
   368  
   369  func (s LocalCacheStore) User() store.UserStore {
   370  	return s.user
   371  }
   372  
   373  func (s LocalCacheStore) Team() store.TeamStore {
   374  	return s.team
   375  }
   376  
   377  func (s LocalCacheStore) DropAllTables() {
   378  	s.Invalidate()
   379  	s.Store.DropAllTables()
   380  }
   381  
   382  func (s *LocalCacheStore) doInvalidateCacheCluster(cache cache.Cache, key string) {
   383  	cache.Remove(key)
   384  	if s.cluster != nil {
   385  		msg := &model.ClusterMessage{
   386  			Event:    cache.GetInvalidateClusterEvent(),
   387  			SendType: model.CLUSTER_SEND_BEST_EFFORT,
   388  			Data:     key,
   389  		}
   390  		s.cluster.SendClusterMessage(msg)
   391  	}
   392  }
   393  
   394  func (s *LocalCacheStore) doStandardAddToCache(cache cache.Cache, key string, value interface{}) {
   395  	cache.SetWithDefaultExpiry(key, value)
   396  }
   397  
   398  func (s *LocalCacheStore) doStandardReadCache(cache cache.Cache, key string, value interface{}) error {
   399  	err := cache.Get(key, value)
   400  	if err == nil {
   401  		if s.metrics != nil {
   402  			s.metrics.IncrementMemCacheHitCounter(cache.Name())
   403  		}
   404  		return nil
   405  	}
   406  	if s.metrics != nil {
   407  		s.metrics.IncrementMemCacheMissCounter(cache.Name())
   408  	}
   409  	return err
   410  }
   411  
   412  func (s *LocalCacheStore) doClearCacheCluster(cache cache.Cache) {
   413  	cache.Purge()
   414  	if s.cluster != nil {
   415  		msg := &model.ClusterMessage{
   416  			Event:    cache.GetInvalidateClusterEvent(),
   417  			SendType: model.CLUSTER_SEND_BEST_EFFORT,
   418  			Data:     ClearCacheMessageData,
   419  		}
   420  		s.cluster.SendClusterMessage(msg)
   421  	}
   422  }
   423  
   424  func (s *LocalCacheStore) Invalidate() {
   425  	s.doClearCacheCluster(s.reactionCache)
   426  	s.doClearCacheCluster(s.schemeCache)
   427  	s.doClearCacheCluster(s.roleCache)
   428  	s.doClearCacheCluster(s.fileInfoCache)
   429  	s.doClearCacheCluster(s.webhookCache)
   430  	s.doClearCacheCluster(s.emojiCacheById)
   431  	s.doClearCacheCluster(s.emojiIdCacheByName)
   432  	s.doClearCacheCluster(s.channelMemberCountsCache)
   433  	s.doClearCacheCluster(s.channelPinnedPostCountsCache)
   434  	s.doClearCacheCluster(s.channelGuestCountCache)
   435  	s.doClearCacheCluster(s.channelByIdCache)
   436  	s.doClearCacheCluster(s.postLastPostsCache)
   437  	s.doClearCacheCluster(s.termsOfServiceCache)
   438  	s.doClearCacheCluster(s.lastPostTimeCache)
   439  	s.doClearCacheCluster(s.userProfileByIdsCache)
   440  	s.doClearCacheCluster(s.profilesInChannelCache)
   441  	s.doClearCacheCluster(s.teamAllTeamIdsForUserCache)
   442  	s.doClearCacheCluster(s.rolePermissionsCache)
   443  }