chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
136
internal/app/contacts/read_model_cache.go
Normal file
136
internal/app/contacts/read_model_cache.go
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package contacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/readmodelcache"
|
||||
)
|
||||
|
||||
const (
|
||||
contactAccountReadModel = "contact_account"
|
||||
defaultContactListReadModelTTL = 24 * time.Hour
|
||||
contactListReadModelMaxUsers = 4096
|
||||
)
|
||||
|
||||
// contactListReadModelCache 是 contact list read-model 的 per-viewer 缓存,由统一缓存原语
|
||||
// readmodelcache.Cache 承载(版本闸门 / epoch 守卫 / LRU 单条驱逐 / clone 内建)。
|
||||
type contactListReadModelCache struct {
|
||||
cache *readmodelcache.Cache[int64, domain.ContactList]
|
||||
}
|
||||
|
||||
func newContactListReadModelCache(ttl time.Duration) *contactListReadModelCache {
|
||||
if ttl <= 0 {
|
||||
ttl = defaultContactListReadModelTTL
|
||||
}
|
||||
return &contactListReadModelCache{
|
||||
cache: readmodelcache.New[int64, domain.ContactList](readmodelcache.Config[int64, domain.ContactList]{
|
||||
MaxEntries: contactListReadModelMaxUsers,
|
||||
TTL: ttl,
|
||||
Clone: cloneContactList,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// getOrLoad 命中即返回 clone,否则经 singleflight load。版本闸门:hasHash 且 currentHash!=0
|
||||
// 时仅复用 storedHash==currentHash 的快照,否则重载(对齐 contact_account 版本脊)。
|
||||
func (c *contactListReadModelCache) getOrLoad(ctx context.Context, userID int64, currentHash int64, hasHash bool, load func() (domain.ContactList, error)) (domain.ContactList, error) {
|
||||
if c == nil {
|
||||
return load()
|
||||
}
|
||||
effectiveHash := int64(0)
|
||||
if hasHash {
|
||||
effectiveHash = currentHash
|
||||
}
|
||||
return c.cache.GetOrLoadVersioned(ctx, userID, effectiveHash, load)
|
||||
}
|
||||
|
||||
func (c *contactListReadModelCache) invalidate(ids ...int64) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.cache.Invalidate(ids...)
|
||||
}
|
||||
|
||||
func (c *contactListReadModelCache) flush() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.cache.Flush()
|
||||
}
|
||||
|
||||
func (s *Service) contactAccountHash(ctx context.Context, userID int64) (int64, bool, error) {
|
||||
if s == nil || s.versions == nil || userID == 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
return s.versions.ReadModelHash(ctx, contactAccountReadModel, userID, domain.PeerTypeUser, userID)
|
||||
}
|
||||
|
||||
func (s *Service) contactListReadModel(ctx context.Context, userID int64, currentHash int64, hasHash bool) (domain.ContactList, error) {
|
||||
if s == nil {
|
||||
return domain.ContactList{}, nil
|
||||
}
|
||||
if s.cache == nil {
|
||||
return s.loadContactListReadModel(ctx, userID, currentHash, hasHash)
|
||||
}
|
||||
return s.cache.getOrLoad(ctx, userID, currentHash, hasHash, func() (domain.ContactList, error) {
|
||||
return s.loadContactListReadModel(ctx, userID, currentHash, hasHash)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) loadContactListReadModel(ctx context.Context, userID int64, currentHash int64, hasHash bool) (domain.ContactList, error) {
|
||||
list, err := s.contacts.ListByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return domain.ContactList{}, err
|
||||
}
|
||||
if err := s.projectContactUsers(ctx, userID, &list); err != nil {
|
||||
return domain.ContactList{}, err
|
||||
}
|
||||
if hasHash && currentHash != 0 {
|
||||
list.Hash = currentHash
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *Service) InvalidateViewers(ids ...int64) {
|
||||
if s == nil || s.cache == nil {
|
||||
return
|
||||
}
|
||||
s.cache.invalidate(ids...)
|
||||
}
|
||||
|
||||
func (s *Service) FlushReadModelCache() {
|
||||
if s == nil || s.cache == nil {
|
||||
return
|
||||
}
|
||||
s.cache.flush()
|
||||
}
|
||||
|
||||
func cloneContactList(in domain.ContactList) domain.ContactList {
|
||||
in.Contacts = cloneContacts(in.Contacts)
|
||||
return in
|
||||
}
|
||||
|
||||
func cloneContacts(in []domain.Contact) []domain.Contact {
|
||||
out := make([]domain.Contact, len(in))
|
||||
for i := range in {
|
||||
out[i] = cloneContact(in[i])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneContact(in domain.Contact) domain.Contact {
|
||||
in.User = cloneUser(in.User)
|
||||
if in.NoteEntities != nil {
|
||||
in.NoteEntities = append([]domain.MessageEntity(nil), in.NoteEntities...)
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func cloneUser(in domain.User) domain.User {
|
||||
if in.PhotoStripped != nil {
|
||||
in.PhotoStripped = append([]byte(nil), in.PhotoStripped...)
|
||||
}
|
||||
return in
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue