Ctrl K
ComunidadGuías…

internal/checks/surveycache.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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()
}