465 lines
8.9 KiB
Go
465 lines
8.9 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, 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, Free: 11}
|
||
|
|
if s != e {
|
||
|
|
t.Fatal(s)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
}
|