Skip to content

Commit

Permalink
Extract race tests to separate file. Add more race tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
vmihailenco committed Mar 16, 2016
1 parent 9d394cc commit f47fb47
Show file tree
Hide file tree
Showing 19 changed files with 411 additions and 330 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
all: testdeps
go test ./... -test.cpu=1,2,4
go test ./... -test.short -test.race
go test ./...
go test ./... -short -race

testdeps: testdata/redis/src/redis-server

Expand Down
5 changes: 4 additions & 1 deletion cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ func startCluster(scenario *clusterScenario) error {
return err
}

client := redis.NewClient(&redis.Options{Addr: "127.0.0.1:" + port})
client := redis.NewClient(&redis.Options{
Addr: ":" + port,
})

info, err := client.ClusterNodes().Result()
if err != nil {
return err
Expand Down
198 changes: 2 additions & 196 deletions command_test.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
package redis_test

import (
"bytes"
"strconv"
"sync"
"testing"
"time"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"gopkg.in/redis.v3"
"gopkg.in/redis.v3/internal/pool"
)

var _ = Describe("Command", func() {
var client *redis.Client

connect := func() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: redisAddr,
PoolTimeout: time.Minute,
})
}

BeforeEach(func() {
client = connect()
client = redis.NewClient(redisOptions())
Expect(client.FlushDb().Err()).NotTo(HaveOccurred())
})

AfterEach(func() {
Expect(client.FlushDb().Err()).NotTo(HaveOccurred())
Expect(client.Close()).NotTo(HaveOccurred())
})

Expand All @@ -54,64 +40,6 @@ var _ = Describe("Command", func() {
Expect(set.Val()).To(Equal("OK"))
})

It("should escape special chars", func() {
set := client.Set("key", "hello1\r\nhello2\r\n", 0)
Expect(set.Err()).NotTo(HaveOccurred())
Expect(set.Val()).To(Equal("OK"))

get := client.Get("key")
Expect(get.Err()).NotTo(HaveOccurred())
Expect(get.Val()).To(Equal("hello1\r\nhello2\r\n"))
})

It("should handle big vals", func() {
bigVal := string(bytes.Repeat([]byte{'*'}, 1<<16))

err := client.Set("key", bigVal, 0).Err()
Expect(err).NotTo(HaveOccurred())

// Reconnect to get new connection.
Expect(client.Close()).To(BeNil())
client = connect()

got, err := client.Get("key").Result()
Expect(err).NotTo(HaveOccurred())
Expect(len(got)).To(Equal(len(bigVal)))
Expect(got).To(Equal(bigVal))
})

It("should handle many keys #1", func() {
const n = 100000
for i := 0; i < n; i++ {
client.Set("keys.key"+strconv.Itoa(i), "hello"+strconv.Itoa(i), 0)
}
keys := client.Keys("keys.*")
Expect(keys.Err()).NotTo(HaveOccurred())
Expect(len(keys.Val())).To(Equal(n))
})

It("should handle many keys #2", func() {
const n = 100000

keys := []string{"non-existent-key"}
for i := 0; i < n; i++ {
key := "keys.key" + strconv.Itoa(i)
client.Set(key, "hello"+strconv.Itoa(i), 0)
keys = append(keys, key)
}
keys = append(keys, "non-existent-key")

mget := client.MGet(keys...)
Expect(mget.Err()).NotTo(HaveOccurred())
Expect(len(mget.Val())).To(Equal(n + 2))
vals := mget.Val()
for i := 0; i < n; i++ {
Expect(vals[i+1]).To(Equal("hello" + strconv.Itoa(i)))
}
Expect(vals[0]).To(BeNil())
Expect(vals[n+1]).To(BeNil())
})

