Fix Docker Swarm discovery network name mismatch
- Changed NetworkName from 'chorus_default' to 'chorus_net' - This matches the actual network 'CHORUS_chorus_net' (service prefix added automatically) - Fixes discovered_count:0 issue - now successfully discovering all 25 agents - Updated IMPLEMENTATION-SUMMARY with deployment status Result: All 25 CHORUS agents now discovered successfully via Docker Swarm API 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
136
internal/licensing/license_cache.go
Normal file
136
internal/licensing/license_cache.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package licensing
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// CacheEntry holds cached license validation data
|
||||
type CacheEntry struct {
|
||||
Response *ValidationResponse
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
// LicenseCache provides in-memory caching for license validations
|
||||
type LicenseCache struct {
|
||||
mu sync.RWMutex
|
||||
entries map[string]*CacheEntry
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// NewLicenseCache creates a new license cache with specified TTL
|
||||
func NewLicenseCache(ttl time.Duration) *LicenseCache {
|
||||
cache := &LicenseCache{
|
||||
entries: make(map[string]*CacheEntry),
|
||||
ttl: ttl,
|
||||
}
|
||||
|
||||
// Start cleanup goroutine
|
||||
go cache.cleanup()
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
// Get retrieves cached validation response if available and not expired
|
||||
func (c *LicenseCache) Get(deploymentID uuid.UUID, feature string) *ValidationResponse {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
key := c.cacheKey(deploymentID, feature)
|
||||
entry, exists := c.entries[key]
|
||||
|
||||
if !exists || time.Now().After(entry.ExpiresAt) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return entry.Response
|
||||
}
|
||||
|
||||
// Set stores validation response in cache with TTL
|
||||
func (c *LicenseCache) Set(deploymentID uuid.UUID, feature string, response *ValidationResponse) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
key := c.cacheKey(deploymentID, feature)
|
||||
c.entries[key] = &CacheEntry{
|
||||
Response: response,
|
||||
ExpiresAt: time.Now().Add(c.ttl),
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate removes specific cache entry
|
||||
func (c *LicenseCache) Invalidate(deploymentID uuid.UUID, feature string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
key := c.cacheKey(deploymentID, feature)
|
||||
delete(c.entries, key)
|
||||
}
|
||||
|
||||
// InvalidateAll removes all cached entries for a deployment
|
||||
func (c *LicenseCache) InvalidateAll(deploymentID uuid.UUID) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
prefix := deploymentID.String() + ":"
|
||||
for key := range c.entries {
|
||||
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
|
||||
delete(c.entries, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes all cached entries
|
||||
func (c *LicenseCache) Clear() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.entries = make(map[string]*CacheEntry)
|
||||
}
|
||||
|
||||
// Stats returns cache statistics
|
||||
func (c *LicenseCache) Stats() map[string]interface{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
totalEntries := len(c.entries)
|
||||
expiredEntries := 0
|
||||
now := time.Now()
|
||||
|
||||
for _, entry := range c.entries {
|
||||
if now.After(entry.ExpiresAt) {
|
||||
expiredEntries++
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_entries": totalEntries,
|
||||
"expired_entries": expiredEntries,
|
||||
"active_entries": totalEntries - expiredEntries,
|
||||
"ttl_seconds": int(c.ttl.Seconds()),
|
||||
}
|
||||
}
|
||||
|
||||
// cacheKey generates cache key from deployment ID and feature
|
||||
func (c *LicenseCache) cacheKey(deploymentID uuid.UUID, feature string) string {
|
||||
return deploymentID.String() + ":" + feature
|
||||
}
|
||||
|
||||
// cleanup removes expired entries periodically
|
||||
func (c *LicenseCache) cleanup() {
|
||||
ticker := time.NewTicker(c.ttl / 2) // Clean up twice as often as TTL
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
c.mu.Lock()
|
||||
now := time.Now()
|
||||
for key, entry := range c.entries {
|
||||
if now.After(entry.ExpiresAt) {
|
||||
delete(c.entries, key)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user