2013-08-31 07:03:44 +02:00
|
|
|
// Copyright 2013 The Chihaya Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by the BSD 2-Clause license,
|
|
|
|
// which can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package redis
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
|
2013-08-31 21:06:42 +02:00
|
|
|
"github.com/garyburd/redigo/redis"
|
|
|
|
|
|
|
|
"github.com/pushrax/chihaya/cache"
|
|
|
|
"github.com/pushrax/chihaya/config"
|
2013-09-01 21:59:46 +02:00
|
|
|
"github.com/pushrax/chihaya/models"
|
2013-08-31 07:03:44 +02:00
|
|
|
)
|
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
// Maximum number of parallel retries; depends on system latency
|
2013-08-31 21:06:42 +02:00
|
|
|
const MAX_RETRIES = 9000
|
2013-09-07 02:59:12 +02:00
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
const sample_infohash = "58c290f4ea1efb3adcb8c1ed2643232117577bcd"
|
|
|
|
const sample_passkey = "32426b162be0bce5428e7e36afaf734ae5afb355"
|
2013-08-31 07:03:44 +02:00
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
// Common interface for benchmarks and test error reporting
|
2013-09-01 21:59:46 +02:00
|
|
|
type TestReporter interface {
|
|
|
|
Error(args ...interface{})
|
2013-09-02 01:05:48 +02:00
|
|
|
Errorf(format string, args ...interface{})
|
|
|
|
Log(args ...interface{})
|
|
|
|
Logf(format string, args ...interface{})
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func verifyErrNil(err error, t TestReporter) {
|
2013-08-31 07:03:44 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
2013-08-31 07:03:44 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
// Legacy JSON support for benching
|
|
|
|
func (tx *Tx) initiateWrite() error {
|
|
|
|
if tx.done {
|
|
|
|
return cache.ErrTxDone
|
|
|
|
}
|
|
|
|
if tx.multi != true {
|
|
|
|
tx.multi = true
|
|
|
|
return tx.Send("MULTI")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *Tx) initiateRead() error {
|
|
|
|
if tx.done {
|
|
|
|
return cache.ErrTxDone
|
|
|
|
}
|
|
|
|
if tx.multi == true {
|
|
|
|
panic("Tried to read during MULTI")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
func createTestTxObj(t TestReporter) *Tx {
|
2013-09-01 21:59:46 +02:00
|
|
|
testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH"))
|
|
|
|
conf := &testConfig.Cache
|
|
|
|
verifyErrNil(err, t)
|
|
|
|
|
|
|
|
testPool := &Pool{
|
|
|
|
conf: conf,
|
|
|
|
pool: redis.Pool{
|
2013-09-07 00:39:14 +02:00
|
|
|
MaxIdle: conf.MaxIdleConns,
|
2013-09-01 21:59:46 +02:00
|
|
|
IdleTimeout: conf.IdleTimeout.Duration,
|
|
|
|
Dial: makeDialFunc(conf),
|
|
|
|
TestOnBorrow: testOnBorrow,
|
|
|
|
},
|
2013-08-31 07:03:44 +02:00
|
|
|
}
|
2013-09-01 21:59:46 +02:00
|
|
|
|
|
|
|
//testDialFunc := makeDialFunc(&testConfig.Cache)
|
|
|
|
//testConn, err := testDialFunc()
|
|
|
|
txObj := &Tx{
|
|
|
|
conf: testPool.conf,
|
|
|
|
done: false,
|
|
|
|
multi: false,
|
|
|
|
Conn: testPool.pool.Get(),
|
|
|
|
}
|
|
|
|
verifyErrNil(err, t)
|
|
|
|
|
|
|
|
// Test connection before returning
|
|
|
|
//txObj := Tx{&testConfig.Cache, false, false, testConn}
|
|
|
|
_, err = txObj.Do("PING")
|
|
|
|
verifyErrNil(err, t)
|
|
|
|
return txObj
|
2013-08-31 07:03:44 +02:00
|
|
|
}
|
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func createTestUser() models.User {
|
|
|
|
testUser := models.User{214, "32426b162be0bce5428e7e36afaf734ae5afb355", 1.01, 1.0, 4, 2, 7}
|
2013-09-02 01:05:48 +02:00
|
|
|
return testUser
|
|
|
|
}
|
|
|
|
|
2013-09-04 05:58:13 +02:00
|
|
|
func createSeeders() []models.Peer {
|
2013-09-02 01:05:48 +02:00
|
|
|
testSeeders := make([]models.Peer, 4)
|
|
|
|
testSeeders[0] = models.Peer{"testPeerID0", 57005, 48879, "testIP", 6889, 1024, 3000, 4200, 6}
|
|
|
|
testSeeders[1] = models.Peer{"testPeerID1", 10101, 48879, "testIP", 6889, 1024, 3000, 4200, 6}
|
|
|
|
testSeeders[2] = models.Peer{"testPeerID2", 29890, 48879, "testIP", 6889, 1024, 3000, 4200, 6}
|
|
|
|
testSeeders[3] = models.Peer{"testPeerID3", 65261, 48879, "testIP", 6889, 1024, 3000, 4200, 6}
|
2013-09-04 05:58:13 +02:00
|
|
|
return testSeeders
|
|
|
|
}
|
|
|
|
|
|
|
|
func createLeechers() []models.Peer {
|
2013-09-02 01:05:48 +02:00
|
|
|
testLeechers := make([]models.Peer, 1)
|
|
|
|
testLeechers[0] = models.Peer{"testPeerID", 11111, 48879, "testIP", 6889, 1024, 3000, 4200, 6}
|
2013-09-04 05:58:13 +02:00
|
|
|
return testLeechers
|
|
|
|
}
|
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func createTestTorrent() models.Torrent {
|
2013-09-04 05:58:13 +02:00
|
|
|
|
|
|
|
testSeeders := createSeeders()
|
|
|
|
testLeechers := createLeechers()
|
2013-09-02 01:05:48 +02:00
|
|
|
|
|
|
|
seeders := make(map[string]models.Peer)
|
|
|
|
for i := range testSeeders {
|
|
|
|
seeders[testSeeders[i].ID] = testSeeders[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
leechers := make(map[string]models.Peer)
|
|
|
|
for i := range testLeechers {
|
|
|
|
leechers[testLeechers[i].ID] = testLeechers[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
testTorrent := models.Torrent{48879, sample_infohash, true, seeders, leechers, 11, 0.0, 0.0, 0}
|
|
|
|
return testTorrent
|
|
|
|
}
|
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func ExampleRedisTypeSchemaRemoveSeeder(torrent *models.Torrent, peer *models.Peer, t TestReporter) {
|
|
|
|
testTx := createTestTxObj(t)
|
|
|
|
setkey := testTx.conf.Prefix + "torrent:" + torrent.Infohash + ":seeders"
|
|
|
|
reply, err := redis.Int(testTx.Do("SREM", setkey, *peer))
|
|
|
|
if reply == 0 {
|
|
|
|
t.Errorf("remove %v failed", *peer)
|
|
|
|
}
|
2013-09-02 01:05:48 +02:00
|
|
|
verifyErrNil(err, t)
|
|
|
|
}
|
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func ExampleRedisTypesSchemaFindUser(passkey string, t TestReporter) (*models.User, bool) {
|
|
|
|
testTx := createTestTxObj(t)
|
|
|
|
hashkey := testTx.conf.Prefix + UserPrefix + passkey
|
|
|
|
userVals, err := redis.Strings(testTx.Do("HVALS", hashkey))
|
|
|
|
if len(userVals) == 0 {
|
|
|
|
return nil, false
|
2013-08-31 07:03:44 +02:00
|
|
|
}
|
2013-09-02 01:05:48 +02:00
|
|
|
verifyErrNil(err, t)
|
2013-09-07 02:59:12 +02:00
|
|
|
compareUser, err := createUser(userVals)
|
2013-09-01 21:59:46 +02:00
|
|
|
verifyErrNil(err, t)
|
2013-09-07 02:59:12 +02:00
|
|
|
return compareUser, true
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func TestFindUserSuccess(t *testing.T) {
|
|
|
|
testUser := createTestUser()
|
2013-09-02 01:05:48 +02:00
|
|
|
testTx := createTestTxObj(t)
|
2013-09-07 02:59:12 +02:00
|
|
|
hashkey := testTx.conf.Prefix + UserPrefix + sample_passkey
|
|
|
|
_, err := testTx.Do("DEL", hashkey)
|
|
|
|
verifyErrNil(err, t)
|
2013-09-02 01:05:48 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
err = testTx.AddUser(&testUser)
|
2013-09-02 01:05:48 +02:00
|
|
|
verifyErrNil(err, t)
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
compareUser, exists := ExampleRedisTypesSchemaFindUser(sample_passkey, t)
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
if !exists {
|
|
|
|
t.Error("User not found!")
|
|
|
|
}
|
|
|
|
if testUser != *compareUser {
|
|
|
|
t.Errorf("user mismatch: %v vs. %v", compareUser, testUser)
|
2013-09-02 01:05:48 +02:00
|
|
|
}
|
|
|
|
}
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func TestFindUserFail(t *testing.T) {
|
|
|
|
compareUser, exists := ExampleRedisTypesSchemaFindUser("not_a_user_passkey", t)
|
|
|
|
if exists {
|
|
|
|
t.Errorf("User %v found when none should exist!", compareUser)
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
func TestAddGetPeers(t *testing.T) {
|
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
testTx := createTestTxObj(t)
|
2013-09-07 02:59:12 +02:00
|
|
|
testTorrent := createTestTorrent()
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
setkey := testTx.conf.Prefix + "torrent:" + testTorrent.Infohash + ":seeders"
|
|
|
|
testTx.Do("DEL", setkey)
|
2013-09-04 05:58:13 +02:00
|
|
|
|
2013-09-07 02:59:12 +02:00
|
|
|
testTx.addPeers(testTorrent.Infohash, testTorrent.Seeders, ":seeders")
|
|
|
|
peerMap, err := testTx.getPeers(sample_infohash, ":seeders")
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
} else if len(peerMap) != len(testTorrent.Seeders) {
|
|
|
|
t.Error("Num Peers not equal")
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
func BenchmarkRedisTypesSchemaRemoveSeeder(b *testing.B) {
|
2013-09-01 21:59:46 +02:00
|
|
|
for bCount := 0; bCount < b.N; bCount++ {
|
2013-09-04 05:58:13 +02:00
|
|
|
// Ensure that remove completes successfully,
|
|
|
|
// even if it doesn't impact the performance
|
2013-09-01 21:59:46 +02:00
|
|
|
b.StopTimer()
|
2013-09-02 01:05:48 +02:00
|
|
|
testTx := createTestTxObj(b)
|
2013-09-07 02:59:12 +02:00
|
|
|
testTorrent := createTestTorrent()
|
2013-09-04 05:58:13 +02:00
|
|
|
setkey := testTx.conf.Prefix + "torrent:" + testTorrent.Infohash + ":seeders"
|
|
|
|
testSeeders := createSeeders()
|
|
|
|
reply, err := redis.Int(testTx.Do("SADD", setkey,
|
|
|
|
testSeeders[0],
|
|
|
|
testSeeders[1],
|
|
|
|
testSeeders[2],
|
|
|
|
testSeeders[3]))
|
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
if reply == 0 {
|
2013-09-04 05:58:13 +02:00
|
|
|
b.Log("no keys added!")
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
|
|
|
verifyErrNil(err, b)
|
|
|
|
b.StartTimer()
|
|
|
|
|
2013-09-04 05:58:13 +02:00
|
|
|
ExampleRedisTypeSchemaRemoveSeeder(&testTorrent, &testSeeders[2], b)
|
2013-09-02 01:05:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkRedisTypesSchemaFindUser(b *testing.B) {
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
// Ensure successful user find ( a failed lookup may have different performance )
|
|
|
|
b.StopTimer()
|
2013-09-07 02:59:12 +02:00
|
|
|
testUser := createTestUser()
|
2013-09-02 01:05:48 +02:00
|
|
|
testTx := createTestTxObj(b)
|
2013-09-07 02:59:12 +02:00
|
|
|
hashkey := testTx.conf.Prefix + UserPrefix + sample_passkey
|
2013-09-04 05:58:13 +02:00
|
|
|
reply, err := testTx.Do("HMSET", hashkey,
|
|
|
|
"id", testUser.ID,
|
|
|
|
"passkey", testUser.Passkey,
|
|
|
|
"up_multiplier", testUser.UpMultiplier,
|
|
|
|
"down_multiplier", testUser.DownMultiplier,
|
|
|
|
"slots", testUser.Slots,
|
|
|
|
"slots_used", testUser.SlotsUsed)
|
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
if reply == nil {
|
2013-09-07 02:59:12 +02:00
|
|
|
b.Log("no hash fields added!")
|
2013-09-02 01:05:48 +02:00
|
|
|
}
|
|
|
|
verifyErrNil(err, b)
|
|
|
|
b.StartTimer()
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
for bCount := 0; bCount < b.N; bCount++ {
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-04 05:58:13 +02:00
|
|
|
compareUser, exists := ExampleRedisTypesSchemaFindUser(sample_passkey, b)
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
b.StopTimer()
|
|
|
|
if !exists {
|
|
|
|
b.Error("User not found!")
|
|
|
|
}
|
|
|
|
if testUser != *compareUser {
|
|
|
|
b.Errorf("user mismatch: %v vs. %v", compareUser, testUser)
|
|
|
|
}
|
|
|
|
b.StartTimer()
|
2013-09-01 21:59:46 +02:00
|
|
|
}
|
2013-08-31 07:03:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestReadAfterWrite(t *testing.T) {
|
2013-09-01 21:59:46 +02:00
|
|
|
// Test requires panic
|
2013-08-31 21:06:42 +02:00
|
|
|
defer func() {
|
2013-09-02 01:05:48 +02:00
|
|
|
if err := recover(); err == nil {
|
2013-08-31 21:06:42 +02:00
|
|
|
t.Error("Read after write did not panic")
|
|
|
|
}
|
|
|
|
}()
|
2013-08-31 07:03:44 +02:00
|
|
|
|
2013-09-01 21:59:46 +02:00
|
|
|
testTx := createTestTxObj(t)
|
2013-09-02 01:05:48 +02:00
|
|
|
verifyErrNil(testTx.initiateWrite(), t)
|
|
|
|
verifyErrNil(testTx.initiateRead(), t)
|
|
|
|
}
|
2013-09-01 21:59:46 +02:00
|
|
|
|
2013-09-02 01:05:48 +02:00
|
|
|
func TestCloseClosedTransaction(t *testing.T) {
|
2013-09-01 21:59:46 +02:00
|
|
|
//require panic
|
2013-08-31 21:06:42 +02:00
|
|
|
defer func() {
|
2013-09-02 01:05:48 +02:00
|
|
|
if err := recover(); err == nil {
|
|
|
|
t.Error("Closing a closed transaction did not panic")
|
2013-08-31 21:06:42 +02:00
|
|
|
}
|
|
|
|
}()
|
2013-09-02 01:05:48 +02:00
|
|
|
|
|
|
|
testTx := createTestTxObj(t)
|
|
|
|
testTx.close()
|
2013-08-31 07:03:44 +02:00
|
|
|
testTx.close()
|
|
|
|
}
|