Files
d7618cc982 Migrate content flagging settings to database (#33989)
* lint fix

* CI

* added new migration mocks

* Used setup for tests

* some comment

* Removed unnecesseery nil check

* Form validation

* WIP tests

* WIP tests

* WIP tests

* fix: mock content flagging config selector with correct reasons format

* fix: add mock for getContentFlaggingConfig in flag post modal test

* Updated error code order in API docs

* removed empty files

* Added tests

* lint fixes

* minor tweak

* lint fix

* type fix

* fixed test

* nit

* test enhancements

* API WIP

* API WIP

* creating values

* creating content flagging channel and properties

* Able to save properties

* Added another property field

* WIP

* WIP

* Added validations

* Added data validations and hidden post if confifgured to

* lint fixes

* Added API spec

* Added some tests

* Added tests for getContentReviewBot

* test: add comprehensive tests for getContentReviewChannels function

* Added more app layer tests

* Added TestCanFlagPost

* test: Add comprehensive tests for FlagPost function

* Added all app layer tests

* Removed a file that was reamoved downstream

* test: add content flagging test file

* test: add comprehensive tests for FlagContentRequest.IsValid method

* Added model tests

* test: add comprehensive tests for SqlPropertyValueStore.CreateMany

* test: add comprehensive tests for flagPost() API function

* Added API tests

* linter fix

* WIP

* sent post flagging confirmation message

* fixed i18n nissues

* fixed i18n nissues

* CI

* WIP

* WIP

* Added API call

* test: add test for Client4.flagPost API call in FlagPostModal

* fix: remove userEvent.setup() from flag post modal test

* test: wrap submit button click in act for proper state updates

* Updated tests

* lint fix

* Updated test

* fix: reset contentFlaggingGroupId for test isolation in content flagging tests

* removed cached group ID

* removed debug log

* CI

* Updated to allow special characters in comments

* Handled empty comment

* Created getContentFlaggingFields API

* created getPostPropertyValues API

* Used finally

* WIP

* Created useContentFlaggingFields hook

* WIP

* WIP

* Added option to retain data for reviewers

* Displayed deleted post's preview

* DIsplayed all properties

* Adding field name i18n

* WIP - managing i18n able texts

* Finished displaying all fields

* Manual cleanup

* lint fixes

* team role filter logic fix

* Fixed tests

* created new API to fetch flagged posts

* lint fix

* Added new client methods

* test: add comprehensive tests for content flagging APIs

* Added new API tests

* fixed openapi spec

* Fixed DataSpillageReport tests

* Fixed PostMarkdown test

* Fixed PostPreviewPropertyRenderer test

* Added metadata to card renderer

* test fixes

* Added no comment placeholder

* Added view detail button

* Created RemoveFlaggedMessageConfirmationModal modal

* Added key and remove flag request modal

* IMplemented delete flagged post

* Handled edge cases of deleting flagged post

* keep message

* UI integration

* Added WS event for post report update and handled deleted files of flagged post

* Added error handling in keep/remove forms

* i18n fixes

* Fixed test

* Updated OpenAPI specs

* fixed types

* fixed types

* refactoring

* refactor: improve test mocking for data spillage report component

* test mock updates

* Fixed tests

* Updated reducer

* not resetting mocks

* Added migrations for content flagging tables

* Created new structure

* review fixes

* Used correct ot name

* WIP

* review fixes

* review fixes

* Added new property translations

* CI

* CI

* CI

* Improved test

* fixed test

* CI

* New UI component

* WIP

* Updated settings APIs

* cached DB data

* used cached reviewer data

* Updated tests

* Lint fixes

* test: add tests for saveContentFlaggingSettings and getContentFlaggingSettings APIs

* test fix

* test: add tests for SaveContentFlaggingConfig and GetContentFlaggingConfigReviewerIDs

* Updated tests

* test: add content flagging test for local cache layer

* test: add comprehensive tests for content flagging store cache

* Updated tests

* lint fix

* Updated mobile text

* Added content flagging SQL store mocks

* Added API specs for new APIs

* fixed tests

* feat: add TestContentFlaggingStore function for content flagging store testing

* feat: add comprehensive tests for content flagging store

* Added SQL store tests

* test: add content flagging test for local cache layer

* test: add tests for content flagging store caching

* Added cache layer tests

* Updated tests

* Fixed

* Handled JSON error

* fixes

* fixes

* Fixed retry layer test

* fixerdf i18n

* Fixed test

* CI

* building index concurrently

* CI

* fixed a test

* CI

* cleanup

* Integrate flag post api (#33798)

* WIP

* WIP

* Added API call

* test: add test for Client4.flagPost API call in FlagPostModal

* fix: remove userEvent.setup() from flag post modal test

* test: wrap submit button click in act for proper state updates

* Updated tests

* lint fix

* CI

* Updated to allow special characters in comments

* Handled empty comment

* Used finally

* CI

* Fixed test

* Spillage card integration (#33832)

* Created getContentFlaggingFields API

* created getPostPropertyValues API

* WIP

* Created useContentFlaggingFields hook

* WIP

* WIP

* Added option to retain data for reviewers

* Displayed deleted post's preview

* DIsplayed all properties

* Adding field name i18n

* WIP - managing i18n able texts

* Finished displaying all fields

* Manual cleanup

* lint fixes

* team role filter logic fix

* Fixed tests

* created new API to fetch flagged posts

* lint fix

* Added new client methods

* test: add comprehensive tests for content flagging APIs

* Added new API tests

* fixed openapi spec

* Fixed DataSpillageReport tests

* Fixed PostMarkdown test

* Fixed PostPreviewPropertyRenderer test

* Added metadata to card renderer

* test fixes

* Added no comment placeholder

* Fixed test

* refactor: improve test mocking for data spillage report component

* test mock updates

* Updated reducer

* not resetting mocks

* WIP

* review fixes

* CI

* Fixed

* fixes

* Content flagging actions implementation (#33852)

* Added view detail button

* Created RemoveFlaggedMessageConfirmationModal modal

* Added key and remove flag request modal

* IMplemented delete flagged post

* Handled edge cases of deleting flagged post

* keep message

* UI integration

* Added WS event for post report update and handled deleted files of flagged post

* Added error handling in keep/remove forms

* i18n fixes

* Updated OpenAPI specs

* fixed types

* fixed types

* refactoring

* Fixed tests

* review fixes

* Added new property translations

* Improved test

* fixed test

* CI

* fixes

* CI

* fixed a test

* CI

* Review fixes

---------

Co-authored-by: Hanzo Dev <[email protected]>
2025-10-13 12:24:01 +05:30

604 lines
23 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
ContentFlaggingCacheSize = 100
)
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
contentFlagging LocalCacheContentFlaggingStore
contentFlaggingCache 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}
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}
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.ClusterEventInvalidateCacheForContentFlagging, localCacheStore.contentFlagging.handleClusterInvalidateContentFlagging)
}
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) ContentFlagging() store.ContentFlaggingStore {
return s.contentFlagging
}
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)
}
// 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
}