mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
The server module named github.com/hanzoai/team/server/v8 — a path that resolves to the retired workspace's repository, not this one — while server/public still published github.com/mattermost/mattermost/server/public. Both are github.com/hanzoteam/server/... now, which is where this code actually lives. That second half is what made the binary unbootable. Upstream's public path stayed claimed by upstream, mattermost-plugin-ai imported it, and the build linked public/model twice; public/plugin's gob.Register then ran twice under one name and panicked at init, before main. Reverting the rename traded the brand for a working binary. Rebranding the plugin alongside it needs no such trade: the agents fork is github.com/hanzoteam/agents/v2 against our public module, so no module named mattermost/mattermost is in the graph at all. Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and it is ours.
189 lines
7.4 KiB
Go
189 lines
7.4 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package localcachelayer
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/hanzoteam/server/server/public/model"
|
|
"github.com/hanzoteam/server/server/public/shared/mlog"
|
|
"github.com/hanzoteam/server/server/public/shared/request"
|
|
"github.com/hanzoteam/server/server/v8/channels/store"
|
|
"github.com/hanzoteam/server/server/v8/platform/services/cache"
|
|
)
|
|
|
|
type LocalCacheReadReceiptStore struct {
|
|
store.ReadReceiptStore
|
|
rootStore *LocalCacheStore
|
|
}
|
|
|
|
func (s *LocalCacheReadReceiptStore) handleClusterInvalidateReadReceipts(msg *model.ClusterMessage) {
|
|
if err := s.rootStore.readReceiptCache.Purge(); err != nil {
|
|
s.rootStore.logger.Error("failed to purge read receipt cache", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) ClearCaches() {
|
|
if err := s.rootStore.readReceiptCache.Purge(); err != nil {
|
|
s.rootStore.logger.Error("failed to purge read receipt cache", mlog.Err(err))
|
|
}
|
|
if err := s.rootStore.readReceiptPostReadersCache.Purge(); err != nil {
|
|
s.rootStore.logger.Error("failed to purge read receipt post readers cache", mlog.Err(err))
|
|
}
|
|
if err := s.rootStore.readReceiptPostUnreadCountCache.Purge(); err != nil {
|
|
s.rootStore.logger.Error("failed to purge read receipt post unread count cache", mlog.Err(err))
|
|
}
|
|
|
|
if s.rootStore.metrics != nil {
|
|
s.rootStore.metrics.IncrementMemCacheInvalidationCounter(s.rootStore.readReceiptCache.Name())
|
|
s.rootStore.metrics.IncrementMemCacheInvalidationCounter(s.rootStore.readReceiptPostReadersCache.Name())
|
|
s.rootStore.metrics.IncrementMemCacheInvalidationCounter(s.rootStore.readReceiptPostUnreadCountCache.Name())
|
|
}
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) InvalidateReadReceiptForPostsCache(postID string) {
|
|
s.rootStore.doInvalidateCacheCluster(s.rootStore.readReceiptPostReadersCache, postID, nil)
|
|
s.rootStore.doInvalidateCacheCluster(s.rootStore.readReceiptPostUnreadCountCache, postID, nil)
|
|
if s.rootStore.metrics != nil {
|
|
s.rootStore.metrics.IncrementMemCacheInvalidationCounter(s.rootStore.readReceiptPostReadersCache.Name())
|
|
s.rootStore.metrics.IncrementMemCacheInvalidationCounter(s.rootStore.readReceiptPostUnreadCountCache.Name())
|
|
}
|
|
if externalCache, ok := s.rootStore.readReceiptCache.(cache.ExternalCache); ok {
|
|
// For redis, invalidate all keys with pattern "postID:*"
|
|
s.rootStore.doInvalidateCacheCluster(externalCache, fmt.Sprintf("%s:*", postID), nil)
|
|
} else {
|
|
if err := s.rootStore.readReceiptCache.Purge(); err != nil {
|
|
s.rootStore.logger.Error("failed to purge read receipt cache", mlog.Err(err))
|
|
return
|
|
}
|
|
}
|
|
if s.rootStore.metrics != nil {
|
|
s.rootStore.metrics.IncrementMemCacheInvalidationCounter(s.rootStore.readReceiptCache.Name())
|
|
}
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) Delete(rctx request.CTX, postID, userID string) error {
|
|
defer func() {
|
|
s.InvalidateReadReceiptForPostsCache(postID)
|
|
}()
|
|
return s.ReadReceiptStore.Delete(rctx, postID, userID)
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) DeleteByPost(rctx request.CTX, postID string) error {
|
|
defer func(postID string) {
|
|
s.InvalidateReadReceiptForPostsCache(postID)
|
|
}(postID)
|
|
|
|
return s.ReadReceiptStore.DeleteByPost(rctx, postID)
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) Save(rctx request.CTX, receipt *model.ReadReceipt) (*model.ReadReceipt, error) {
|
|
defer func() {
|
|
s.rootStore.doInvalidateCacheCluster(s.rootStore.readReceiptCache, fmt.Sprintf("%s:%s", receipt.PostID, receipt.UserID), nil)
|
|
s.rootStore.doInvalidateCacheCluster(s.rootStore.readReceiptPostReadersCache, receipt.PostID, nil)
|
|
s.rootStore.doInvalidateCacheCluster(s.rootStore.readReceiptPostUnreadCountCache, receipt.PostID, nil)
|
|
}()
|
|
return s.ReadReceiptStore.Save(rctx, receipt)
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) Update(rctx request.CTX, receipt *model.ReadReceipt) (*model.ReadReceipt, error) {
|
|
defer func() {
|
|
s.rootStore.doInvalidateCacheCluster(s.rootStore.readReceiptCache, fmt.Sprintf("%s:%s", receipt.PostID, receipt.UserID), nil)
|
|
}()
|
|
return s.ReadReceiptStore.Update(rctx, receipt)
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) Get(rctx request.CTX, postID, userID string) (*model.ReadReceipt, error) {
|
|
// no need to store the entire struct in cache, just the expireAt would be sufficient
|
|
// as other two fields are part of the cache key
|
|
var expireAt int64
|
|
if err := s.rootStore.doStandardReadCache(s.rootStore.readReceiptCache, fmt.Sprintf("%s:%s", postID, userID), &expireAt); err == nil {
|
|
return &model.ReadReceipt{
|
|
PostID: postID,
|
|
UserID: userID,
|
|
ExpireAt: expireAt,
|
|
}, nil
|
|
}
|
|
|
|
rr, err := s.ReadReceiptStore.Get(rctx, postID, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.rootStore.doStandardAddToCache(s.rootStore.readReceiptCache, fmt.Sprintf("%s:%s", postID, userID), rr.ExpireAt)
|
|
|
|
// Update post readers cache: add this userID to the list if not already present
|
|
var existingUserIDs []string
|
|
if err := s.rootStore.doStandardReadCache(s.rootStore.readReceiptPostReadersCache, postID, &existingUserIDs); err == nil {
|
|
// Cache exists: check if userID is already in the list
|
|
if !slices.Contains(existingUserIDs, userID) {
|
|
// Add userID to the existing list
|
|
existingUserIDs = append(existingUserIDs, userID)
|
|
s.rootStore.doStandardAddToCache(s.rootStore.readReceiptPostReadersCache, postID, existingUserIDs)
|
|
}
|
|
} else {
|
|
// Cache doesn't exist: create new entry with just this userID
|
|
s.rootStore.doStandardAddToCache(s.rootStore.readReceiptPostReadersCache, postID, []string{userID})
|
|
}
|
|
|
|
return rr, nil
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) GetByPost(rctx request.CTX, postID string) ([]*model.ReadReceipt, error) {
|
|
// Try to get cached user IDs for this post
|
|
var cachedUserIDs []string
|
|
if err := s.rootStore.doStandardReadCache(s.rootStore.readReceiptPostReadersCache, postID, &cachedUserIDs); err == nil {
|
|
// Cache hit: reconstruct receipts from cached user IDs and individual receipt caches
|
|
receipts := make([]*model.ReadReceipt, 0, len(cachedUserIDs))
|
|
for _, userID := range cachedUserIDs {
|
|
var expireAt int64
|
|
if err := s.rootStore.doStandardReadCache(s.rootStore.readReceiptCache, fmt.Sprintf("%s:%s", postID, userID), &expireAt); err == nil {
|
|
receipts = append(receipts, &model.ReadReceipt{
|
|
PostID: postID,
|
|
UserID: userID,
|
|
ExpireAt: expireAt,
|
|
})
|
|
}
|
|
}
|
|
// If we got all receipts from cache, return them
|
|
if len(receipts) == len(cachedUserIDs) {
|
|
return receipts, nil
|
|
}
|
|
}
|
|
|
|
// Cache miss or partial cache: fetch from underlying store
|
|
receipts, err := s.ReadReceiptStore.GetByPost(rctx, postID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Cache the user IDs for this post
|
|
userIDs := make([]string, len(receipts))
|
|
for i, receipt := range receipts {
|
|
userIDs[i] = receipt.UserID
|
|
// Also ensure individual receipts are cached
|
|
s.rootStore.doStandardAddToCache(s.rootStore.readReceiptCache, fmt.Sprintf("%s:%s", postID, receipt.UserID), receipt.ExpireAt)
|
|
}
|
|
s.rootStore.doStandardAddToCache(s.rootStore.readReceiptPostReadersCache, postID, userIDs)
|
|
|
|
return receipts, nil
|
|
}
|
|
|
|
func (s LocalCacheReadReceiptStore) GetUnreadCountForPost(rctx request.CTX, post *model.Post) (int64, error) {
|
|
var count int64
|
|
if err := s.rootStore.doStandardReadCache(s.rootStore.readReceiptPostUnreadCountCache, post.Id, &count); err == nil {
|
|
return count, nil
|
|
}
|
|
|
|
count, err := s.ReadReceiptStore.GetUnreadCountForPost(rctx, post)
|
|
if err != nil {
|
|
return count, err
|
|
}
|
|
|
|
s.rootStore.doStandardAddToCache(s.rootStore.readReceiptPostUnreadCountCache, post.Id, count)
|
|
|
|
return count, nil
|
|
}
|