2018-08-26 19:20:08 +02:00
|
|
|
package retentiongrid
|
2017-06-21 20:29:20 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2018-08-26 19:20:08 +02:00
|
|
|
type RetentionInterval interface {
|
|
|
|
Length() time.Duration
|
|
|
|
KeepCount() int
|
2017-06-21 20:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const RetentionGridKeepCountAll int = -1
|
|
|
|
|
2018-08-26 19:20:08 +02:00
|
|
|
type retentionGrid struct {
|
2017-06-21 20:29:20 +02:00
|
|
|
intervals []RetentionInterval
|
|
|
|
}
|
|
|
|
|
2018-08-26 19:20:08 +02:00
|
|
|
//A point inside the grid, i.e. a thing the grid can decide to remove
|
2017-06-21 20:29:20 +02:00
|
|
|
type RetentionGridEntry interface {
|
|
|
|
Date() time.Time
|
|
|
|
LessThan(b RetentionGridEntry) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func dateInInterval(date, startDateInterval time.Time, i RetentionInterval) bool {
|
2018-08-26 19:20:08 +02:00
|
|
|
return date.After(startDateInterval) && date.Before(startDateInterval.Add(i.Length()))
|
2017-06-21 20:29:20 +02:00
|
|
|
}
|
|
|
|
|
2018-08-26 19:20:08 +02:00
|
|
|
func newRetentionGrid(l []RetentionInterval) *retentionGrid {
|
2017-06-21 20:29:20 +02:00
|
|
|
// TODO Maybe check for ascending interval lengths here, although the algorithm
|
|
|
|
// itself doesn't care about that.
|
2018-08-26 19:20:08 +02:00
|
|
|
return &retentionGrid{l}
|
2017-06-21 20:29:20 +02:00
|
|
|
}
|
|
|
|
|
2018-08-26 19:20:08 +02:00
|
|
|
// Partition a list of RetentionGridEntries into the retentionGrid,
|
2017-06-21 20:29:20 +02:00
|
|
|
// relative to a given start date `now`.
|
|
|
|
//
|
2018-08-26 19:20:08 +02:00
|
|
|
// The `keepCount` oldest entries per `RetentionInterval` are kept (`keep`),
|
2017-06-21 20:29:20 +02:00
|
|
|
// the others are removed (`remove`).
|
|
|
|
//
|
|
|
|
// Entries that are younger than `now` are always kept.
|
|
|
|
// Those that are older than the earliest beginning of an interval are removed.
|
2018-08-26 19:20:08 +02:00
|
|
|
func (g retentionGrid) FitEntries(now time.Time, entries []RetentionGridEntry) (keep, remove []RetentionGridEntry) {
|
2017-06-21 20:29:20 +02:00
|
|
|
|
|
|
|
type bucket struct {
|
|
|
|
entries []RetentionGridEntry
|
|
|
|
}
|
|
|
|
buckets := make([]bucket, len(g.intervals))
|
|
|
|
|
|
|
|
keep = make([]RetentionGridEntry, 0)
|
|
|
|
remove = make([]RetentionGridEntry, 0)
|
|
|
|
|
|
|
|
oldestIntervalStart := now
|
|
|
|
for i := range g.intervals {
|
2018-08-26 19:20:08 +02:00
|
|
|
oldestIntervalStart = oldestIntervalStart.Add(-g.intervals[i].Length())
|
2017-06-21 20:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for ei := 0; ei < len(entries); ei++ {
|
|
|
|
e := entries[ei]
|
|
|
|
|
|
|
|
date := e.Date()
|
|
|
|
|
|
|
|
if date == now || date.After(now) {
|
|
|
|
keep = append(keep, e)
|
|
|
|
continue
|
|
|
|
} else if date.Before(oldestIntervalStart) {
|
|
|
|
remove = append(remove, e)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
iStartTime := now
|
|
|
|
for i := 0; i < len(g.intervals); i++ {
|
2018-08-26 19:20:08 +02:00
|
|
|
iStartTime = iStartTime.Add(-g.intervals[i].Length())
|
2017-06-21 20:29:20 +02:00
|
|
|
if date == iStartTime || dateInInterval(date, iStartTime, g.intervals[i]) {
|
|
|
|
buckets[i].entries = append(buckets[i].entries, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for bi, b := range buckets {
|
|
|
|
|
2018-08-26 19:20:08 +02:00
|
|
|
interval := g.intervals[bi]
|
2017-06-21 20:29:20 +02:00
|
|
|
|
|
|
|
sort.SliceStable(b.entries, func(i, j int) bool {
|
|
|
|
return b.entries[i].LessThan((b.entries[j]))
|
|
|
|
})
|
|
|
|
|
|
|
|
i := 0
|
2018-08-26 19:20:08 +02:00
|
|
|
for ; (interval.KeepCount() == RetentionGridKeepCountAll || i < interval.KeepCount()) && i < len(b.entries); i++ {
|
2017-06-21 20:29:20 +02:00
|
|
|
keep = append(keep, b.entries[i])
|
|
|
|
}
|
|
|
|
for ; i < len(b.entries); i++ {
|
|
|
|
remove = append(remove, b.entries[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|