feat: enforce tiered storage reservations
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RetentionCandidate struct {
|
||||
IssueUUID [16]byte
|
||||
IssueTime time.Time
|
||||
TerminalTime time.Time
|
||||
ChargedBytes uint64
|
||||
Terminal bool
|
||||
}
|
||||
|
||||
type RetentionSelection struct {
|
||||
IssueUUIDs [][16]byte
|
||||
ReleasedBytes uint64
|
||||
}
|
||||
|
||||
func SelectWholeCommandEvictions(now time.Time, age time.Duration, bytesNeeded uint64, candidates []RetentionCandidate) RetentionSelection {
|
||||
ordered := append([]RetentionCandidate(nil), candidates...)
|
||||
sort.Slice(ordered, func(left, right int) bool {
|
||||
if ordered[left].IssueTime.Equal(ordered[right].IssueTime) {
|
||||
return bytes.Compare(ordered[left].IssueUUID[:], ordered[right].IssueUUID[:]) < 0
|
||||
}
|
||||
return ordered[left].IssueTime.Before(ordered[right].IssueTime)
|
||||
})
|
||||
selected := make(map[[16]byte]bool, len(ordered))
|
||||
var result RetentionSelection
|
||||
selectCandidate := func(candidate RetentionCandidate) {
|
||||
if selected[candidate.IssueUUID] {
|
||||
return
|
||||
}
|
||||
selected[candidate.IssueUUID] = true
|
||||
result.IssueUUIDs = append(result.IssueUUIDs, candidate.IssueUUID)
|
||||
if total, overflow := addChecked(result.ReleasedBytes, candidate.ChargedBytes); overflow {
|
||||
result.ReleasedBytes = ^uint64(0)
|
||||
} else {
|
||||
result.ReleasedBytes = total
|
||||
}
|
||||
}
|
||||
if age > 0 {
|
||||
cutoff := now.Add(-age)
|
||||
for _, candidate := range ordered {
|
||||
if candidate.Terminal && !candidate.TerminalTime.IsZero() && !candidate.TerminalTime.After(cutoff) {
|
||||
selectCandidate(candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, candidate := range ordered {
|
||||
if result.ReleasedBytes >= bytesNeeded {
|
||||
break
|
||||
}
|
||||
if candidate.Terminal {
|
||||
selectCandidate(candidate)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type OutputSegmentCandidate struct {
|
||||
Ordinal uint32
|
||||
ChargedBytes uint64
|
||||
Sealed bool
|
||||
}
|
||||
|
||||
func SelectOutputRotation(currentBytes, limit uint64, candidates []OutputSegmentCandidate) []uint32 {
|
||||
if currentBytes <= limit {
|
||||
return nil
|
||||
}
|
||||
ordered := append([]OutputSegmentCandidate(nil), candidates...)
|
||||
sort.Slice(ordered, func(left, right int) bool { return ordered[left].Ordinal < ordered[right].Ordinal })
|
||||
var selected []uint32
|
||||
for _, candidate := range ordered {
|
||||
if currentBytes <= limit {
|
||||
break
|
||||
}
|
||||
if !candidate.Sealed {
|
||||
continue
|
||||
}
|
||||
selected = append(selected, candidate.Ordinal)
|
||||
if candidate.ChargedBytes >= currentBytes {
|
||||
currentBytes = 0
|
||||
} else {
|
||||
currentBytes -= candidate.ChargedBytes
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
|
||||
func FIFOEntriesToRemove(current, maximum uint64) uint64 {
|
||||
if current <= maximum {
|
||||
return 0
|
||||
}
|
||||
return current - maximum
|
||||
}
|
||||
Reference in New Issue
Block a user