1
0
pool/adapative.go

92 lines
1.8 KiB
Go
Raw Normal View History

2026-03-04 15:03:29 +01:00
package pool
2026-03-05 12:34:35 +01:00
import (
"code.squareroundforest.org/arpio/times"
2026-03-14 19:03:18 +01:00
"math"
2026-03-05 12:34:35 +01:00
"time"
)
2026-03-04 15:03:29 +01:00
const (
// arbitrary values to be most likely out of sync with anything else:
minNightshiftTime = 729 * time.Millisecond // ~1sec
maxNightshiftTime = 59049 * time.Second // ~2/3day
)
type adaptive struct {
2026-03-05 12:34:35 +01:00
clock times.Clock
2026-03-04 15:03:29 +01:00
activeTime time.Time
nsTO time.Duration
idle bool
2026-03-14 19:03:18 +01:00
average float64
deviation float64
2026-03-04 15:03:29 +01:00
}
func makeAdaptiveAlgo() *adaptive {
return &adaptive{idle: true}
}
2026-03-05 12:34:35 +01:00
func (a *adaptive) setClock(c times.Clock) {
a.clock = c
}
2026-03-14 19:03:18 +01:00
func movingAverage(prev, currv float64) float64 {
return prev + (currv-prev)/math.E
2026-03-04 15:03:29 +01:00
}
2026-03-14 19:03:18 +01:00
func movingAbsoluteDeviation(prev, currv, currav float64) float64 {
return prev + (math.Abs(currv-currav)-prev)/math.E
2026-03-04 15:03:29 +01:00
}
2026-03-14 19:03:18 +01:00
func targetCapacity(av, dev float64) float64 {
return av + dev*math.E
2026-03-04 15:03:29 +01:00
}
func (a *adaptive) target(s Stats) int {
2026-03-14 19:03:18 +01:00
av := movingAverage(a.average, float64(s.Active))
dev := movingAbsoluteDeviation(a.deviation, float64(s.Active), av)
2026-03-04 15:03:29 +01:00
a.average = av
a.deviation = dev
2026-03-14 19:03:18 +01:00
return int(targetCapacity(av, dev))
2026-03-04 15:03:29 +01:00
}
func (a *adaptive) nightshift(s Stats) time.Duration {
if a.idle && s.Active == 0 {
return a.nsTO
}
if !a.idle && s.Active > 0 {
return 0
}
2026-03-05 12:34:35 +01:00
now := a.clock.Now()
2026-03-04 15:03:29 +01:00
a.idle = !a.idle
if !a.idle {
a.activeTime = now
return 0
}
a.nsTO = now.Sub(a.activeTime)
2026-03-14 19:03:18 +01:00
a.nsTO = (3 * a.nsTO) / 8
2026-03-04 15:03:29 +01:00
if a.nsTO < minNightshiftTime {
a.nsTO = minNightshiftTime
}
if a.nsTO > maxNightshiftTime {
a.nsTO = maxNightshiftTime
}
return a.nsTO
}
func (a *adaptive) Target(s Stats) (int, time.Duration) {
t := a.target(s)
ns := a.nightshift(s)
return t, ns
}
2026-03-14 19:03:18 +01:00
func (a *adaptive) Load(n int) {
// we lie to the algorithm when adding the additional idle count to the average active count. This way
// we can adjust the calculated target capacity:
a.average += float64(n)
}