It("should convert strings via helpers", func() {
set := client.Set("key", "10", 0)
Expect(set.Err()).NotTo(HaveOccurred())
Expand All @@ -129,126 +57,4 @@ var _ = Describe("Command", func() {
Expect(f).To(Equal(float64(10)))
})

It("Cmd should return string", func() {
cmd := redis.NewCmd("PING")
client.Process(cmd)
Expect(cmd.Err()).NotTo(HaveOccurred())
Expect(cmd.Val()).To(Equal("PONG"))
})

Describe("races", func() {
var C, N = 10, 1000
if testing.Short() {
C = 3
N = 100
}

It("should echo", func() {
perform(C, func() {
for i := 0; i < N; i++ {
msg := "echo" + strconv.Itoa(i)
echo, err := client.Echo(msg).Result()
Expect(err).NotTo(HaveOccurred())
Expect(echo).To(Equal(msg))
}
})
})

It("should incr", func() {
key := "TestIncrFromGoroutines"

perform(C, func() {
for i := 0; i < N; i++ {
err := client.Incr(key).Err()
Expect(err).NotTo(HaveOccurred())
}
})

val, err := client.Get(key).Int64()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal(int64(C * N)))
})

It("should handle big vals", func() {
client2 := connect()
defer client2.Close()

bigVal := string(bytes.Repeat([]byte{'*'}, 1<<16))

var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
perform(C, func() {
for i := 0; i < N; i++ {
got, err := client.Get("key").Result()
if err == redis.Nil {
continue
}
Expect(got).To(Equal(bigVal))
}
})
}()

go func() {
defer wg.Done()
perform(C, func() {
for i := 0; i < N; i++ {
err := client2.Set("key", bigVal, 0).Err()
Expect(err).NotTo(HaveOccurred())
}
})
}()

wg.Wait()
})

It("should PubSub", func() {
connPool := client.Pool()
connPool.(*pool.ConnPool).DialLimiter = nil

var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
perform(C, func() {
for i := 0; i < N; i++ {
pubsub, err := client.Subscribe("mychannel")
Expect(err).NotTo(HaveOccurred())

go func() {
defer GinkgoRecover()

time.Sleep(time.Millisecond)
err := pubsub.Close()
Expect(err).NotTo(HaveOccurred())
}()

_, err = pubsub.ReceiveMessage()
Expect(err.Error()).To(ContainSubstring("closed"))
}
})
}()

go func() {
defer wg.Done()
perform(C, func() {
for i := 0; i < N; i++ {
val := "echo" + strconv.Itoa(i)
echo, err := client.Echo(val).Result()
Expect(err).NotTo(HaveOccurred())
Expect(echo).To(Equal(val))
}
})
}()

wg.Wait()

Expect(connPool.Len()).To(Equal(connPool.FreeLen()))
Expect(connPool.Len()).To(BeNumerically("<=", 10))
})
})

})
15 changes: 6 additions & 9 deletions commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@ var _ = Describe("Commands", func() {
var client *redis.Client

BeforeEach(func() {
client = redis.NewClient(&redis.Options{
Addr: redisAddr,
PoolTimeout: 30 * time.Second,
})
client = redis.NewClient(redisOptions())
Expect(client.FlushDb().Err()).NotTo(HaveOccurred())
})

AfterEach(func() {
Expect(client.FlushDb().Err()).NotTo(HaveOccurred())
Expect(client.Close()).NotTo(HaveOccurred())
})

Expand Down Expand Up @@ -299,31 +296,31 @@ var _ = Describe("Commands", func() {
})

It("should Move", func() {
move := client.Move("key", 1)
move := client.Move("key", 2)
Expect(move.Err()).NotTo(HaveOccurred())
Expect(move.Val()).To(Equal(false))

set := client.Set("key", "hello", 0)
Expect(set.Err()).NotTo(HaveOccurred())
Expect(set.Val()).To(Equal("OK"))

move = client.Move("key", 1)
move = client.Move("key", 2)
Expect(move.Err()).NotTo(HaveOccurred())
Expect(move.Val()).To(Equal(true))

get := client.Get("key")
Expect(get.Err()).To(Equal(redis.Nil))
Expect(get.Val()).To(Equal(""))

sel := client.Select(1)
sel := client.Select(2)
Expect(sel.Err()).NotTo(HaveOccurred())
Expect(sel.Val()).To(Equal("OK"))

get = client.Get("key")
Expect(get.Err()).NotTo(HaveOccurred())
Expect(get.Val()).To(Equal("hello"))
Expect(client.FlushDb().Err()).NotTo(HaveOccurred())
Expect(client.Select(0).Err()).NotTo(HaveOccurred())
Expect(client.Select(1).Err()).NotTo(HaveOccurred())
})

It("should Object", func() {
Expand Down
7 changes: 3 additions & 4 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ import (
var client *redis.Client

func init() {
client = redis.NewClient(&redis.Options{
Addr: ":6379",
DialTimeout: 10 * time.Second,
})
opt := redisOptions()
opt.Addr = ":6379"
client = redis.NewClient(opt)
client.FlushDb()
}

Expand Down
12 changes: 12 additions & 0 deletions internal/pool/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pool

import (
"bufio"
"io"
"net"
"sync/atomic"
"time"
Expand Down Expand Up @@ -78,6 +79,17 @@ func (cn *Conn) RemoteAddr() net.Addr {
return cn.NetConn.RemoteAddr()
}

func (cn *Conn) ReadN(n int) ([]byte, error) {
if d := n - cap(cn.Buf); d > 0 {
cn.Buf = cn.Buf[:cap(cn.Buf)]
cn.Buf = append(cn.Buf, make([]byte, d)...)
} else {
cn.Buf = cn.Buf[:n]
}
_, err := io.ReadFull(cn.Rd, cn.Buf)
return cn.Buf, err
}

func (cn *Conn) Close() error {
return cn.NetConn.Close()
}
19 changes: 11 additions & 8 deletions internal/pool/conn_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (l *connList) Add(cn *Conn) {
l.mu.Lock()
for i, c := range l.cns {
if c == nil {
cn.idx = int32(i)
cn.SetIndex(i)
l.cns[i] = cn
l.mu.Unlock()
return
Expand All @@ -65,22 +65,25 @@ func (l *connList) Remove(idx int) {
l.mu.Lock()
if l.cns != nil {
l.cns[idx] = nil
l.len -= 1
atomic.AddInt32(&l.len, -1)
}
l.mu.Unlock()
}

func (l *connList) Close() error {
func (l *connList) Reset() []*Conn {
l.mu.Lock()
for _, c := range l.cns {
if c == nil {

for _, cn := range l.cns {
if cn == nil {
continue
}
c.idx = -1
c.Close()
cn.SetIndex(-1)
}

cns := l.cns
l.cns = nil
l.len = 0

l.mu.Unlock()
return nil
return cns
}
Loading

0 comments on commit f47fb47

Please sign in to comment.