feat: sync langpack and auth updates
Sync telesrv commits 49f9bab and 04d9563 into the public mirror. Exclude private docs and runtime key material per sync rules.
This commit is contained in:
parent
6af61f26ba
commit
1292540350
244 changed files with 1552172 additions and 27668 deletions
238
internal/app/langpack/cache.go
Normal file
238
internal/app/langpack/cache.go
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
package langpack
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLangPackCacheMaxBytes = int64(128 << 20)
|
||||
defaultLangPackCacheMaxEntries = 256
|
||||
defaultLanguageListCacheMaxEntries = 32
|
||||
langPackStringValueHeaderBytes = int64(144)
|
||||
langPackFixedHeaderBytes = int64(80)
|
||||
)
|
||||
|
||||
type langPackCacheKind uint8
|
||||
|
||||
const (
|
||||
langPackCacheRaw langPackCacheKind = iota
|
||||
langPackCacheEffective
|
||||
)
|
||||
|
||||
type langPackCacheKey struct {
|
||||
pack string
|
||||
code string
|
||||
kind langPackCacheKind
|
||||
}
|
||||
|
||||
func (k langPackCacheKey) singleflightKey() string {
|
||||
return string(rune(k.kind)) + "\x00" + k.pack + "\x00" + k.code
|
||||
}
|
||||
|
||||
type langPackCache struct {
|
||||
mu sync.Mutex
|
||||
maxBytes int64
|
||||
maxEntries int
|
||||
usedBytes int64
|
||||
epoch uint64
|
||||
ll *list.List
|
||||
items map[langPackCacheKey]*list.Element
|
||||
}
|
||||
|
||||
type langPackCacheEntry struct {
|
||||
key langPackCacheKey
|
||||
pack domain.LangPack
|
||||
size int64
|
||||
}
|
||||
|
||||
func newLangPackCache(maxBytes int64, maxEntries int) *langPackCache {
|
||||
if maxBytes <= 0 || maxEntries <= 0 {
|
||||
return nil
|
||||
}
|
||||
return &langPackCache{
|
||||
maxBytes: maxBytes,
|
||||
maxEntries: maxEntries,
|
||||
ll: list.New(),
|
||||
items: make(map[langPackCacheKey]*list.Element),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *langPackCache) get(key langPackCacheKey) (domain.LangPack, bool) {
|
||||
if c == nil {
|
||||
return domain.LangPack{}, false
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
element, ok := c.items[key]
|
||||
if !ok {
|
||||
return domain.LangPack{}, false
|
||||
}
|
||||
c.ll.MoveToFront(element)
|
||||
return cloneLangPack(element.Value.(*langPackCacheEntry).pack), true
|
||||
}
|
||||
|
||||
func (c *langPackCache) loadEpoch() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
c.mu.Lock()
|
||||
epoch := c.epoch
|
||||
c.mu.Unlock()
|
||||
return epoch
|
||||
}
|
||||
|
||||
// putIfEpoch 返回 false 仅表示 load 期间发生过 flush,调用方必须重载。
|
||||
// 超大单项不进入缓存,但仍可安全返回给当前请求,因此返回 true。
|
||||
func (c *langPackCache) putIfEpoch(key langPackCacheKey, pack domain.LangPack, loadEpoch uint64) bool {
|
||||
if c == nil {
|
||||
return true
|
||||
}
|
||||
size := estimateLangPackBytes(pack)
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.epoch != loadEpoch {
|
||||
return false
|
||||
}
|
||||
if existing, ok := c.items[key]; ok {
|
||||
c.remove(existing)
|
||||
}
|
||||
if size > c.maxBytes {
|
||||
return true
|
||||
}
|
||||
entry := &langPackCacheEntry{key: key, pack: cloneLangPack(pack), size: size}
|
||||
c.items[key] = c.ll.PushFront(entry)
|
||||
c.usedBytes += size
|
||||
for c.usedBytes > c.maxBytes || c.ll.Len() > c.maxEntries {
|
||||
oldest := c.ll.Back()
|
||||
if oldest == nil {
|
||||
break
|
||||
}
|
||||
c.remove(oldest)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *langPackCache) flush() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.epoch++
|
||||
c.usedBytes = 0
|
||||
c.ll.Init()
|
||||
clear(c.items)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *langPackCache) remove(element *list.Element) {
|
||||
entry := element.Value.(*langPackCacheEntry)
|
||||
delete(c.items, entry.key)
|
||||
c.ll.Remove(element)
|
||||
c.usedBytes -= entry.size
|
||||
}
|
||||
|
||||
func estimateLangPackBytes(pack domain.LangPack) int64 {
|
||||
size := langPackFixedHeaderBytes + int64(len(pack.LangPack)+len(pack.LangCode))
|
||||
size += int64(len(pack.Strings)) * langPackStringValueHeaderBytes
|
||||
for _, item := range pack.Strings {
|
||||
size += int64(len(item.Key) + len(item.Value) + len(item.ZeroValue) + len(item.OneValue) +
|
||||
len(item.TwoValue) + len(item.FewValue) + len(item.ManyValue) + len(item.OtherValue))
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func cloneLangPack(pack domain.LangPack) domain.LangPack {
|
||||
pack.Strings = append([]domain.LangPackString(nil), pack.Strings...)
|
||||
return pack
|
||||
}
|
||||
|
||||
type languageListCache struct {
|
||||
mu sync.Mutex
|
||||
maxEntries int
|
||||
epoch uint64
|
||||
ll *list.List
|
||||
items map[string]*list.Element
|
||||
}
|
||||
|
||||
type languageListCacheEntry struct {
|
||||
pack string
|
||||
languages []domain.LangPackLanguage
|
||||
}
|
||||
|
||||
func newLanguageListCache(maxEntries int) *languageListCache {
|
||||
if maxEntries <= 0 {
|
||||
return nil
|
||||
}
|
||||
return &languageListCache{
|
||||
maxEntries: maxEntries,
|
||||
ll: list.New(),
|
||||
items: make(map[string]*list.Element),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *languageListCache) get(pack string) ([]domain.LangPackLanguage, bool) {
|
||||
if c == nil {
|
||||
return nil, false
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
element, ok := c.items[pack]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
c.ll.MoveToFront(element)
|
||||
return cloneLanguages(element.Value.(*languageListCacheEntry).languages), true
|
||||
}
|
||||
|
||||
func (c *languageListCache) loadEpoch() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
c.mu.Lock()
|
||||
epoch := c.epoch
|
||||
c.mu.Unlock()
|
||||
return epoch
|
||||
}
|
||||
|
||||
func (c *languageListCache) putIfEpoch(pack string, languages []domain.LangPackLanguage, loadEpoch uint64) bool {
|
||||
if c == nil {
|
||||
return true
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.epoch != loadEpoch {
|
||||
return false
|
||||
}
|
||||
if existing, ok := c.items[pack]; ok {
|
||||
c.ll.Remove(existing)
|
||||
delete(c.items, pack)
|
||||
}
|
||||
entry := &languageListCacheEntry{pack: pack, languages: cloneLanguages(languages)}
|
||||
c.items[pack] = c.ll.PushFront(entry)
|
||||
if c.ll.Len() > c.maxEntries {
|
||||
oldest := c.ll.Back()
|
||||
if oldest != nil {
|
||||
delete(c.items, oldest.Value.(*languageListCacheEntry).pack)
|
||||
c.ll.Remove(oldest)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *languageListCache) flush() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
c.epoch++
|
||||
c.ll.Init()
|
||||
clear(c.items)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func cloneLanguages(languages []domain.LangPackLanguage) []domain.LangPackLanguage {
|
||||
return append([]domain.LangPackLanguage(nil), languages...)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue