Files
Devin BinnieandGitHub 6554833b26 [MM-68988][MM-68989][MM-68990][MM-68991][MM-68997][MM-68998] Session Attributes MVF - Server-work (#36934)
* [MM-68988][MM-68989][MM-68990][MM-68991][MM-68997] Session Attributes MVF - Server-work

* PR feedback

* [MM-68998] Add web app hooks for Desktop App to signal a refresh of attributes/manifest

* Fix types

* Adjust the test to test the license first

* PR feedback

* Coderabbit feedback

* More tests
2026-06-09 13:10:53 -04:00

745 lines
29 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package localcachelayer
import (
"runtime"
"time"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/mattermost/mattermost/server/v8/einterfaces"
"github.com/mattermost/mattermost/server/v8/platform/services/cache"
)
const (
ReactionCacheSize = 20000
ReactionCacheSec = 30 * 60
RoleCacheSize = 20000
RoleCacheSec = 30 * 60
SchemeCacheSize = 20000
SchemeCacheSec = 30 * 60
FileInfoCacheSize = 25000
FileInfoCacheSec = 30 * 60
ChannelGuestCountCacheSize = model.ChannelCacheSize
ChannelGuestCountCacheSec = 30 * 60
WebhookCacheSize = 25000
WebhookCacheSec = 15 * 60
EmojiCacheSize = 5000
EmojiCacheSec = 30 * 60
ChannelPinnedPostsCountsCacheSize = model.ChannelCacheSize
ChannelPinnedPostsCountsCacheSec = 30 * 60
AllChannelMembersForUserCacheSize = model.SessionCacheSize
AllChannelMembersForUserCacheDuration = 15 * time.Minute
AllChannelMembersNotifyPropsForChannelCacheSize = model.SessionCacheSize
AllChannelMembersNotifyPropsForChannelCacheDuration = 30 * time.Minute
ChannelCacheDuration = 15 * time.Minute
ChannelMembersCountsCacheSize = model.ChannelCacheSize
ChannelMembersCountsCacheSec = 30 * 60
LastPostsCacheSize = 20000
LastPostsCacheSec = 30 * 60
PostsUsageCacheSize = 1
PostsUsageCacheSec = 30 * 60
TermsOfServiceCacheSize = 20000
TermsOfServiceCacheSec = 30 * 60
LastPostTimeCacheSize = 25000
LastPostTimeCacheSec = 15 * 60
UserProfileByIDCacheSize = 20000
UserProfileByIDSec = 30 * 60
ProfilesInChannelCacheSize = model.ChannelCacheSize
ProfilesInChannelCacheSec = 15 * 60
TeamCacheSize = 20000
TeamCacheSec = 30 * 60
ChannelCacheSec = 15 * 60 // 15 mins
// Auto-translation caches
UserAutoTranslationCacheSize = 50000 // User+channel combos
UserAutoTranslationCacheSec = 15 * 60
PostTranslationEtagCacheSize = 25000 // Channel etags for post translations
PostTranslationEtagCacheSec = 15 * 60
ContentFlaggingCacheSize = 100
ReadReceiptCacheSize = 50000
TemporaryPostCacheSize = 10000
TemporaryPostCacheMinutes = 60
SessionAttributeCacheSize = model.SessionCacheSize
PropertyFieldCacheSize = 100
PropertyFieldCacheSec = 30 * 60
)
var clearCacheMessageData = []byte("")
type LocalCacheStore struct {
store.Store
cacheType string
metrics einterfaces.MetricsInterface
cluster einterfaces.ClusterInterface
logger mlog.LoggerIFace
reaction LocalCacheReactionStore
reactionCache cache.Cache
fileInfo LocalCacheFileInfoStore
fileInfoCache cache.Cache
role LocalCacheRoleStore
roleCache cache.Cache
rolePermissionsCache 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
channelMembersForUserCache cache.Cache
channelMembersNotifyPropsCache cache.Cache
channelByNameCache cache.Cache
webhook LocalCacheWebhookStore
webhookCache cache.Cache
post LocalCachePostStore
postLastPostsCache cache.Cache
lastPostTimeCache cache.Cache
postsUsageCache cache.Cache
user *LocalCacheUserStore
allUserCache cache.Cache
userProfileByIdsCache cache.Cache
profilesInChannelCache cache.Cache
team LocalCacheTeamStore
teamAllTeamIdsForUserCache cache.Cache
termsOfService LocalCacheTermsOfServiceStore
termsOfServiceCache cache.Cache
autotranslation LocalCacheAutoTranslationStore
userAutoTranslationCache cache.Cache
postTranslationEtagCache cache.Cache
contentFlagging LocalCacheContentFlaggingStore
contentFlaggingCache cache.Cache
readReceipt LocalCacheReadReceiptStore
readReceiptCache cache.Cache
readReceiptPostReadersCache cache.Cache
readReceiptPostUnreadCountCache cache.Cache
temporaryPost LocalCacheTemporaryPostStore
temporaryPostCache cache.Cache
sessionAttribute LocalCacheSessionAttributeStore
sessionAttributeCache cache.Cache
propertyField LocalCachePropertyFieldStore
propertyFieldCache cache.Cache
}
func NewLocalCacheLayer(baseStore store.Store, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface, cacheProvider cache.Provider, logger mlog.LoggerIFace) (localCacheStore LocalCacheStore, err error) {
localCacheStore = LocalCacheStore{
Store: baseStore,
cluster: cluster,
metrics: metrics,
cacheType: cacheProvider.Type(),
logger: logger,
}
// Reactions
if localCacheStore.reactionCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ReactionCacheSize,
Name: "Reaction",
DefaultExpiry: ReactionCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForReactions,
}); err != nil {
return
}
localCacheStore.reaction = LocalCacheReactionStore{ReactionStore: baseStore.Reaction(), rootStore: &localCacheStore}
// Roles
if localCacheStore.roleCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: RoleCacheSize,
Name: "Role",
DefaultExpiry: RoleCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForRoles,
Striped: true,
StripedBuckets: max(runtime.NumCPU()-1, 1),
}); err != nil {
return
}
if localCacheStore.rolePermissionsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: RoleCacheSize,
Name: "RolePermission",
DefaultExpiry: RoleCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForRolePermissions,
}); err != nil {
return
}
localCacheStore.role = LocalCacheRoleStore{RoleStore: baseStore.Role(), rootStore: &localCacheStore}
// Schemes
if localCacheStore.schemeCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: SchemeCacheSize,
Name: "Scheme",
DefaultExpiry: SchemeCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForSchemes,
}); err != nil {
return
}
localCacheStore.scheme = LocalCacheSchemeStore{SchemeStore: baseStore.Scheme(), rootStore: &localCacheStore}
// FileInfo
if localCacheStore.fileInfoCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: FileInfoCacheSize,
Name: "FileInfo",
DefaultExpiry: FileInfoCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForFileInfos,
}); err != nil {
return
}
localCacheStore.fileInfo = LocalCacheFileInfoStore{FileInfoStore: baseStore.FileInfo(), rootStore: &localCacheStore}
// Webhooks
if localCacheStore.webhookCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: WebhookCacheSize,
Name: "Webhook",
DefaultExpiry: WebhookCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForWebhooks,
}); err != nil {
return
}
localCacheStore.webhook = LocalCacheWebhookStore{WebhookStore: baseStore.Webhook(), rootStore: &localCacheStore}
// Emojis
if localCacheStore.emojiCacheById, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: EmojiCacheSize,
Name: "EmojiById",
DefaultExpiry: EmojiCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForEmojisById,
}); err != nil {
return
}
if localCacheStore.emojiIdCacheByName, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: EmojiCacheSize,
Name: "EmojiByName",
DefaultExpiry: EmojiCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForEmojisIdByName,
}); err != nil {
return
}
localCacheStore.emoji = &LocalCacheEmojiStore{
EmojiStore: baseStore.Emoji(),
rootStore: &localCacheStore,
emojiByIdInvalidations: make(map[string]bool),
emojiByNameInvalidations: make(map[string]bool),
}
// Channels
if localCacheStore.channelPinnedPostCountsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ChannelPinnedPostsCountsCacheSize,
Name: "ChannelPinnedPostsCounts",
DefaultExpiry: ChannelPinnedPostsCountsCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForChannelPinnedpostsCounts,
}); err != nil {
return
}
if localCacheStore.channelMemberCountsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ChannelMembersCountsCacheSize,
Name: "ChannelMemberCounts",
DefaultExpiry: ChannelMembersCountsCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForChannelMemberCounts,
}); err != nil {
return
}
if localCacheStore.channelGuestCountCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ChannelGuestCountCacheSize,
Name: "ChannelGuestsCount",
DefaultExpiry: ChannelGuestCountCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForChannelGuestCount,
}); err != nil {
return
}
if localCacheStore.channelByIdCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: model.ChannelCacheSize,
Name: "channelById",
DefaultExpiry: ChannelCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForChannel,
}); err != nil {
return
}
if localCacheStore.channelMembersForUserCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: AllChannelMembersForUserCacheSize,
Name: "ChannelMembersForUser",
DefaultExpiry: AllChannelMembersForUserCacheDuration,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForUser,
}); err != nil {
return
}
if localCacheStore.channelMembersNotifyPropsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: AllChannelMembersNotifyPropsForChannelCacheSize,
Name: "ChannnelMembersNotifyProps",
DefaultExpiry: AllChannelMembersNotifyPropsForChannelCacheDuration,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForChannelMembersNotifyProps,
}); err != nil {
return
}
if localCacheStore.channelByNameCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: model.ChannelCacheSize,
Name: "ChannelByName",
DefaultExpiry: ChannelCacheDuration,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForChannelByName,
}); err != nil {
return
}
localCacheStore.channel = LocalCacheChannelStore{ChannelStore: baseStore.Channel(), rootStore: &localCacheStore}
// Posts
if localCacheStore.postLastPostsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: LastPostsCacheSize,
Name: "LastPost",
DefaultExpiry: LastPostsCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForLastPosts,
}); err != nil {
return
}
if localCacheStore.lastPostTimeCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: LastPostTimeCacheSize,
Name: "LastPostTime",
DefaultExpiry: LastPostTimeCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForLastPostTime,
}); err != nil {
return
}
if localCacheStore.postsUsageCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: PostsUsageCacheSize,
Name: "PostsUsage",
DefaultExpiry: PostsUsageCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForPostsUsage,
}); err != nil {
return
}
localCacheStore.post = LocalCachePostStore{PostStore: baseStore.Post(), rootStore: &localCacheStore}
// TOS
if localCacheStore.termsOfServiceCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: TermsOfServiceCacheSize,
Name: "TermsOfService",
DefaultExpiry: TermsOfServiceCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForTermsOfService,
}); err != nil {
return
}
localCacheStore.termsOfService = LocalCacheTermsOfServiceStore{TermsOfServiceStore: baseStore.TermsOfService(), rootStore: &localCacheStore}
// Users
if localCacheStore.allUserCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: 1,
Name: "AllUserProfiles",
DefaultExpiry: UserProfileByIDSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForAllProfiles,
}); err != nil {
return
}
if localCacheStore.userProfileByIdsCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: UserProfileByIDCacheSize,
Name: "UserProfileByIds",
DefaultExpiry: UserProfileByIDSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForProfileByIds,
Striped: true,
StripedBuckets: max(runtime.NumCPU()-1, 1),
}); err != nil {
return
}
// Hardcoding this to LRU because of the volume of SCAN calls in case of Redis.
if localCacheStore.profilesInChannelCache, err = cache.NewProvider().NewCache(&cache.CacheOptions{
Size: ProfilesInChannelCacheSize,
Name: "ProfilesInChannel",
DefaultExpiry: ProfilesInChannelCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForProfileInChannel,
}); err != nil {
return
}
localCacheStore.user = &LocalCacheUserStore{
UserStore: baseStore.User(),
rootStore: &localCacheStore,
userProfileByIdsInvalidations: make(map[string]bool),
}
// Teams
if localCacheStore.teamAllTeamIdsForUserCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: TeamCacheSize,
Name: "Team",
DefaultExpiry: TeamCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForTeams,
}); err != nil {
return
}
localCacheStore.team = LocalCacheTeamStore{TeamStore: baseStore.Team(), rootStore: &localCacheStore}
// Auto-translation
if localCacheStore.userAutoTranslationCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: UserAutoTranslationCacheSize,
Name: "UserAutoTranslation",
DefaultExpiry: UserAutoTranslationCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForUserAutoTranslation,
}); err != nil {
return
}
if localCacheStore.postTranslationEtagCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: PostTranslationEtagCacheSize,
Name: "PostTranslationEtag",
DefaultExpiry: PostTranslationEtagCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForPostTranslationEtag,
}); err != nil {
return
}
localCacheStore.autotranslation = LocalCacheAutoTranslationStore{AutoTranslationStore: baseStore.AutoTranslation(), rootStore: &localCacheStore}
if localCacheStore.contentFlaggingCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ContentFlaggingCacheSize,
Name: "ContentFlagging",
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForContentFlagging,
}); err != nil {
return
}
localCacheStore.contentFlagging = LocalCacheContentFlaggingStore{ContentFlaggingStore: baseStore.ContentFlagging(), rootStore: &localCacheStore}
// Read Receipts
if localCacheStore.readReceiptCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ReadReceiptCacheSize,
Name: "ReadReceipt",
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForReadReceipts,
}); err != nil {
return
}
if localCacheStore.readReceiptPostReadersCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ReadReceiptCacheSize,
Name: "ReadReceiptPostReaders",
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForReadReceipts,
}); err != nil {
return
}
if localCacheStore.readReceiptPostUnreadCountCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: ReadReceiptCacheSize,
Name: "ReadReceiptPostUnreadCount",
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForReadReceipts,
}); err != nil {
return
}
localCacheStore.readReceipt = LocalCacheReadReceiptStore{ReadReceiptStore: baseStore.ReadReceipt(), rootStore: &localCacheStore}
// Temporary Posts
if localCacheStore.temporaryPostCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: TemporaryPostCacheSize,
Name: "TemporaryPost",
DefaultExpiry: TemporaryPostCacheMinutes * time.Minute,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForTemporaryPosts,
}); err != nil {
return
}
localCacheStore.temporaryPost = LocalCacheTemporaryPostStore{TemporaryPostStore: baseStore.TemporaryPost(), rootStore: &localCacheStore}
// Session Attributes
if localCacheStore.sessionAttributeCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: SessionAttributeCacheSize,
Name: "SessionAttribute",
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForSessionAttributes,
}); err != nil {
return
}
localCacheStore.sessionAttribute = LocalCacheSessionAttributeStore{SessionAttributeStore: baseStore.SessionAttribute(), rootStore: &localCacheStore}
// Property Fields
if localCacheStore.propertyFieldCache, err = cacheProvider.NewCache(&cache.CacheOptions{
Size: PropertyFieldCacheSize,
Name: "PropertyField",
DefaultExpiry: PropertyFieldCacheSec * time.Second,
InvalidateClusterEvent: model.ClusterEventInvalidateCacheForPropertyFields,
}); err != nil {
return
}
localCacheStore.propertyField = LocalCachePropertyFieldStore{PropertyFieldStore: baseStore.PropertyField(), rootStore: &localCacheStore}
if cluster != nil {
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForReactions, localCacheStore.reaction.handleClusterInvalidateReaction)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForRoles, localCacheStore.role.handleClusterInvalidateRole)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForRolePermissions, localCacheStore.role.handleClusterInvalidateRolePermissions)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForSchemes, localCacheStore.scheme.handleClusterInvalidateScheme)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForFileInfos, localCacheStore.fileInfo.handleClusterInvalidateFileInfo)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForLastPostTime, localCacheStore.post.handleClusterInvalidateLastPostTime)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForPostsUsage, localCacheStore.post.handleClusterInvalidatePostsUsage)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForWebhooks, localCacheStore.webhook.handleClusterInvalidateWebhook)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForEmojisById, localCacheStore.emoji.handleClusterInvalidateEmojiById)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForEmojisIdByName, localCacheStore.emoji.handleClusterInvalidateEmojiIdByName)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForChannelPinnedpostsCounts, localCacheStore.channel.handleClusterInvalidateChannelPinnedPostCount)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForChannelMemberCounts, localCacheStore.channel.handleClusterInvalidateChannelMemberCounts)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForChannelGuestCount, localCacheStore.channel.handleClusterInvalidateChannelGuestCounts)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForChannel, localCacheStore.channel.handleClusterInvalidateChannelById)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForUser, localCacheStore.channel.handleClusterInvalidateChannelForUser)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForChannelMembersNotifyProps, localCacheStore.channel.handleClusterInvalidateChannelMembersNotifyProps)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForChannelByName, localCacheStore.channel.handleClusterInvalidateChannelByName)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForLastPosts, localCacheStore.post.handleClusterInvalidateLastPosts)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForTermsOfService, localCacheStore.termsOfService.handleClusterInvalidateTermsOfService)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForProfileByIds, localCacheStore.user.handleClusterInvalidateScheme)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForProfileInChannel, localCacheStore.user.handleClusterInvalidateProfilesInChannel)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForAllProfiles, localCacheStore.user.handleClusterInvalidateAllProfiles)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForTeams, localCacheStore.team.handleClusterInvalidateTeam)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForUserAutoTranslation, localCacheStore.autotranslation.handleClusterInvalidateUserAutoTranslation)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForPostTranslationEtag, localCacheStore.autotranslation.handleClusterInvalidatePostTranslationEtag)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForContentFlagging, localCacheStore.contentFlagging.handleClusterInvalidateContentFlagging)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForReadReceipts, localCacheStore.readReceipt.handleClusterInvalidateReadReceipts)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForTemporaryPosts, localCacheStore.temporaryPost.handleClusterInvalidateTemporaryPosts)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForSessionAttributes, localCacheStore.sessionAttribute.handleClusterInvalidateSessionAttributes)
cluster.RegisterClusterMessageHandler(model.ClusterEventInvalidateCacheForPropertyFields, localCacheStore.propertyField.handleClusterInvalidatePropertyField)
}
return
}
func (s LocalCacheStore) Reaction() store.ReactionStore {
return s.reaction
}
func (s LocalCacheStore) Role() store.RoleStore {
return s.role
}
func (s LocalCacheStore) Scheme() store.SchemeStore {
return s.scheme
}
func (s LocalCacheStore) FileInfo() store.FileInfoStore {
return s.fileInfo
}
func (s LocalCacheStore) Webhook() store.WebhookStore {
return s.webhook
}
func (s LocalCacheStore) Emoji() store.EmojiStore {
return s.emoji
}
func (s LocalCacheStore) Channel() store.ChannelStore {
return s.channel
}
func (s LocalCacheStore) Post() store.PostStore {
return s.post
}
func (s LocalCacheStore) TermsOfService() store.TermsOfServiceStore {
return s.termsOfService
}
func (s LocalCacheStore) User() store.UserStore {
return s.user
}
func (s LocalCacheStore) Team() store.TeamStore {
return s.team
}
func (s LocalCacheStore) AutoTranslation() store.AutoTranslationStore {
return s.autotranslation
}
func (s LocalCacheStore) ContentFlagging() store.ContentFlaggingStore {
return s.contentFlagging
}
func (s LocalCacheStore) ReadReceipt() store.ReadReceiptStore {
return s.readReceipt
}
func (s LocalCacheStore) TemporaryPost() store.TemporaryPostStore {
return s.temporaryPost
}
func (s LocalCacheStore) SessionAttribute() store.SessionAttributeStore {
return &s.sessionAttribute
}
func (s LocalCacheStore) PropertyField() store.PropertyFieldStore {
return s.propertyField
}
func (s LocalCacheStore) DropAllTables() {
s.Invalidate()
s.Store.DropAllTables()
}
func (s *LocalCacheStore) doInvalidateCacheCluster(cache cache.Cache, key string, props map[string]string) {
err := cache.Remove(key)
if err != nil {
s.logger.Warn("Error while removing cache entry", mlog.Err(err), mlog.String("cache_name", cache.Name()))
}
if s.cluster != nil && cache.GetInvalidateClusterEvent() != model.ClusterEventNone {
msg := &model.ClusterMessage{
Event: cache.GetInvalidateClusterEvent(),
SendType: model.ClusterSendBestEffort,
Data: []byte(key),
}
if props != nil {
msg.Props = props
}
s.cluster.SendClusterMessage(msg)
}
}
func (s *LocalCacheStore) doMultiInvalidateCacheCluster(cache cache.Cache, keys []string, props map[string]string) {
err := cache.RemoveMulti(keys)
if err != nil {
s.logger.Warn("Error while removing cache entry", mlog.Err(err), mlog.String("cache_name", cache.Name()))
}
if s.cluster != nil && cache.GetInvalidateClusterEvent() != model.ClusterEventNone {
for _, key := range keys {
msg := &model.ClusterMessage{
Event: cache.GetInvalidateClusterEvent(),
SendType: model.ClusterSendBestEffort,
Data: []byte(key),
}
if props != nil {
msg.Props = props
}
s.cluster.SendClusterMessage(msg)
}
}
}
func (s *LocalCacheStore) doStandardAddToCache(cache cache.Cache, key string, value any) {
err := cache.SetWithDefaultExpiry(key, value)
if err != nil {
s.logger.Warn("Error while setting cache entry", mlog.Err(err), mlog.String("cache_name", cache.Name()))
}
}
func (s *LocalCacheStore) doStandardReadCache(c cache.Cache, key string, value any) error {
err := c.Get(key, value)
if err == nil {
if s.metrics != nil {
s.metrics.IncrementMemCacheHitCounter(c.Name())
}
return nil
}
if err != cache.ErrKeyNotFound {
s.logger.Warn("Error while reading from cache", mlog.Err(err), mlog.String("cache_name", c.Name()))
}
if s.metrics != nil {
s.metrics.IncrementMemCacheMissCounter(c.Name())
}
return err
}
func (s *LocalCacheStore) doMultiReadCache(cache cache.Cache, keys []string, values []any) []error {
errs := cache.GetMulti(keys, values)
if s.metrics != nil {
for _, err := range errs {
if err == nil {
s.metrics.IncrementMemCacheHitCounter(cache.Name())
continue
}
s.metrics.IncrementMemCacheMissCounter(cache.Name())
}
}
return errs
}
func (s *LocalCacheStore) doIncrementCache(cache cache.ExternalCache, key string, val int) {
err := cache.Increment(key, val)
if err != nil {
s.logger.Warn("Error while incrementing cache entry", mlog.Err(err), mlog.String("cache_name", cache.Name()))
}
}
func (s *LocalCacheStore) doDecrementCache(cache cache.ExternalCache, key string, val int) {
err := cache.Decrement(key, val)
if err != nil {
s.logger.Warn("Error while decrementing cache entry", mlog.Err(err), mlog.String("cache_name", cache.Name()))
}
}
func (s *LocalCacheStore) doClearCacheCluster(cache cache.Cache) {
err := cache.Purge()
if err != nil {
s.logger.Warn("Error while purging cache", mlog.Err(err), mlog.String("cache_name", cache.Name()))
}
if s.cluster != nil && cache.GetInvalidateClusterEvent() != model.ClusterEventNone {
msg := &model.ClusterMessage{
Event: cache.GetInvalidateClusterEvent(),
SendType: model.ClusterSendBestEffort,
Data: clearCacheMessageData,
}
s.cluster.SendClusterMessage(msg)
}
}
func (s *LocalCacheStore) Invalidate() {
s.doClearCacheCluster(s.reactionCache)
s.doClearCacheCluster(s.schemeCache)
s.doClearCacheCluster(s.roleCache)
s.doClearCacheCluster(s.fileInfoCache)
s.doClearCacheCluster(s.webhookCache)
s.doClearCacheCluster(s.emojiCacheById)
s.doClearCacheCluster(s.emojiIdCacheByName)
s.doClearCacheCluster(s.channelMemberCountsCache)
s.doClearCacheCluster(s.channelPinnedPostCountsCache)
s.doClearCacheCluster(s.channelGuestCountCache)
s.doClearCacheCluster(s.channelByIdCache)
s.doClearCacheCluster(s.channelMembersForUserCache)
s.doClearCacheCluster(s.channelMembersNotifyPropsCache)
s.doClearCacheCluster(s.channelByNameCache)
s.doClearCacheCluster(s.postLastPostsCache)
s.doClearCacheCluster(s.termsOfServiceCache)
s.doClearCacheCluster(s.lastPostTimeCache)
s.doClearCacheCluster(s.userProfileByIdsCache)
s.doClearCacheCluster(s.allUserCache)
s.doClearCacheCluster(s.profilesInChannelCache)
s.doClearCacheCluster(s.teamAllTeamIdsForUserCache)
s.doClearCacheCluster(s.rolePermissionsCache)
s.doClearCacheCluster(s.userAutoTranslationCache)
s.doClearCacheCluster(s.postTranslationEtagCache)
s.doClearCacheCluster(s.readReceiptCache)
s.doClearCacheCluster(s.readReceiptPostReadersCache)
s.doClearCacheCluster(s.temporaryPostCache)
s.doClearCacheCluster(s.sessionAttributeCache)
s.doClearCacheCluster(s.propertyFieldCache)
}
// allocateCacheTargets is used to fill target value types
// for getting items from cache.
func allocateCacheTargets[T any](l int) []any {
toPass := make([]any, 0, l)
for range l {
toPass = append(toPass, new(T))
}
return toPass
}