Files

101 lines
2.5 KiB
Go

package jetstream
import (
"sync"
"time"
)
// HoldRepoStats represents stats for a single owner+repo from a specific hold
type HoldRepoStats struct {
OwnerDID string
Repository string
PullCount int64
PushCount int64
LastPull *time.Time
LastPush *time.Time
}
// StatsCache provides in-memory caching of per-hold stats with aggregation
// This allows summing stats across multiple holds for the same owner+repo
type StatsCache struct {
mu sync.RWMutex
// holdDID -> (ownerDID/repo -> stats)
holds map[string]map[string]*HoldRepoStats
}
// NewStatsCache creates a new in-memory stats cache
func NewStatsCache() *StatsCache {
return &StatsCache{
holds: make(map[string]map[string]*HoldRepoStats),
}
}
// makeKey creates a cache key from ownerDID and repository
func makeKey(ownerDID, repo string) string {
return ownerDID + "/" + repo
}
// Update stores or updates stats for a hold+owner+repo combination
func (c *StatsCache) Update(holdDID, ownerDID, repo string, pullCount, pushCount int64, lastPull, lastPush *time.Time) {
c.mu.Lock()
defer c.mu.Unlock()
// Ensure hold map exists
if c.holds[holdDID] == nil {
c.holds[holdDID] = make(map[string]*HoldRepoStats)
}
key := makeKey(ownerDID, repo)
c.holds[holdDID][key] = &HoldRepoStats{
OwnerDID: ownerDID,
Repository: repo,
PullCount: pullCount,
PushCount: pushCount,
LastPull: lastPull,
LastPush: lastPush,
}
}
// Delete removes stats for a hold+owner+repo combination
func (c *StatsCache) Delete(holdDID, ownerDID, repo string) {
c.mu.Lock()
defer c.mu.Unlock()
if c.holds[holdDID] != nil {
key := makeKey(ownerDID, repo)
delete(c.holds[holdDID], key)
}
}
// GetAggregated returns aggregated stats for an owner+repo by summing across all holds
// Returns (pullCount, pushCount, lastPull, lastPush)
func (c *StatsCache) GetAggregated(ownerDID, repo string) (int64, int64, *time.Time, *time.Time) {
c.mu.RLock()
defer c.mu.RUnlock()
key := makeKey(ownerDID, repo)
var totalPull, totalPush int64
var latestPull, latestPush *time.Time
for _, holdStats := range c.holds {
if stats, ok := holdStats[key]; ok {
totalPull += stats.PullCount
totalPush += stats.PushCount
// Track latest timestamps
if stats.LastPull != nil {
if latestPull == nil || stats.LastPull.After(*latestPull) {
latestPull = stats.LastPull
}
}
if stats.LastPush != nil {
if latestPush == nil || stats.LastPush.After(*latestPush) {
latestPush = stats.LastPush
}
}
}
}
return totalPull, totalPush, latestPull, latestPush
}