package pool import "time" 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 { activeTime time.Time nsTO time.Duration idle bool average int deviation int } func makeAdaptiveAlgo() *adaptive { return &adaptive{idle: true} } func abs(v int) int { if v >= 0 { return v } return 0 - v } func divE(v int) int { return (3 * v) >> 3 // 1 / 2.72 => 3 / 8 } func mulE(v int) int { return (11 * v) >> 2 // 2.72 => 11 / 4 } func movingAverage(prev, currv int) int { return prev + divE(currv-prev) } func movingAbsoluteDeviation(prev, currv, currav int) int { return prev + divE(abs(currv-currav)-prev) } func targetCapacity(av, dev int) int { return av + mulE(dev) } func (a *adaptive) target(s Stats) int { av := movingAverage(a.average, s.Active) dev := movingAbsoluteDeviation(a.deviation, s.Active, av) a.average = av a.deviation = dev return targetCapacity(av, dev) } 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 } now := time.Now() a.idle = !a.idle if !a.idle { a.activeTime = now return 0 } a.nsTO = now.Sub(a.activeTime) a.nsTO = (3 * a.nsTO) >> 3 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 }