зеркало из https://github.com/github/vitess-gh.git
improve cache pool unit test
1. add verify tablet error test func in testutils_test.go 2. add methods to enable/disable fakecacheservice
This commit is contained in:
Родитель
b10354ed0b
Коммит
b87e7d0329
|
@ -6,9 +6,11 @@ package tabletserver
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -188,7 +190,7 @@ func TestCachePoolGetFailedBecauseCachePoolIsClosed(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCachePoolStatsURL(t *testing.T) {
|
||||
fakecacheservice.Register()
|
||||
cache := fakecacheservice.Register()
|
||||
fakesqldb.Register()
|
||||
rowCacheConfig := RowCacheConfig{
|
||||
Binary: "ls",
|
||||
|
@ -198,15 +200,66 @@ func TestCachePoolStatsURL(t *testing.T) {
|
|||
idleTimeout := 1 * time.Second
|
||||
cachePool.idleTimeout = idleTimeout
|
||||
cachePool.Open()
|
||||
defer cachePool.Close()
|
||||
request, _ := http.NewRequest("GET", cachePool.statsURL, nil)
|
||||
request, _ := http.NewRequest("GET", fmt.Sprintf("%sstats", cachePool.statsURL), nil)
|
||||
response := httptest.NewRecorder()
|
||||
cachePool.ServeHTTP(response, request)
|
||||
// any memcache calls should fail
|
||||
cache.EnableCacheServiceError()
|
||||
response = httptest.NewRecorder()
|
||||
cachePool.ServeHTTP(response, request)
|
||||
cache.DisableCacheServiceError()
|
||||
cachePool.Close()
|
||||
response = httptest.NewRecorder()
|
||||
cachePool.ServeHTTP(response, request)
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
matcher := regexp.MustCompile("closed")
|
||||
if !matcher.Match(body) {
|
||||
t.Fatalf("stats page should contain 'closed', but got %s", string(body))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCachePoolMemcacheStatsFail(t *testing.T) {
|
||||
cache := fakecacheservice.Register()
|
||||
fakesqldb.Register()
|
||||
rowCacheConfig := RowCacheConfig{
|
||||
Binary: "ls",
|
||||
Connections: 100,
|
||||
}
|
||||
cachePool := newTestCachePool(rowCacheConfig, true)
|
||||
idleTimeout := 1 * time.Second
|
||||
cachePool.idleTimeout = idleTimeout
|
||||
cachePool.Open()
|
||||
defer cachePool.Close()
|
||||
memcacheStatsBefore := internalErrors.Counts()["MemcacheStats"]
|
||||
// any memcache calls should fail
|
||||
cache.EnableCacheServiceError()
|
||||
cachePool.memcacheStats.update()
|
||||
memcacheStatsAfter := internalErrors.Counts()["MemcacheStats"]
|
||||
if memcacheStatsAfter <= memcacheStatsBefore {
|
||||
t.Fatalf("memcache stats should cause an internal error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCachePoolFailToStartBecauseCacheServiceWasDown(t *testing.T) {
|
||||
cache := fakecacheservice.Register()
|
||||
fakesqldb.Register()
|
||||
testUtils := &testUtils{}
|
||||
rowCacheConfig := RowCacheConfig{
|
||||
Binary: "ls",
|
||||
Connections: 100,
|
||||
}
|
||||
cachePool := newTestCachePool(rowCacheConfig, false)
|
||||
idleTimeout := 1 * time.Second
|
||||
cachePool.idleTimeout = idleTimeout
|
||||
// any memcache calls should fail
|
||||
cache.EnableCacheServiceError()
|
||||
defer testUtils.checkTabletErrorWithRecover(t, ErrFatal, "can't communicate with cache service")
|
||||
cachePool.Open()
|
||||
}
|
||||
|
||||
func newTestCachePool(rowcacheConfig RowCacheConfig, enablePublishStats bool) *CachePool {
|
||||
randID := rand.Int63()
|
||||
name := fmt.Sprintf("TestCachePool-%d-", randID)
|
||||
statsURL := fmt.Sprintf("/debug/cache-%d", randID)
|
||||
statsURL := fmt.Sprintf("/debug/cache-%d/", randID)
|
||||
return NewCachePool(name, rowcacheConfig, 1*time.Second, statsURL, enablePublishStats)
|
||||
}
|
||||
|
|
|
@ -12,8 +12,11 @@ import (
|
|||
"time"
|
||||
|
||||
cs "github.com/youtube/vitess/go/cacheservice"
|
||||
"github.com/youtube/vitess/go/sync2"
|
||||
)
|
||||
|
||||
var errCacheService = "cacheservice error"
|
||||
|
||||
// FakeCacheService is a fake implementation of CacheService
|
||||
type FakeCacheService struct {
|
||||
cache *Cache
|
||||
|
@ -21,8 +24,9 @@ type FakeCacheService struct {
|
|||
|
||||
// Cache is a cache like data structure.
|
||||
type Cache struct {
|
||||
data map[string]*cs.Result
|
||||
mu sync.Mutex
|
||||
mu sync.Mutex
|
||||
data map[string]*cs.Result
|
||||
enableCacheServiceError sync2.AtomicInt32
|
||||
}
|
||||
|
||||
// Set sets a key and associated value to the cache.
|
||||
|
@ -61,6 +65,16 @@ func (cache *Cache) Clear() {
|
|||
cache.data = make(map[string]*cs.Result)
|
||||
}
|
||||
|
||||
// EnableCacheServiceError makes cache service return error.
|
||||
func (cache *Cache) EnableCacheServiceError() {
|
||||
cache.enableCacheServiceError.Set(1)
|
||||
}
|
||||
|
||||
// DisableCacheServiceError makes cache service back to normal.
|
||||
func (cache *Cache) DisableCacheServiceError() {
|
||||
cache.enableCacheServiceError.Set(0)
|
||||
}
|
||||
|
||||
// NewFakeCacheService creates a FakeCacheService
|
||||
func NewFakeCacheService(cache *Cache) *FakeCacheService {
|
||||
return &FakeCacheService{
|
||||
|
@ -70,6 +84,9 @@ func NewFakeCacheService(cache *Cache) *FakeCacheService {
|
|||
|
||||
// Get returns cached data for given keys.
|
||||
func (service *FakeCacheService) Get(keys ...string) ([]cs.Result, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return nil, fmt.Errorf(errCacheService)
|
||||
}
|
||||
results := make([]cs.Result, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
if val, ok := service.cache.Get(key); ok {
|
||||
|
@ -83,6 +100,9 @@ func (service *FakeCacheService) Get(keys ...string) ([]cs.Result, error) {
|
|||
// for using with CAS. Gets returns a CAS identifier with the item. If
|
||||
// the item's CAS value has changed since you Gets'ed it, it will not be stored.
|
||||
func (service *FakeCacheService) Gets(keys ...string) ([]cs.Result, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return nil, fmt.Errorf(errCacheService)
|
||||
}
|
||||
results := make([]cs.Result, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
if val, ok := service.cache.Get(key); ok {
|
||||
|
@ -96,6 +116,9 @@ func (service *FakeCacheService) Gets(keys ...string) ([]cs.Result, error) {
|
|||
|
||||
// Set set the value with specified cache key.
|
||||
func (service *FakeCacheService) Set(key string, flags uint16, timeout uint64, value []byte) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
service.cache.Set(key, &cs.Result{
|
||||
Key: key,
|
||||
Value: value,
|
||||
|
@ -107,6 +130,9 @@ func (service *FakeCacheService) Set(key string, flags uint16, timeout uint64, v
|
|||
|
||||
// Add store the value only if it does not already exist.
|
||||
func (service *FakeCacheService) Add(key string, flags uint16, timeout uint64, value []byte) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
if _, ok := service.cache.Get(key); ok {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -122,6 +148,9 @@ func (service *FakeCacheService) Add(key string, flags uint16, timeout uint64, v
|
|||
// Replace replaces the value, only if the value already exists,
|
||||
// for the specified cache key.
|
||||
func (service *FakeCacheService) Replace(key string, flags uint16, timeout uint64, value []byte) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
result, ok := service.cache.Get(key)
|
||||
if !ok {
|
||||
return false, nil
|
||||
|
@ -134,6 +163,9 @@ func (service *FakeCacheService) Replace(key string, flags uint16, timeout uint6
|
|||
|
||||
// Append appends the value after the last bytes in an existing item.
|
||||
func (service *FakeCacheService) Append(key string, flags uint16, timeout uint64, value []byte) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
result, ok := service.cache.Get(key)
|
||||
if !ok {
|
||||
return false, nil
|
||||
|
@ -146,6 +178,9 @@ func (service *FakeCacheService) Append(key string, flags uint16, timeout uint64
|
|||
|
||||
// Prepend prepends the value before existing value.
|
||||
func (service *FakeCacheService) Prepend(key string, flags uint16, timeout uint64, value []byte) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
result, ok := service.cache.Get(key)
|
||||
if !ok {
|
||||
return false, nil
|
||||
|
@ -158,6 +193,9 @@ func (service *FakeCacheService) Prepend(key string, flags uint16, timeout uint6
|
|||
|
||||
// Cas stores the value only if no one else has updated the data since you read it last.
|
||||
func (service *FakeCacheService) Cas(key string, flags uint16, timeout uint64, value []byte, cas uint64) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
result, ok := service.cache.Get(key)
|
||||
if !ok || result.Cas != cas {
|
||||
return false, nil
|
||||
|
@ -171,18 +209,27 @@ func (service *FakeCacheService) Cas(key string, flags uint16, timeout uint64, v
|
|||
|
||||
// Delete delete the value for the specified cache key.
|
||||
func (service *FakeCacheService) Delete(key string) (bool, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return false, fmt.Errorf(errCacheService)
|
||||
}
|
||||
service.cache.Delete(key)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// FlushAll purges the entire cache.
|
||||
func (service *FakeCacheService) FlushAll() error {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return fmt.Errorf(errCacheService)
|
||||
}
|
||||
service.cache.Clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stats returns a list of basic stats.
|
||||
func (service *FakeCacheService) Stats(key string) ([]byte, error) {
|
||||
if service.cache.enableCacheServiceError.Get() == 1 {
|
||||
return nil, fmt.Errorf(errCacheService)
|
||||
}
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -122,3 +122,50 @@ func TestFakeCacheService(t *testing.T) {
|
|||
service.Stats("")
|
||||
service.Close()
|
||||
}
|
||||
|
||||
func TestFakeCacheServiceError(t *testing.T) {
|
||||
service := NewFakeCacheService(&Cache{data: make(map[string]*cs.Result)})
|
||||
service.cache.EnableCacheServiceError()
|
||||
key1 := "key1"
|
||||
_, err := service.Set(key1, 0, 0, []byte("test"))
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Get(key1)
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Gets(key1)
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Cas(key1, 0, 0, []byte("test2"), 0)
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Add(key1, 0, 0, []byte("test3"))
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Replace("unknownKey", 0, 0, []byte("test4"))
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Append("unknownKey", 0, 0, []byte("test5"))
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Prepend("unknownKey", 0, 0, []byte("test5"))
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Prepend(key1, 0, 0, []byte("test5"))
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Delete(key1)
|
||||
checkCacheServiceError(t, err)
|
||||
err = service.FlushAll()
|
||||
checkCacheServiceError(t, err)
|
||||
_, err = service.Stats("")
|
||||
checkCacheServiceError(t, err)
|
||||
|
||||
service.cache.DisableCacheServiceError()
|
||||
ok, err := service.Set(key1, 0, 0, []byte("test"))
|
||||
if !ok || err != nil {
|
||||
t.Fatalf("set should succeed")
|
||||
}
|
||||
results, err := service.Get(key1)
|
||||
if !reflect.DeepEqual(results[0].Value, []byte("test")) {
|
||||
t.Fatalf("expect to get value: test, but get: %s", string(results[0].Value))
|
||||
}
|
||||
service.Close()
|
||||
}
|
||||
|
||||
func checkCacheServiceError(t *testing.T, err error) {
|
||||
if err.Error() != errCacheService {
|
||||
t.Fatalf("should get cacheservice error")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"html/template"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -44,6 +45,43 @@ func (util *testUtils) checkEqual(t *testing.T, expected interface{}, result int
|
|||
}
|
||||
}
|
||||
|
||||
func (util *testUtils) checkTabletErrorWithRecover(t *testing.T, tabletErrType int, tabletErrStr string) {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
t.Fatalf("should get error")
|
||||
}
|
||||
util.checkTabletError(t, err, tabletErrType, tabletErrStr)
|
||||
}
|
||||
|
||||
func (util *testUtils) checkTabletError(t *testing.T, err interface{}, tabletErrType int, tabletErrStr string) {
|
||||
tabletError, ok := err.(*TabletError)
|
||||
if !ok {
|
||||
t.Fatalf("should return a TabletError, but got err: %v", err)
|
||||
}
|
||||
if tabletError.ErrorType != tabletErrType {
|
||||
t.Fatalf("should return a TabletError with error type: %s", util.getTabletErrorString(tabletErrType))
|
||||
}
|
||||
if !strings.Contains(tabletError.Error(), tabletErrStr) {
|
||||
t.Fatalf("expect the tablet error should contain string: '%s', but it does not. Got tablet error: '%s'", tabletErrStr, tabletError.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (util *testUtils) getTabletErrorString(tabletErrorType int) string {
|
||||
switch tabletErrorType {
|
||||
case ErrFail:
|
||||
return "ErrFail"
|
||||
case ErrRetry:
|
||||
return "ErrRetry"
|
||||
case ErrFatal:
|
||||
return "ErrFatal"
|
||||
case ErrTxPoolFull:
|
||||
return "ErrTxPoolFull"
|
||||
case ErrNotInTx:
|
||||
return "ErrNotInTx"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func newTestSchemaInfo(
|
||||
queryCacheSize int,
|
||||
reloadTime time.Duration,
|
||||
|
|
Загрузка…
Ссылка в новой задаче