1
0
pool/maxto_test.go

465 lines
9.0 KiB
Go

package pool_test
import (
"code.squareroundforest.org/arpio/pool"
"code.squareroundforest.org/arpio/times"
"testing"
"time"
)
func TestMaxTO(t *testing.T) {
t.Run("noshrink", func(t *testing.T) {
base := scenarioOptions{algo: pool.NoShrink()}
t.Run("basic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
testBasicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testBasicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
testBasicSet(t, o)
})
})
t.Run("cyclic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
testCyclicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testCyclicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
testCyclicSet(t, o)
})
})
})
t.Run("max", func(t *testing.T) {
base := scenarioOptions{algo: pool.Max(60)}
t.Run("basic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
testBasicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testBasicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
o.exclude = []string{
"steady_step_up_small",
"steady_step_up_large",
"slow_rise_from_zero_small",
"slow_rise_from_zero_large",
}
testBasicSet(t, o)
})
})
t.Run("cyclic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
testCyclicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testCyclicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
testCyclicSet(t, o)
})
})
})
t.Run("to", func(t *testing.T) {
base := scenarioOptions{
algo: pool.Timeout(300 * time.Millisecond),
minDelay: 10 * time.Millisecond,
maxDelay: 100 * time.Millisecond,
}
t.Run("basic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
o.exclude = []string{
"steady_step_up_large",
"slow_rise_from_zero_small",
"slow_rise_from_zero_large",
}
testBasicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testBasicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
testBasicSet(t, o)
})
})
t.Run("cyclic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
o.exclude = []string{"sinus_large"}
testCyclicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testCyclicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
testCyclicSet(t, o)
})
})
})
t.Run("maxto", func(t *testing.T) {
base := scenarioOptions{
algo: pool.MaxTimeout(60, 300*time.Millisecond),
minDelay: 10 * time.Millisecond,
maxDelay: 100 * time.Millisecond,
}
t.Run("basic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
o.exclude = []string{
"steady_step_up_large",
"slow_rise_from_zero_small",
"slow_rise_from_zero_large",
}
testBasicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testBasicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
o.exclude = []string{
"steady_step_up_large",
"steady_step_up_small",
}
testBasicSet(t, o)
})
})
t.Run("cyclic", func(t *testing.T) {
t.Run("no concurrency", func(t *testing.T) {
o := base
o.exclude = []string{"sinus_large"}
testCyclicSet(t, o)
})
t.Run("low concurrency", func(t *testing.T) {
o := base
o.concurrency = 8
testCyclicSet(t, o)
})
t.Run("high concurrency", func(t *testing.T) {
o := base
o.concurrency = 256
testCyclicSet(t, o)
})
})
})
t.Run("external put", func(t *testing.T) {
t.Run("initial", func(t *testing.T) {
const (
initialCount = 15
steadyUseCycles = 8
stepDuration = 30 * time.Millisecond
)
clock := times.Test()
o := pool.Options{
Clock: clock,
Algo: pool.MaxTimeout(15, 300*time.Millisecond),
}
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, o)
var active [][]byte
get := func() {
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
active = append(active, b)
}
put := func() {
if len(active) == 0 {
t.Fatal("put called from empty active")
}
var b []byte
b, active = active[0], active[1:]
p.Put(b)
}
for i := 0; i < initialCount; i++ {
p.Put(make([]byte, 1<<9))
}
for i := 0; i < steadyUseCycles; i++ {
get()
clock.Pass(stepDuration)
put()
clock.Pass(stepDuration)
}
s := p.Stats()
e := pool.Stats{Idle: 1, Active: 0, Get: 8, Put: 23, Alloc: 0, Free: 14}
if s != e {
t.Fatal(s)
}
})
t.Run("expect higher load", func(t *testing.T) {
const (
initialCount = 15
steadyUseCycles = 8
adjustCount = 15
highLoadCycles = 8
stepDuration = 30 * time.Millisecond
)
clock := times.Test()
o := pool.Options{
Clock: clock,
Algo: pool.MaxTimeout(15, 300*time.Millisecond),
}
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, o)
var active [][]byte
get := func() {
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
active = append(active, b)
}
put := func() {
if len(active) == 0 {
t.Fatal("put called from empty active")
}
var b []byte
b, active = active[0], active[1:]
p.Put(b)
}
for i := 0; i < initialCount; i++ {
get()
}
for i := 0; i < steadyUseCycles; i++ {
put()
clock.Pass(stepDuration)
get()
clock.Pass(stepDuration)
}
for i := 0; i < adjustCount; i++ {
p.Put(make([]byte, 1<<9))
}
for i := 0; i < highLoadCycles; i++ {
get()
clock.Pass(stepDuration)
get()
clock.Pass(stepDuration)
put()
clock.Pass(stepDuration)
}
s := p.Stats()
e := pool.Stats{Idle: 1, Active: 8, Get: 39, Put: 31, Alloc: 19, Free: 10}
if s != e {
t.Fatal(s)
}
})
})
t.Run("load", func(t *testing.T) {
t.Run("prewarm", func(t *testing.T) {
const (
initialCount = 15
steadyUseCycles = 8
stepDuration = 30 * time.Millisecond
)
clock := times.Test()
o := pool.Options{
Clock: clock,
Algo: pool.MaxTimeout(15, 300*time.Millisecond),
}
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, o)
var active [][]byte
get := func() {
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
active = append(active, b)
}
put := func() {
if len(active) == 0 {
t.Fatal("put called from empty active")
}
var b []byte
b, active = active[0], active[1:]
p.Put(b)
}
l := make([][]byte, initialCount)
for i := 0; i < initialCount; i++ {
l[i] = make([]byte, 1<<9)
}
p.Load(l)
for i := 0; i < steadyUseCycles; i++ {
get()
clock.Pass(stepDuration)
put()
clock.Pass(stepDuration)
}
s := p.Stats()
e := pool.Stats{Idle: 1, Active: 0, Get: 8, Put: 8, Alloc: 0, Load: initialCount, Free: 14}
if s != e {
t.Fatal(s)
}
})
t.Run("expect higher load", func(t *testing.T) {
const (
initialCount = 15
steadyUseCycles = 8
adjustCount = 15
highLoadCycles = 8
stepDuration = 30 * time.Millisecond
)
clock := times.Test()
o := pool.Options{
Clock: clock,
Algo: pool.MaxTimeout(15, 300*time.Millisecond),
}
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, o)
var active [][]byte
get := func() {
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
active = append(active, b)
}
put := func() {
if len(active) == 0 {
t.Fatal("put called from empty active")
}
var b []byte
b, active = active[0], active[1:]
p.Put(b)
}
for i := 0; i < initialCount; i++ {
get()
}
for i := 0; i < steadyUseCycles; i++ {
get()
clock.Pass(stepDuration)
put()
clock.Pass(stepDuration)
}
l := make([][]byte, adjustCount)
for i := 0; i < adjustCount; i++ {
l[i] = make([]byte, 1<<9)
}
p.Load(l)
for i := 0; i < highLoadCycles; i++ {
get()
clock.Pass(stepDuration)
get()
clock.Pass(stepDuration)
put()
clock.Pass(stepDuration)
}
s := p.Stats()
e := pool.Stats{Idle: 1, Active: 23, Get: 39, Put: 16, Alloc: 20, Load: adjustCount, Free: 11}
if s != e {
t.Fatal(s)
}
})
})
}