merged from gramsrv upstream
This commit is contained in:
parent
79c64ee916
commit
21a0856587
651 changed files with 54774 additions and 4590 deletions
174
internal/store/postgres/channel_difference_cache.go
Normal file
174
internal/store/postgres/channel_difference_cache.go
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/readmodelcache"
|
||||
)
|
||||
|
||||
type channelDifferenceBaseKey struct {
|
||||
channelID int64
|
||||
requestPts int
|
||||
capturedPts int
|
||||
capturedTopID int
|
||||
limit int
|
||||
}
|
||||
|
||||
// channelDifferenceBase contains only viewer-independent durable facts. Access,
|
||||
// available-min, monoforum visibility, unread flags and dialog state are applied
|
||||
// after the cache lookup by ListChannelDifference.
|
||||
type channelDifferenceBase struct {
|
||||
retainedThroughPts int
|
||||
lastPts int
|
||||
tooLong bool
|
||||
events []domain.ChannelUpdateEvent
|
||||
messages []domain.ChannelMessage
|
||||
// mentionCandidateIDs is a viewer-independent sparse gate sourced from
|
||||
// channel_unread_mention_index. When candidatesKnown is true, messages not
|
||||
// present in this set cannot have a viewer mention overlay and must not cause
|
||||
// a channel_unread_mentions query.
|
||||
mentionCandidateIDs map[int]struct{}
|
||||
candidatesKnown bool
|
||||
}
|
||||
|
||||
type ChannelDifferenceCacheSnapshot struct {
|
||||
Entries int
|
||||
Weight int64
|
||||
Hits uint64
|
||||
Misses uint64
|
||||
Loads uint64
|
||||
LoadErrors uint64
|
||||
}
|
||||
|
||||
// ChannelDifferenceBaseCache deduplicates immutable channel event/message pages
|
||||
// shared by many viewers catching up from the same cursor. It never stores a
|
||||
// permission decision or a final ChannelDifference response.
|
||||
type ChannelDifferenceBaseCache struct {
|
||||
cache *readmodelcache.Cache[channelDifferenceBaseKey, channelDifferenceBase]
|
||||
|
||||
hits atomic.Uint64
|
||||
misses atomic.Uint64
|
||||
loads atomic.Uint64
|
||||
loadErrors atomic.Uint64
|
||||
}
|
||||
|
||||
func NewChannelDifferenceBaseCache(maxEntries int, maxWeight int64, ttl time.Duration) *ChannelDifferenceBaseCache {
|
||||
cache := readmodelcache.New[channelDifferenceBaseKey, channelDifferenceBase](readmodelcache.Config[channelDifferenceBaseKey, channelDifferenceBase]{
|
||||
MaxEntries: maxEntries,
|
||||
MaxWeight: maxWeight,
|
||||
TTL: ttl,
|
||||
Clone: cloneChannelDifferenceBase,
|
||||
Weight: channelDifferenceBaseWeight,
|
||||
KeyString: func(key channelDifferenceBaseKey) string {
|
||||
return strconv.FormatInt(key.channelID, 10) + ":" +
|
||||
strconv.Itoa(key.requestPts) + ":" + strconv.Itoa(key.capturedPts) + ":" +
|
||||
strconv.Itoa(key.capturedTopID) + ":" + strconv.Itoa(key.limit)
|
||||
},
|
||||
})
|
||||
if cache == nil {
|
||||
return nil
|
||||
}
|
||||
return &ChannelDifferenceBaseCache{cache: cache}
|
||||
}
|
||||
|
||||
func (c *ChannelDifferenceBaseCache) getOrLoad(
|
||||
ctx context.Context,
|
||||
key channelDifferenceBaseKey,
|
||||
load func() (channelDifferenceBase, error),
|
||||
) (channelDifferenceBase, error) {
|
||||
if c == nil {
|
||||
return load()
|
||||
}
|
||||
if _, ok := c.cache.Peek(key); ok {
|
||||
c.hits.Add(1)
|
||||
} else {
|
||||
c.misses.Add(1)
|
||||
}
|
||||
return c.cache.GetOrLoad(ctx, key, func() (channelDifferenceBase, error) {
|
||||
c.loads.Add(1)
|
||||
value, err := load()
|
||||
if err != nil {
|
||||
c.loadErrors.Add(1)
|
||||
}
|
||||
return value, err
|
||||
})
|
||||
}
|
||||
|
||||
func (c *ChannelDifferenceBaseCache) deleteChannel(channelID int64) {
|
||||
if c == nil || channelID == 0 {
|
||||
return
|
||||
}
|
||||
c.cache.InvalidateWhere(func(key channelDifferenceBaseKey) bool { return key.channelID == channelID })
|
||||
}
|
||||
|
||||
func (c *ChannelDifferenceBaseCache) flush() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.cache.Flush()
|
||||
}
|
||||
|
||||
func (c *ChannelDifferenceBaseCache) Snapshot() ChannelDifferenceCacheSnapshot {
|
||||
if c == nil {
|
||||
return ChannelDifferenceCacheSnapshot{}
|
||||
}
|
||||
return ChannelDifferenceCacheSnapshot{
|
||||
Entries: c.cache.Len(),
|
||||
Weight: c.cache.Weight(),
|
||||
Hits: c.hits.Load(),
|
||||
Misses: c.misses.Load(),
|
||||
Loads: c.loads.Load(),
|
||||
LoadErrors: c.loadErrors.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
func cloneChannelDifferenceBase(base channelDifferenceBase) channelDifferenceBase {
|
||||
candidates := base.mentionCandidateIDs
|
||||
base.events = append([]domain.ChannelUpdateEvent(nil), base.events...)
|
||||
for i := range base.events {
|
||||
base.events[i].MessageIDs = append([]int(nil), base.events[i].MessageIDs...)
|
||||
base.events[i].UserIDs = append([]int64(nil), base.events[i].UserIDs...)
|
||||
base.events[i].Message = cloneChannelTopMessage(base.events[i].Message)
|
||||
}
|
||||
base.messages = append([]domain.ChannelMessage(nil), base.messages...)
|
||||
for i := range base.messages {
|
||||
base.messages[i] = cloneChannelTopMessage(base.messages[i])
|
||||
}
|
||||
if base.mentionCandidateIDs != nil {
|
||||
base.mentionCandidateIDs = make(map[int]struct{}, len(base.mentionCandidateIDs))
|
||||
for id := range candidates {
|
||||
base.mentionCandidateIDs[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func channelDifferenceBaseWeight(base channelDifferenceBase) int64 {
|
||||
weight := int64(96 + len(base.events)*192 + len(base.messages)*192 + len(base.mentionCandidateIDs)*16)
|
||||
for _, event := range base.events {
|
||||
weight += int64(len(event.MessageIDs)*8 + len(event.UserIDs)*8)
|
||||
weight += channelDifferenceMessageWeight(event.Message)
|
||||
}
|
||||
for _, message := range base.messages {
|
||||
weight += channelDifferenceMessageWeight(message)
|
||||
}
|
||||
return weight
|
||||
}
|
||||
|
||||
func channelDifferenceMessageWeight(message domain.ChannelMessage) int64 {
|
||||
if message.ID == 0 {
|
||||
return 0
|
||||
}
|
||||
weight := int64(len(message.Body) + len(message.PostAuthor) + len(message.Entities)*48)
|
||||
if message.RichMessage != nil {
|
||||
weight += int64(len(message.RichMessage.Blocks) + len(message.RichMessage.BotAPIProjection))
|
||||
}
|
||||
if message.Action != nil {
|
||||
weight += int64(len(message.Action.Title) + len(message.Action.UserIDs)*8 + len(message.Action.TodoItems)*64)
|
||||
}
|
||||
return weight
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue