MM-25115- migrate post, session and status cache to cache2 (#14667)
* use cache2.Provider cache * use cache2.Provider cache * clean up imports * reset test runner * add close() call * Fixing i18n * address review comments Co-authored-by: Jesús Espino <jespinog@gmail.com> Co-authored-by: mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
18
app/post.go
18
app/post.go
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||||
"github.com/mattermost/mattermost-server/v5/model"
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
"github.com/mattermost/mattermost-server/v5/plugin"
|
"github.com/mattermost/mattermost-server/v5/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/v5/services/cache2"
|
||||||
"github.com/mattermost/mattermost-server/v5/store"
|
"github.com/mattermost/mattermost-server/v5/store"
|
||||||
"github.com/mattermost/mattermost-server/v5/utils"
|
"github.com/mattermost/mattermost-server/v5/utils"
|
||||||
)
|
)
|
||||||
@@ -111,15 +112,16 @@ func (a *App) deduplicateCreatePost(post *model.Post) (foundPost *model.Post, er
|
|||||||
|
|
||||||
// Query the cache atomically for the given pending post id, saving a record if
|
// Query the cache atomically for the given pending post id, saving a record if
|
||||||
// it hasn't previously been seen.
|
// it hasn't previously been seen.
|
||||||
value, loaded := a.Srv().seenPendingPostIdsCache.GetOrAdd(post.PendingPostId, unknownPostId, PENDING_POST_IDS_CACHE_TTL)
|
var postId string
|
||||||
|
nErr := a.Srv().seenPendingPostIdsCache.Get(post.PendingPostId, &postId)
|
||||||
// If we were the first thread to save this pending post id into the cache,
|
if nErr == cache2.ErrKeyNotFound {
|
||||||
// proceed with create post normally.
|
a.Srv().seenPendingPostIdsCache.SetWithExpiry(post.PendingPostId, unknownPostId, PENDING_POST_IDS_CACHE_TTL)
|
||||||
if !loaded {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
postId := value.(string)
|
if nErr != nil {
|
||||||
|
return nil, model.NewAppError("errorGetPostId", "api.post.error_get_post_id.pending", nil, "", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
// If another thread saved the cache record, but hasn't yet updated it with the actual post
|
// If another thread saved the cache record, but hasn't yet updated it with the actual post
|
||||||
// id (because it's still saving), notify the client with an error. Ideally, we'd wait
|
// id (because it's still saving), notify the client with an error. Ideally, we'd wait
|
||||||
@@ -161,7 +163,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Srv().seenPendingPostIdsCache.AddWithExpiresInSecs(post.PendingPostId, savedPost.Id, int64(PENDING_POST_IDS_CACHE_TTL.Seconds()))
|
a.Srv().seenPendingPostIdsCache.SetWithExpiry(post.PendingPostId, savedPost.Id, PENDING_POST_IDS_CACHE_TTL)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
post.SanitizeProps()
|
post.SanitizeProps()
|
||||||
@@ -288,7 +290,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
|
|||||||
|
|
||||||
// Update the mapping from pending post id to the actual post id, for any clients that
|
// Update the mapping from pending post id to the actual post id, for any clients that
|
||||||
// might be duplicating requests.
|
// might be duplicating requests.
|
||||||
a.Srv().seenPendingPostIdsCache.AddWithExpiresInSecs(post.PendingPostId, rpost.Id, int64(PENDING_POST_IDS_CACHE_TTL.Seconds()))
|
a.Srv().seenPendingPostIdsCache.SetWithExpiry(post.PendingPostId, rpost.Id, PENDING_POST_IDS_CACHE_TTL)
|
||||||
|
|
||||||
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
|
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
|
||||||
a.Srv().Go(func() {
|
a.Srv().Go(func() {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v5/plugin"
|
"github.com/mattermost/mattermost-server/v5/plugin"
|
||||||
"github.com/mattermost/mattermost-server/v5/services/cache"
|
"github.com/mattermost/mattermost-server/v5/services/cache"
|
||||||
"github.com/mattermost/mattermost-server/v5/services/cache/lru"
|
"github.com/mattermost/mattermost-server/v5/services/cache/lru"
|
||||||
|
"github.com/mattermost/mattermost-server/v5/services/cache2"
|
||||||
"github.com/mattermost/mattermost-server/v5/services/filesstore"
|
"github.com/mattermost/mattermost-server/v5/services/filesstore"
|
||||||
"github.com/mattermost/mattermost-server/v5/services/httpservice"
|
"github.com/mattermost/mattermost-server/v5/services/httpservice"
|
||||||
"github.com/mattermost/mattermost-server/v5/services/imageproxy"
|
"github.com/mattermost/mattermost-server/v5/services/imageproxy"
|
||||||
@@ -105,9 +106,9 @@ type Server struct {
|
|||||||
newStore func() store.Store
|
newStore func() store.Store
|
||||||
|
|
||||||
htmlTemplateWatcher *utils.HTMLTemplateWatcher
|
htmlTemplateWatcher *utils.HTMLTemplateWatcher
|
||||||
sessionCache cache.Cache
|
sessionCache cache2.Cache
|
||||||
seenPendingPostIdsCache cache.Cache
|
seenPendingPostIdsCache cache2.Cache
|
||||||
statusCache cache.Cache
|
statusCache cache2.Cache
|
||||||
configListenerId string
|
configListenerId string
|
||||||
licenseListenerId string
|
licenseListenerId string
|
||||||
logListenerId string
|
logListenerId string
|
||||||
@@ -158,6 +159,8 @@ type Server struct {
|
|||||||
|
|
||||||
CacheProvider cache.Provider
|
CacheProvider cache.Provider
|
||||||
|
|
||||||
|
CacheProvider2 cache2.Provider
|
||||||
|
|
||||||
tracer *tracing.Tracer
|
tracer *tracing.Tracer
|
||||||
timestampLastDiagnosticSent time.Time
|
timestampLastDiagnosticSent time.Time
|
||||||
}
|
}
|
||||||
@@ -256,9 +259,20 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
|
|
||||||
s.CacheProvider.Connect()
|
s.CacheProvider.Connect()
|
||||||
|
|
||||||
s.sessionCache = s.CacheProvider.NewCache(model.SESSION_CACHE_SIZE)
|
s.CacheProvider2 = cache2.NewProvider()
|
||||||
s.seenPendingPostIdsCache = s.CacheProvider.NewCache(PENDING_POST_IDS_CACHE_SIZE)
|
if err := s.CacheProvider2.Connect(); err != nil {
|
||||||
s.statusCache = s.CacheProvider.NewCache(model.STATUS_CACHE_SIZE)
|
return nil, errors.Wrapf(err, "Unable to connect to cache provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.sessionCache = s.CacheProvider2.NewCache(&cache2.CacheOptions{
|
||||||
|
Size: model.SESSION_CACHE_SIZE,
|
||||||
|
})
|
||||||
|
s.seenPendingPostIdsCache = s.CacheProvider2.NewCache(&cache2.CacheOptions{
|
||||||
|
Size: PENDING_POST_IDS_CACHE_SIZE,
|
||||||
|
})
|
||||||
|
s.statusCache = s.CacheProvider2.NewCache(&cache2.CacheOptions{
|
||||||
|
Size: model.STATUS_CACHE_SIZE,
|
||||||
|
})
|
||||||
|
|
||||||
if err := s.RunOldAppInitialization(); err != nil {
|
if err := s.RunOldAppInitialization(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -486,6 +500,12 @@ func (s *Server) Shutdown() error {
|
|||||||
s.CacheProvider.Close()
|
s.CacheProvider.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.CacheProvider2 != nil {
|
||||||
|
if err = s.CacheProvider2.Close(); err != nil {
|
||||||
|
mlog.Error("Unable to cleanly shutdown cache", mlog.Err(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mlog.Info("Server stopped")
|
mlog.Info("Server stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/audit"
|
"github.com/mattermost/mattermost-server/v5/audit"
|
||||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||||
@@ -30,8 +31,7 @@ func (a *App) GetSession(token string) (*model.Session, *model.AppError) {
|
|||||||
|
|
||||||
var session *model.Session
|
var session *model.Session
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if ts, ok := a.Srv().sessionCache.Get(token); ok {
|
if err := a.Srv().sessionCache.Get(token, &session); err == nil {
|
||||||
session = ts.(*model.Session)
|
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
metrics.IncrementMemCacheHitCounterSession()
|
metrics.IncrementMemCacheHitCounterSession()
|
||||||
}
|
}
|
||||||
@@ -178,15 +178,15 @@ func (a *App) ClearSessionCacheForAllUsers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
|
func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
|
||||||
keys := a.Srv().sessionCache.Keys()
|
if keys, err := a.Srv().sessionCache.Keys(); err == nil {
|
||||||
|
var session *model.Session
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if ts, ok := a.Srv().sessionCache.Get(key); ok {
|
if err := a.Srv().sessionCache.Get(key, &session); err == nil {
|
||||||
session := ts.(*model.Session)
|
if session.UserId == userId {
|
||||||
if session.UserId == userId {
|
a.Srv().sessionCache.Remove(key)
|
||||||
a.Srv().sessionCache.Remove(key)
|
if a.Metrics() != nil {
|
||||||
if a.Metrics() != nil {
|
a.Metrics().IncrementMemCacheInvalidationCounterSession()
|
||||||
a.Metrics().IncrementMemCacheInvalidationCounterSession()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,11 +201,14 @@ func (a *App) ClearSessionCacheForAllUsersSkipClusterSend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AddSessionToCache(session *model.Session) {
|
func (a *App) AddSessionToCache(session *model.Session) {
|
||||||
a.Srv().sessionCache.AddWithExpiresInSecs(session.Token, session, int64(*a.Config().ServiceSettings.SessionCacheInMinutes*60))
|
a.Srv().sessionCache.SetWithExpiry(session.Token, session, time.Duration(int64(*a.Config().ServiceSettings.SessionCacheInMinutes))*time.Minute)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SessionCacheLength() int {
|
func (a *App) SessionCacheLength() int {
|
||||||
return a.Srv().sessionCache.Len()
|
if l, err := a.Srv().sessionCache.Len(); err == nil {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) RevokeSessionsForDeviceId(userId string, deviceId string, currentSessionId string) *model.AppError {
|
func (a *App) RevokeSessionsForDeviceId(userId string, deviceId string, currentSessionId string) *model.AppError {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/model"
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -28,21 +29,24 @@ func TestCache(t *testing.T) {
|
|||||||
UserId: model.NewId(),
|
UserId: model.NewId(),
|
||||||
}
|
}
|
||||||
|
|
||||||
th.App.Srv().sessionCache.AddWithExpiresInSecs(session.Token, session, 5*60)
|
th.App.Srv().sessionCache.SetWithExpiry(session.Token, session, 5*time.Minute)
|
||||||
th.App.Srv().sessionCache.AddWithExpiresInSecs(session2.Token, session2, 5*60)
|
th.App.Srv().sessionCache.SetWithExpiry(session2.Token, session2, 5*time.Minute)
|
||||||
|
|
||||||
keys := th.App.Srv().sessionCache.Keys()
|
keys, err := th.App.Srv().sessionCache.Keys()
|
||||||
|
require.Nil(t, err)
|
||||||
require.NotEmpty(t, keys)
|
require.NotEmpty(t, keys)
|
||||||
|
|
||||||
th.App.ClearSessionCacheForUser(session.UserId)
|
th.App.ClearSessionCacheForUser(session.UserId)
|
||||||
|
|
||||||
rkeys := th.App.Srv().sessionCache.Keys()
|
rkeys, err := th.App.Srv().sessionCache.Keys()
|
||||||
|
require.Nil(t, err)
|
||||||
require.Lenf(t, rkeys, len(keys)-1, "should have one less: %d - %d != 1", len(keys), len(rkeys))
|
require.Lenf(t, rkeys, len(keys)-1, "should have one less: %d - %d != 1", len(keys), len(rkeys))
|
||||||
require.NotEmpty(t, rkeys)
|
require.NotEmpty(t, rkeys)
|
||||||
|
|
||||||
th.App.ClearSessionCacheForAllUsers()
|
th.App.ClearSessionCacheForAllUsers()
|
||||||
|
|
||||||
rkeys = th.App.Srv().sessionCache.Keys()
|
rkeys, err = th.App.Srv().sessionCache.Keys()
|
||||||
|
require.Nil(t, err)
|
||||||
require.Empty(t, rkeys)
|
require.Empty(t, rkeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,9 +295,9 @@ func TestApp_ExtendExpiryIfNeeded(t *testing.T) {
|
|||||||
require.False(t, session.IsExpired())
|
require.False(t, session.IsExpired())
|
||||||
|
|
||||||
// check cache was updated
|
// check cache was updated
|
||||||
ts, ok := th.App.Srv().sessionCache.Get(session.Token)
|
var cachedSession *model.Session
|
||||||
require.True(t, ok)
|
errGet := th.App.Srv().sessionCache.Get(session.Token, &cachedSession)
|
||||||
cachedSession := ts.(*model.Session)
|
require.Nil(t, errGet)
|
||||||
require.Equal(t, session.ExpiresAt, cachedSession.ExpiresAt)
|
require.Equal(t, session.ExpiresAt, cachedSession.ExpiresAt)
|
||||||
|
|
||||||
// check database was updated.
|
// check database was updated.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (a *App) AddStatusCacheSkipClusterSend(status *model.Status) {
|
func (a *App) AddStatusCacheSkipClusterSend(status *model.Status) {
|
||||||
a.Srv().statusCache.Add(status.UserId, status)
|
a.Srv().statusCache.Set(status.UserId, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AddStatusCache(status *model.Status) {
|
func (a *App) AddStatusCache(status *model.Status) {
|
||||||
@@ -30,16 +30,15 @@ func (a *App) GetAllStatuses() map[string]*model.Status {
|
|||||||
return map[string]*model.Status{}
|
return map[string]*model.Status{}
|
||||||
}
|
}
|
||||||
|
|
||||||
userIds := a.Srv().statusCache.Keys()
|
|
||||||
statusMap := map[string]*model.Status{}
|
statusMap := map[string]*model.Status{}
|
||||||
|
if userIds, err := a.Srv().statusCache.Keys(); err == nil {
|
||||||
for _, userId := range userIds {
|
for _, userId := range userIds {
|
||||||
status := a.GetStatusFromCache(userId)
|
status := a.GetStatusFromCache(userId)
|
||||||
if status != nil {
|
if status != nil {
|
||||||
statusMap[userId] = status
|
statusMap[userId] = status
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusMap
|
return statusMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +52,9 @@ func (a *App) GetStatusesByIds(userIds []string) (map[string]interface{}, *model
|
|||||||
|
|
||||||
missingUserIds := []string{}
|
missingUserIds := []string{}
|
||||||
for _, userId := range userIds {
|
for _, userId := range userIds {
|
||||||
if result, ok := a.Srv().statusCache.Get(userId); ok {
|
var status *model.Status
|
||||||
statusMap[userId] = result.(*model.Status).Status
|
if err := a.Srv().statusCache.Get(userId, &status); err == nil {
|
||||||
|
statusMap[userId] = status.Status
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
metrics.IncrementMemCacheHitCounter("Status")
|
metrics.IncrementMemCacheHitCounter("Status")
|
||||||
}
|
}
|
||||||
@@ -100,8 +100,9 @@ func (a *App) GetUserStatusesByIds(userIds []string) ([]*model.Status, *model.Ap
|
|||||||
|
|
||||||
missingUserIds := []string{}
|
missingUserIds := []string{}
|
||||||
for _, userId := range userIds {
|
for _, userId := range userIds {
|
||||||
if result, ok := a.Srv().statusCache.Get(userId); ok {
|
var status *model.Status
|
||||||
statusMap = append(statusMap, result.(*model.Status))
|
if err := a.Srv().statusCache.Get(userId, &status); err == nil {
|
||||||
|
statusMap = append(statusMap, status)
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
metrics.IncrementMemCacheHitCounter("Status")
|
metrics.IncrementMemCacheHitCounter("Status")
|
||||||
}
|
}
|
||||||
@@ -321,8 +322,8 @@ func (a *App) SetStatusOutOfOffice(userId string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetStatusFromCache(userId string) *model.Status {
|
func (a *App) GetStatusFromCache(userId string) *model.Status {
|
||||||
if result, ok := a.Srv().statusCache.Get(userId); ok {
|
var status *model.Status
|
||||||
status := result.(*model.Status)
|
if err := a.Srv().statusCache.Get(userId, &status); err == nil {
|
||||||
statusCopy := &model.Status{}
|
statusCopy := &model.Status{}
|
||||||
*statusCopy = *status
|
*statusCopy = *status
|
||||||
return statusCopy
|
return statusCopy
|
||||||
|
|||||||
@@ -1692,6 +1692,10 @@
|
|||||||
"id": "api.post.do_action.action_integration.app_error",
|
"id": "api.post.do_action.action_integration.app_error",
|
||||||
"translation": "Action integration error."
|
"translation": "Action integration error."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.post.error_get_post_id.pending",
|
||||||
|
"translation": "Unable to get the pending post."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.post.get_message_for_notification.files_sent",
|
"id": "api.post.get_message_for_notification.files_sent",
|
||||||
"translation": {
|
"translation": {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user