Files
swift/go/hummingbird/memcachering_test.go
Michael Barton ca9591de34 go: conf.d support + some refactoring
Refactor how configs are loaded and passed to daemons.

Add conf.d support. So you can have an /etc/swift/object-server.conf.d
or /etc/swift/object-server/1.conf.d, and any .conf files under it
will be loaded as one config.

conf.d files are combined in filename lexigraphical order, with any
sections in later files replace existing section entries.

Change-Id: I78d7e5449cd0b62df9dfdb25616a2d4c91159f8c
2016-05-03 15:58:47 +00:00

332 lines
7.6 KiB
Go

package hummingbird
import (
"bytes"
"fmt"
"net"
"os"
"os/exec"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
ini "github.com/vaughan0/go-ini"
)
func TestLocalHost(t *testing.T) {
// construct ini and check for running memcache servers
var buffer bytes.Buffer
iniFile := Config{File: make(ini.File)}
for i := 0; i < 4; i++ {
server := fmt.Sprintf("localhost:%d", 10000+i)
if i > 0 {
buffer.WriteString(",")
}
buffer.WriteString(server)
if i < 3 {
c, err := net.Dial("tcp", server)
if err != nil {
t.Skipf("skipping test; no server running at %s", server)
return
}
c.Write([]byte("flush_all\r\n"))
c.Close()
}
}
section := iniFile.Section("memcache")
section["dial_timeout"] = "1000"
section["max_free_connections_per_server"] = "3"
section["memcache_servers"] = buffer.String()
ring, err := NewMemcacheRingFromConfig(iniFile)
if err != nil {
t.Fatal("Unable to get memcache ring")
}
testWithRing(t, ring)
}
func TestUnixSocket(t *testing.T) {
// construct ini and start memcache servers
var buffer bytes.Buffer
iniFile := Config{File: make(ini.File)}
for i := 0; i < 4; i++ {
sock := fmt.Sprintf("/tmp/test-memecachering-%d", i)
if err := os.Remove(sock); err != nil {
if !os.IsNotExist(err) {
t.Fatalf("")
}
}
if i > 0 {
buffer.WriteString(",")
}
buffer.WriteString(sock)
// don't start a memcache server for last sock
if i < 3 {
// start memcache server
cmd := exec.Command("memcached", "-u", "memcache", "-s", sock)
if err := cmd.Start(); err != nil {
t.Fatal("Unable to run memcached")
return
}
defer cmd.Wait()
defer cmd.Process.Kill()
// Wait a bit for the socket to appear.
for i := 1; i < 10; i++ {
if _, err := os.Stat(sock); err == nil {
break
}
time.Sleep(time.Duration(25*i) * time.Millisecond)
}
}
}
time.Sleep(time.Duration(25) * time.Millisecond)
section := iniFile.Section("memcache")
section["dial_timeout"] = "1000"
section["max_free_connections_per_server"] = "3"
section["memcache_servers"] = buffer.String()
ring, err := NewMemcacheRingFromConfig(iniFile)
if err != nil {
t.Fatal("Unable to get memcache ring")
}
testWithRing(t, ring)
}
func testWithRing(t *testing.T, ring *memcacheRing) {
testSetGetDelete(t, ring)
testGetCacheMiss(t, ring)
testIncr(t, ring)
testDecr(t, ring)
testSetGetMulti(t, ring)
testManySetGets(t, ring)
testConnectionLimits(t, ring)
testExpiration(t, ring)
}
func testSetGetDelete(t *testing.T, ring *memcacheRing) {
key := "testJsonSetGet"
setValue := "some_value"
if err := ring.Set(key, setValue, 0); err != nil {
t.Error(err)
return
}
if getValue, err := ring.Get(key); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, setValue, getValue)
}
if err := ring.Delete(key); err != nil {
t.Error(err)
return
}
if _, err := ring.Get(key); err != nil {
assert.EqualValues(t, CacheMiss, err)
} else {
t.Errorf("Expected a cache miss")
}
}
func testGetCacheMiss(t *testing.T, ring *memcacheRing) {
// make a call to an unset cache value and check for cache miss return
key := "testGetCacheMiss"
if _, err := ring.Get(key); err != nil {
assert.EqualValues(t, CacheMiss, err)
} else {
t.Errorf("Expected a cache miss")
}
}
func testIncr(t *testing.T, ring *memcacheRing) {
// increment multiple times and check running total
key := "testIncr"
var running_total int64 = 0
for i := 1; i <= 10; i++ {
running_total += int64(i)
if j, err := ring.Incr(key, i, 2); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, running_total, j)
if value, err := ring.Incr(key, 0, 2); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, running_total, value)
}
}
}
}
func testDecr(t *testing.T, ring *memcacheRing) {
key := "testDecr"
// test decrement on unset value sets value to 0
if value, err := ring.Decr(key, 1, 2); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, int64(0), value)
}
// test running total goes to and stays at zero
var running_total int = 30
if value, err := ring.Incr(key, running_total, 2); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, running_total, value)
}
for i := 1; i <= 10; i++ {
if i < running_total {
running_total -= i
} else {
running_total = 0
}
if j, err := ring.Decr(key, i, 2); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, running_total, j)
if value, err := ring.Incr(key, 0, 2); err != nil {
t.Error(err)
return
} else {
assert.EqualValues(t, running_total, value)
}
}
}
}
func testSetGetMulti(t *testing.T, ring *memcacheRing) {
key := "testSetGetMulti"
// set three values at once
setValues := map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
}
if err := ring.SetMulti(key, setValues, 0); err != nil {
t.Error(err)
return
}
// get the three values
getValues, err := ring.GetMulti(key, []string{"key1", "key2", "key3"})
if err != nil {
t.Error(err)
return
}
// verify the values match what was set
found := []string{}
for getKey, getValue := range getValues {
for setKey, setValue := range setValues {
if getKey == setKey {
assert.EqualValues(t, setValue, getValue)
found = append(found, getKey)
}
}
}
assert.EqualValues(t, len(setValues), len(found))
}
func testManySetGets(t *testing.T, ring *memcacheRing) {
key := "testManySetGets"
opCount := 100
var wg sync.WaitGroup
setIt := func(i int) {
defer wg.Done()
setKey := fmt.Sprintf("%s%d", key, i)
setValue := fmt.Sprintf("value%d", i)
if err := ring.Set(setKey, setValue, 0); err != nil {
t.Error(err)
}
}
// set a bunch of keys
wg.Add(opCount)
for i := 1; i <= opCount; i++ {
go setIt(i)
}
wg.Wait()
getIt := func(i int) {
defer wg.Done()
getKey := fmt.Sprintf("%s%d", key, i)
setValue := fmt.Sprintf("value%d", i)
if getValue, err := ring.Get(getKey); err != nil {
t.Error(err)
} else {
assert.EqualValues(t, setValue, getValue)
}
}
// get a bunch of keys
wg.Add(opCount)
for i := 1; i <= opCount; i++ {
go getIt(i)
}
wg.Wait()
}
func testConnectionLimits(t *testing.T, ring *memcacheRing) {
key := "testConnectionLimits"
opCount := 1000
var wg sync.WaitGroup
setIt := func(i int) {
defer wg.Done()
setKey := fmt.Sprintf("%s%d", key, i)
if err := ring.Set(setKey, "", 0); err != nil {
t.Error(err)
}
}
// set a bunck of keys
wg.Add(opCount)
for i := 0; i < opCount; i++ {
go setIt(i)
}
wg.Wait()
// verify connections per server are less than the limit
for _, server := range ring.servers {
assert.True(t, server.connectionCount() <= 3)
}
}
func testExpiration(t *testing.T, ring *memcacheRing) {
if testing.Short() {
t.Log("Skipping testExpiration()")
return
}
key := "testExpiration"
opCount := 100
var wg sync.WaitGroup
setIt := func(i int, expiration int) {
defer wg.Done()
setKey := fmt.Sprintf("%s%d", key, i)
if err := ring.Set(setKey, "", expiration); err != nil {
t.Error(err)
}
}
// test both ways of setting expiration
for i := 0; i < 2; i++ {
var expiration int
if i == 0 {
expiration = 1
} else {
now := time.Now()
expiration = int(now.Unix()) + 1
}
wg.Add(opCount)
// set a bunch of keys with expiration
for i := 0; i < opCount; i++ {
go setIt(i, expiration)
}
wg.Wait()
// sleep so the items will expire
time.Sleep(2 * time.Second)
// get a bunch of keys and verify cache miss
for i := 0; i < opCount; i++ {
getKey := fmt.Sprintf("%s%d", key, i)
if _, err := ring.Get(getKey); err != nil {
assert.EqualValues(t, CacheMiss, err)
} else {
t.Errorf("Expected a cache miss")
}
}
}
}