merged from gramsrv upstream

This commit is contained in:
onysd 2026-09-01 12:06:31 +03:00
parent 79c64ee916
commit 21a0856587
651 changed files with 54774 additions and 4590 deletions

View file

@ -0,0 +1,153 @@
package dialogs
import (
"context"
"time"
"telesrv/internal/domain"
"telesrv/internal/readmodelcache"
)
const (
defaultDialogDraftReadModelTTL = 24 * time.Hour
defaultDialogDraftReadModelMaxEntries = 1000000
defaultDialogDraftReadModelMaxBytes int64 = 256 << 20
)
type dialogDraftCacheEntry struct {
draft domain.DialogDraft
found bool
}
type dialogDraftReadModelCache struct {
cache *readmodelcache.Cache[dialogPeerCacheKey, dialogDraftCacheEntry]
}
func newDialogDraftReadModelCache(maxEntries int, maxBytes int64, ttl time.Duration) *dialogDraftReadModelCache {
if ttl <= 0 {
ttl = defaultDialogDraftReadModelTTL
}
return &dialogDraftReadModelCache{cache: readmodelcache.New[dialogPeerCacheKey, dialogDraftCacheEntry](readmodelcache.Config[dialogPeerCacheKey, dialogDraftCacheEntry]{
MaxEntries: maxEntries,
MaxWeight: maxBytes,
Weight: dialogDraftEntryApproxBytes,
TTL: ttl,
Clone: cloneDialogDraftCacheEntry,
})}
}
func (s *Service) dialogDraftsReadModel(ctx context.Context, userID int64, peers []domain.Peer) (map[domain.Peer]dialogDraftCacheEntry, error) {
out := make(map[domain.Peer]dialogDraftCacheEntry, len(peers))
if s == nil || s.dialogs == nil || userID == 0 || len(peers) == 0 {
return out, nil
}
unique := uniqueDialogPeers(peers)
if len(unique) == 0 {
return out, nil
}
keys := make([]dialogPeerCacheKey, 0, len(unique))
for _, peer := range unique {
keys = append(keys, dialogPeerCacheKey{userID: userID, peer: peer})
}
hashes := map[domain.Peer]int64{}
if s.versions != nil {
var err error
hashes, err = s.dialogHashes(ctx, userID, unique)
if err != nil {
return nil, err
}
}
var cache *readmodelcache.Cache[dialogPeerCacheKey, dialogDraftCacheEntry]
if s.draftCache != nil {
cache = s.draftCache.cache
}
loaded, err := cache.GetOrLoadBatch(ctx, keys,
func(key dialogPeerCacheKey) (int64, bool) {
hash := hashes[key.peer]
return hash, s.versions != nil && hash != 0
},
func(ctx context.Context, missing []dialogPeerCacheKey) (map[dialogPeerCacheKey]dialogDraftCacheEntry, error) {
requested := make([]domain.Peer, 0, len(missing))
for _, key := range missing {
requested = append(requested, key.peer)
}
drafts, err := s.dialogs.ListDraftsByPeers(ctx, userID, requested)
if err != nil {
return nil, err
}
entries := make(map[dialogPeerCacheKey]dialogDraftCacheEntry, len(missing))
for _, key := range missing {
entries[key] = dialogDraftCacheEntry{}
}
for _, draft := range drafts {
if draft.TopMessageID != 0 {
continue
}
key := dialogPeerCacheKey{userID: userID, peer: draft.Peer}
if _, ok := entries[key]; ok {
entries[key] = dialogDraftCacheEntry{draft: cloneDraft(draft), found: true}
}
}
return entries, nil
})
if err != nil {
return nil, err
}
for key, entry := range loaded {
out[key.peer] = entry
}
return out, nil
}
func uniqueDialogPeers(peers []domain.Peer) []domain.Peer {
out := make([]domain.Peer, 0, len(peers))
seen := make(map[domain.Peer]struct{}, len(peers))
for _, peer := range peers {
if peer.ID == 0 || (peer.Type != domain.PeerTypeUser && peer.Type != domain.PeerTypeChannel) {
continue
}
if _, ok := seen[peer]; ok {
continue
}
seen[peer] = struct{}{}
out = append(out, peer)
}
return out
}
func (c *dialogDraftReadModelCache) invalidate(key dialogPeerCacheKey) {
if c != nil {
c.cache.Invalidate(key)
}
}
func (c *dialogDraftReadModelCache) flush() {
if c != nil {
c.cache.Flush()
}
}
func cloneDialogDraftCacheEntry(entry dialogDraftCacheEntry) dialogDraftCacheEntry {
if entry.found {
entry.draft = cloneDraft(entry.draft)
}
return entry
}
func dialogDraftEntryApproxBytes(entry dialogDraftCacheEntry) int64 {
if !entry.found {
return 64
}
draft := entry.draft
weight := int64(256 + len(draft.Message) + len(draft.Entities)*64)
if draft.ReplyTo != nil {
weight += int64(128 + len(draft.ReplyTo.QuoteText) + len(draft.ReplyTo.QuoteEntities)*64)
}
if draft.WebPage != nil {
weight += int64(64 + len(draft.WebPage.URL))
}
if draft.RichMessage != nil {
weight += int64(len(draft.RichMessage.Blocks) + len(draft.RichMessage.BotAPIProjection) + len(draft.RichMessage.Photos)*256 + len(draft.RichMessage.Documents)*256)
}
return weight
}