package executor import ( "crypto/sha256" "encoding/hex" "sync" "time" ) type userIDCacheEntry struct { value string expire time.Time } var ( userIDCache = make(map[string]userIDCacheEntry) userIDCacheMu sync.RWMutex userIDCacheCleanupOnce sync.Once ) const ( userIDTTL = time.Hour userIDCacheCleanupPeriod = 15 * time.Minute ) func startUserIDCacheCleanup() { go func() { ticker := time.NewTicker(userIDCacheCleanupPeriod) defer ticker.Stop() for range ticker.C { purgeExpiredUserIDs() } }() } func purgeExpiredUserIDs() { now := time.Now() userIDCacheMu.Lock() for key, entry := range userIDCache { if !entry.expire.After(now) { delete(userIDCache, key) } } userIDCacheMu.Unlock() } func userIDCacheKey(apiKey string) string { sum := sha256.Sum256([]byte(apiKey)) return hex.EncodeToString(sum[:]) } func cachedUserID(apiKey string) string { if apiKey == "" { return generateFakeUserID() } userIDCacheCleanupOnce.Do(startUserIDCacheCleanup) key := userIDCacheKey(apiKey) now := time.Now() userIDCacheMu.RLock() entry, ok := userIDCache[key] valid := ok && entry.value != "" && entry.expire.After(now) && isValidUserID(entry.value) userIDCacheMu.RUnlock() if valid { userIDCacheMu.Lock() entry = userIDCache[key] if entry.value != "" && entry.expire.After(now) && isValidUserID(entry.value) { entry.expire = now.Add(userIDTTL) userIDCache[key] = entry userIDCacheMu.Unlock() return entry.value } userIDCacheMu.Unlock() } newID := generateFakeUserID() userIDCacheMu.Lock() entry, ok = userIDCache[key] if !ok || entry.value == "" || !entry.expire.After(now) || !isValidUserID(entry.value) { entry.value = newID } entry.expire = now.Add(userIDTTL) userIDCache[key] = entry userIDCacheMu.Unlock() return entry.value }