1
0

fix some algo edge cases

This commit is contained in:
Arpad Ryszka 2026-03-04 15:03:29 +01:00
parent 99e8b6f6b6
commit 9ee50d312b
5 changed files with 172 additions and 207 deletions

92
adapative.go Normal file
View File

@ -0,0 +1,92 @@
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
}

163
algo.go
View File

@ -1,163 +0,0 @@
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 {
lastTurnedActive time.Time
nightshiftTime time.Duration
idle bool
average int
deviation int
}
type maxTimeout struct {
max int
to time.Duration
items []time.Time
}
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.nightshiftTime
}
if !a.idle && s.Active > 0 {
return 0
}
now := time.Now()
a.idle = !a.idle
if !a.idle {
a.lastTurnedActive = now
return 0
}
a.nightshiftTime = now.Sub(a.lastTurnedActive)
a.nightshiftTime = (3 * a.nightshiftTime) >> 3
if a.nightshiftTime < minNightshiftTime {
a.nightshiftTime = minNightshiftTime
}
if a.nightshiftTime > maxNightshiftTime {
a.nightshiftTime = maxNightshiftTime
}
return a.nightshiftTime
}
func (a *adaptive) Target(s Stats) (int, time.Duration) {
t := a.target(s)
ns := a.nightshift(s)
return t, ns
}
func makeMaxTimeout(max int, to time.Duration) *maxTimeout {
return &maxTimeout{
max: max,
to: to,
}
}
func (a *maxTimeout) Target(s Stats) (int, time.Duration) {
if a.max <= 0 && a.to <= 0 {
return s.Idle, 0
}
if a.max > 0 && a.to <= 0 {
t := s.Idle
if t > a.max {
t = a.max
}
return t, 0
}
var zero time.Time
if len(a.items) > s.Idle {
for i := s.Idle; i < len(a.items); i++ {
a.items[i] = zero
}
a.items = a.items[:s.Idle]
if len(a.items) == 0 {
a.items = nil
}
}
now := time.Now()
for len(a.items) < s.Idle {
a.items = append(a.items, now)
}
var drop int
for drop < len(a.items) && a.items[drop].Add(a.to).Before(now) {
a.items[drop] = zero
drop++
}
if drop > 0 {
for i := 0; i < drop; i++ {
a.items[i] = zero
}
a.items = a.items[drop:]
}
if len(a.items) == 0 {
a.items = nil
return 0, 0
}
t := len(a.items)
if a.max > 0 && t > a.max {
t = a.max
}
return t, a.items[0].Add(a.to).Sub(now)
}

View File

@ -1,44 +0,0 @@
package pool_test
import (
"code.squareroundforest.org/arpio/pool"
"time"
)
type maxAlgo int
type timeoutAlgo struct {
to time.Duration
items []time.Time
}
func (a maxAlgo) Target(s pool.Stats) (int, time.Duration) {
if s.Idle <= int(a) {
return s.Idle, 0
}
return int(a), 0
}
func (a *timeoutAlgo) Target(s pool.Stats) (int, time.Duration) {
now := time.Now()
for len(a.items) < s.Idle {
a.items = append(a.items, now)
}
t := s.Idle
for len(a.items) > 0 && a.items[0].Add(a.to).Before(now) {
t--
a.items = a.items[1:]
}
if t < 0 {
t = 0
}
if len(a.items) == 0 {
return t, 0
}
return t, a.items[0].Add(a.to).Sub(now)
}

79
maxto.go Normal file
View File

@ -0,0 +1,79 @@
package pool
import "time"
type maxTimeout struct {
max int
to time.Duration
items []time.Time
lastPut int
}
func makeMaxTimeout(max int, to time.Duration) *maxTimeout {
return &maxTimeout{
max: max,
to: to,
}
}
func (a *maxTimeout) Target(s Stats) (int, time.Duration) {
if a.max <= 0 && a.to <= 0 {
return s.Idle, 0
}
if a.max > 0 && a.to <= 0 {
t := s.Idle
if t > a.max {
t = a.max
}
return t, 0
}
var zero time.Time
if len(a.items) > s.Idle {
for i := s.Idle; i < len(a.items); i++ {
a.items[i] = zero
}
a.items = a.items[:s.Idle]
if len(a.items) == 0 {
a.items = nil
}
}
now := time.Now()
for len(a.items) < s.Idle {
a.items = append(a.items, now)
}
puts := s.Put - a.lastPut
if puts > 0 && len(a.items) >= puts {
a.lastPut = s.Put
for i := len(a.items) - puts; i < len(a.items); i++ {
a.items[i] = now
}
}
var drop int
for drop < len(a.items) && a.items[drop].Add(a.to).Before(now) {
a.items[drop] = zero
drop++
}
if drop > 0 {
a.items = a.items[drop:]
}
if len(a.items) == 0 {
a.items = nil
return 0, 0
}
t := len(a.items)
if a.max > 0 && t > a.max {
t = a.max
}
return t, a.items[0].Add(a.to).Sub(now)
}

View File

@ -202,6 +202,7 @@ func (p pool[R]) freeIdle() {
t, f := p.options.Algo.Target(s.stats)
prev := s.stats.Free
s = p.freeItems(s, t)
s.stats.Idle = len(s.items)
if s.stats.Free > prev {
p.sendEvent(FreeOperation, s.stats)
}