
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
332 lines
7.6 KiB
Go
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")
|
|
}
|
|
}
|
|
}
|
|
}
|