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 }