333 lines
6.1 KiB
Go
333 lines
6.1 KiB
Go
package pool_test
|
|
|
|
import (
|
|
"code.squareroundforest.org/arpio/pool"
|
|
"code.squareroundforest.org/arpio/syncbus"
|
|
"code.squareroundforest.org/arpio/times"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestAdaptive(t *testing.T) {
|
|
t.Run("basic", func(t *testing.T) {
|
|
t.Run("no concurrency", func(t *testing.T) {
|
|
testBasicSet(t, scenarioOptions{})
|
|
})
|
|
|
|
t.Run("low concurrency", func(t *testing.T) {
|
|
testBasicSet(t, scenarioOptions{concurrency: 8})
|
|
})
|
|
|
|
t.Run("high concurrency", func(t *testing.T) {
|
|
testBasicSet(t, scenarioOptions{concurrency: 256})
|
|
})
|
|
})
|
|
|
|
t.Run("cyclic", func(t *testing.T) {
|
|
t.Run("no concurrency", func(t *testing.T) {
|
|
testCyclicSet(t, scenarioOptions{})
|
|
})
|
|
|
|
t.Run("low concurrency", func(t *testing.T) {
|
|
testCyclicSet(t, scenarioOptions{concurrency: 8})
|
|
})
|
|
|
|
t.Run("high concurrency", func(t *testing.T) {
|
|
testCyclicSet(t, scenarioOptions{concurrency: 256})
|
|
})
|
|
})
|
|
|
|
t.Run("nightshift", func(t *testing.T) {
|
|
const (
|
|
initial = 15
|
|
variation = 10
|
|
variationCycles = 3
|
|
variationStepTime = 10 * time.Millisecond
|
|
dropStepTime = time.Millisecond
|
|
waitCycles = 36
|
|
waitTime = time.Second
|
|
)
|
|
|
|
bus := syncbus.New(time.Second)
|
|
clock := times.Test()
|
|
o := pool.Options{
|
|
Clock: clock,
|
|
TestBus: bus,
|
|
}
|
|
|
|
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 < initial; i++ {
|
|
get()
|
|
}
|
|
|
|
for i := 0; i <= variationCycles; i++ {
|
|
for j := 0; j < variation; j++ {
|
|
put()
|
|
clock.Pass(variationStepTime)
|
|
}
|
|
|
|
for j := 0; j < variation; j++ {
|
|
get()
|
|
clock.Pass(variationStepTime)
|
|
}
|
|
}
|
|
|
|
for len(active) > 0 {
|
|
put()
|
|
clock.Pass(dropStepTime)
|
|
}
|
|
|
|
for i := 0; i < waitCycles; i++ {
|
|
if err := bus.Wait("background-job-waiting"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bus.ResetSignals("background-job-waiting")
|
|
clock.Pass(waitTime)
|
|
if err := bus.Wait("free-idle-done"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bus.ResetSignals("free-idle-done")
|
|
}
|
|
|
|
if p.Stats().Idle != 0 {
|
|
t.Fatal(p.Stats())
|
|
}
|
|
})
|
|
|
|
t.Run("external put", func(t *testing.T) {
|
|
t.Run("initial", func(t *testing.T) {
|
|
const (
|
|
initialCount = 15
|
|
steadyUseCycles = 8
|
|
)
|
|
|
|
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
|
|
p := pool.Make(alloc, nil, pool.Options{})
|
|
|
|
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()
|
|
put()
|
|
}
|
|
|
|
s := p.Stats()
|
|
e := pool.Stats{Idle: 0, Active: 0, Get: 8, Put: 23, Alloc: 8, Free: 23}
|
|
if s != e {
|
|
t.Fatal(s)
|
|
}
|
|
})
|
|
|
|
t.Run("expect higher load", func(t *testing.T) {
|
|
const (
|
|
initialCount = 15
|
|
steadyUseCycles = 8
|
|
adjustCount = 15
|
|
highLoadCycles = 8
|
|
)
|
|
|
|
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
|
|
p := pool.Make(alloc, nil, pool.Options{})
|
|
|
|
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()
|
|
put()
|
|
}
|
|
|
|
for i := 0; i < adjustCount; i++ {
|
|
p.Put(make([]byte, 1<<9))
|
|
}
|
|
|
|
for i := 0; i < highLoadCycles; i++ {
|
|
get()
|
|
get()
|
|
put()
|
|
}
|
|
|
|
s := p.Stats()
|
|
e := pool.Stats{Idle: 1, Active: 8, Get: 39, Put: 31, Alloc: 20, Free: 11}
|
|
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
|
|
)
|
|
|
|
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
|
|
p := pool.Make(alloc, nil, pool.Options{})
|
|
|
|
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()
|
|
put()
|
|
}
|
|
|
|
s := p.Stats()
|
|
e := pool.Stats{Idle: 3, Active: 0, Get: 8, Put: 8, Alloc: 0, Free: 12}
|
|
if s != e {
|
|
t.Fatal(s)
|
|
}
|
|
})
|
|
|
|
t.Run("expect higher load", func(t *testing.T) {
|
|
const (
|
|
initialCount = 15
|
|
steadyUseCycles = 8
|
|
adjustCount = 15
|
|
highLoadCycles = 8
|
|
)
|
|
|
|
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
|
|
p := pool.Make(alloc, nil, pool.Options{})
|
|
|
|
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()
|
|
put()
|
|
}
|
|
|
|
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()
|
|
get()
|
|
put()
|
|
}
|
|
|
|
s := p.Stats()
|
|
e := pool.Stats{Idle: 8, Active: 23, Get: 39, Put: 16, Alloc: 16, Free: 0}
|
|
if s != e {
|
|
t.Fatal(s)
|
|
}
|
|
})
|
|
})
|
|
}
|