168 lines
4.0 KiB
Go
168 lines
4.0 KiB
Go
package store
|
|
|
|
import (
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/tinode/chat/server/store/types"
|
|
)
|
|
|
|
const configCacheTTL = time.Minute
|
|
|
|
type configCacheEntry[T any] struct {
|
|
value T
|
|
expiresAt time.Time
|
|
}
|
|
|
|
type configCache[T any] struct {
|
|
mu sync.RWMutex
|
|
items map[string]configCacheEntry[T]
|
|
}
|
|
|
|
func newConfigCache[T any]() *configCache[T] {
|
|
return &configCache[T]{items: make(map[string]configCacheEntry[T])}
|
|
}
|
|
|
|
func (c *configCache[T]) get(key string) (T, bool) {
|
|
var zero T
|
|
c.mu.RLock()
|
|
entry, ok := c.items[key]
|
|
c.mu.RUnlock()
|
|
if !ok || time.Now().After(entry.expiresAt) {
|
|
return zero, false
|
|
}
|
|
return entry.value, true
|
|
}
|
|
|
|
func (c *configCache[T]) set(key string, value T) {
|
|
c.mu.Lock()
|
|
c.items[key] = configCacheEntry[T]{value: value, expiresAt: time.Now().Add(configCacheTTL)}
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (c *configCache[T]) delete(key string) {
|
|
c.mu.Lock()
|
|
delete(c.items, key)
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (c *configCache[T]) clear() {
|
|
c.mu.Lock()
|
|
c.items = make(map[string]configCacheEntry[T])
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
var (
|
|
tenantByCodeCache = newConfigCache[*types.Tenant]()
|
|
tenantByIDCache = newConfigCache[*types.Tenant]()
|
|
registrationSMSCache = newConfigCache[bool]()
|
|
thirdPartyConfigCache = newConfigCache[map[string]string]()
|
|
appReleaseConfigCache = newConfigCache[*types.AppRelease]()
|
|
moderationProviderCache = newConfigCache[*types.ModerationProviderConfig]()
|
|
moderationPolicyCache = newConfigCache[*types.ModerationPolicy]()
|
|
moderationWordsCache = newConfigCache[*types.SensitiveWordSet]()
|
|
thirdPartyConfigAllKeys = "*"
|
|
)
|
|
|
|
func InvalidateModerationConfigCache(tenantCode string, provider, policy, words bool) {
|
|
if provider {
|
|
moderationProviderCache.clear()
|
|
}
|
|
if policy {
|
|
if strings.TrimSpace(tenantCode) == "" {
|
|
moderationPolicyCache.clear()
|
|
} else {
|
|
moderationPolicyCache.delete(strings.TrimSpace(tenantCode))
|
|
}
|
|
}
|
|
if words {
|
|
if strings.TrimSpace(tenantCode) == "" {
|
|
moderationWordsCache.clear()
|
|
} else {
|
|
moderationWordsCache.delete(strings.TrimSpace(tenantCode))
|
|
}
|
|
}
|
|
}
|
|
|
|
func cloneTenant(t *types.Tenant) *types.Tenant {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
cp := *t
|
|
return &cp
|
|
}
|
|
|
|
// InvalidateAppReleaseConfigCache clears global client release policies.
|
|
func InvalidateAppReleaseConfigCache() {
|
|
appReleaseConfigCache.clear()
|
|
}
|
|
|
|
func cloneStringMap(src map[string]string) map[string]string {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
dst := make(map[string]string, len(src))
|
|
for k, v := range src {
|
|
dst[k] = v
|
|
}
|
|
return dst
|
|
}
|
|
|
|
func thirdPartyCacheKey(group string, keys ...string) string {
|
|
group = strings.TrimSpace(group)
|
|
if len(keys) == 0 {
|
|
return group + ":" + thirdPartyConfigAllKeys
|
|
}
|
|
normalized := make([]string, 0, len(keys))
|
|
for _, key := range keys {
|
|
if key = strings.TrimSpace(key); key != "" {
|
|
normalized = append(normalized, key)
|
|
}
|
|
}
|
|
sort.Strings(normalized)
|
|
return group + ":" + strings.Join(normalized, ",")
|
|
}
|
|
|
|
// InvalidateTenantConfigCache clears cached tenant registry/config records. An
|
|
// empty selector clears all tenant records.
|
|
func InvalidateTenantConfigCache(code string, id types.TenantID) {
|
|
if strings.TrimSpace(code) != "" {
|
|
tenantByCodeCache.delete(strings.TrimSpace(code))
|
|
} else {
|
|
tenantByCodeCache.clear()
|
|
}
|
|
if id.IsValid() {
|
|
tenantByIDCache.delete(formatTenantIDForCache(id))
|
|
} else if strings.TrimSpace(code) == "" {
|
|
tenantByIDCache.clear()
|
|
}
|
|
}
|
|
|
|
// InvalidateRegistrationConfigCache clears cached global registration config.
|
|
func InvalidateRegistrationConfigCache() {
|
|
registrationSMSCache.clear()
|
|
}
|
|
|
|
// InvalidateThirdPartyConfigCache clears cached third-party config.
|
|
func InvalidateThirdPartyConfigCache(group string) {
|
|
if strings.TrimSpace(group) == "" {
|
|
thirdPartyConfigCache.clear()
|
|
return
|
|
}
|
|
prefix := strings.TrimSpace(group) + ":"
|
|
thirdPartyConfigCache.mu.Lock()
|
|
for key := range thirdPartyConfigCache.items {
|
|
if strings.HasPrefix(key, prefix) {
|
|
delete(thirdPartyConfigCache.items, key)
|
|
}
|
|
}
|
|
thirdPartyConfigCache.mu.Unlock()
|
|
}
|
|
|
|
func formatTenantIDForCache(id types.TenantID) string {
|
|
return strconv.FormatInt(int64(id), 10)
|
|
}
|