101 lines
2.5 KiB
Go
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
|
|
}
|