Files
distributor/internal/state/prune.go

122 lines
2.9 KiB
Go

package state
import (
"sort"
"time"
)
type PruneCandidate struct {
Path string
UpdatedAt time.Time
Owner *OwnerScope
}
type PrunePlanOptions struct {
Now time.Time
OlderThan *time.Duration
KeepLatest *int
}
type PrunePlan struct {
Pruned []PruneCandidate
Preserved []PruneCandidate
}
func SingleOwnerPruneCandidates(s DistributorState) []PruneCandidate {
candidates := make([]PruneCandidate, 0, len(s.Outputs))
for _, output := range s.Outputs {
candidates = append(candidates, PruneCandidate{
Path: output.Path,
UpdatedAt: output.UpdatedAt,
})
}
return candidates
}
func SharedRootPruneCandidates(s SharedRootState, scope OwnerScope) []PruneCandidate {
candidates := make([]PruneCandidate, 0, len(s.Outputs))
for _, output := range s.Outputs {
if output.Owner != scope {
continue
}
owner := output.Owner
candidates = append(candidates, PruneCandidate{
Path: output.Path,
UpdatedAt: output.UpdatedAt,
Owner: &owner,
})
}
return candidates
}
func PlanPrune(candidates []PruneCandidate, options PrunePlanOptions) PrunePlan {
ordered := append([]PruneCandidate(nil), candidates...)
sortPruneCandidatesNewestFirst(ordered)
if options.OlderThan == nil && options.KeepLatest == nil {
return PrunePlan{
Pruned: []PruneCandidate{},
Preserved: ordered,
}
}
preservedByPath := make(map[string]struct{})
if options.KeepLatest != nil {
keep := *options.KeepLatest
if keep < 0 {
keep = 0
}
if keep > len(ordered) {
keep = len(ordered)
}
for _, candidate := range ordered[:keep] {
preservedByPath[candidate.Path] = struct{}{}
}
}
plan := PrunePlan{
Pruned: []PruneCandidate{},
Preserved: []PruneCandidate{},
}
cutoff := time.Time{}
if options.OlderThan != nil {
now := options.Now.UTC()
if now.IsZero() {
now = time.Now().UTC()
}
cutoff = now.Add(-*options.OlderThan)
}
for _, candidate := range ordered {
if _, preserved := preservedByPath[candidate.Path]; preserved {
plan.Preserved = append(plan.Preserved, candidate)
continue
}
if options.OlderThan == nil || candidate.UpdatedAt.Before(cutoff) {
plan.Pruned = append(plan.Pruned, candidate)
continue
}
plan.Preserved = append(plan.Preserved, candidate)
}
sortPruneCandidatesOldestFirst(plan.Pruned)
sortPruneCandidatesNewestFirst(plan.Preserved)
return plan
}
func sortPruneCandidatesNewestFirst(candidates []PruneCandidate) {
sort.Slice(candidates, func(i, j int) bool {
if !candidates[i].UpdatedAt.Equal(candidates[j].UpdatedAt) {
return candidates[i].UpdatedAt.After(candidates[j].UpdatedAt)
}
return candidates[i].Path < candidates[j].Path
})
}
func sortPruneCandidatesOldestFirst(candidates []PruneCandidate) {
sort.Slice(candidates, func(i, j int) bool {
if !candidates[i].UpdatedAt.Equal(candidates[j].UpdatedAt) {
return candidates[i].UpdatedAt.Before(candidates[j].UpdatedAt)
}
return candidates[i].Path < candidates[j].Path
})
}