package checks import ( "context" "hash/fnv" "slices" "strconv" "strings" "sync" "time" "github.com/sophotechlabs/spinoza/internal/api" ) const surveyTTL = 2 * time.Second type surveyed struct { at time.Time scan scan failure string absent []string } type Surveys struct { mu sync.Mutex ttl time.Duration now func() time.Time held map[string]surveyed } func NewSurveys(now func() time.Time) *Surveys { return &Surveys{ttl: surveyTTL, now: now, held: map[string]surveyed{}} } func (s *Surveys) take( ctx context.Context, lister Lister, descs map[string]api.ResourceDescriptor, usage api.Metrics, keep Filter, ) (scan, string, []string) { if s == nil { return survey(ctx, lister, descs, usage, keep) } key := surveyKey(descs, usage, keep) held, fresh := s.lookup(key) if fresh { return held.scan, held.failure, held.absent } sc, failure, absent := survey(ctx, lister, descs, usage, keep) s.keep(key, surveyed{at: s.now(), scan: sc, failure: failure, absent: absent}) return sc, failure, absent } func (s *Surveys) lookup(key string) (surveyed, bool) { s.mu.Lock() defer s.mu.Unlock() held, ok := s.held[key] if !ok { return surveyed{}, false } if s.now().Sub(held.at) >= s.ttl { return surveyed{}, false } return held, true } func (s *Surveys) keep(key string, one surveyed) { s.mu.Lock() defer s.mu.Unlock() for held, was := range s.held { if s.now().Sub(was.at) >= s.ttl { delete(s.held, held) } } s.held[key] = one } func surveyKey(descs map[string]api.ResourceDescriptor, usage api.Metrics, keep Filter) string { ids := make([]string, 0, 32) for _, entry := range keep.chosen(keep.checks()) { ids = append(ids, entry.id) } slices.Sort(ids) return strconv.FormatBool(keep.WholeCluster) + "|" + strconv.FormatBool(keep.EveryKind) + "|" + strconv.FormatUint(fingerprint(descs), 10) + "|" + strconv.Itoa(len(usage.Pods)) + "|" + strconv.FormatBool(usage.Error == "") + "|" + strings.Join(ids, ",") } func fingerprint(descs map[string]api.ResourceDescriptor) uint64 { keys := make([]string, 0, len(descs)) for key := range descs { keys = append(keys, key) } slices.Sort(keys) sum := fnv.New64a() for _, key := range keys { _, _ = sum.Write([]byte(key)) _, _ = sum.Write([]byte{0}) } return sum.Sum64() }