Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go 1.16 version and feature updates #18

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ jobs:
name: check licenses
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.14
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.16
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
Expand All @@ -28,10 +28,10 @@ jobs:
name: tests
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.14
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.14
go-version: 1.16
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ According to the Redis Serialization Protocol ([RESP Specification](https://redi
| Redigo | No |
| GoRedis | No |

Neither **Redigo** nor **GoRedis** pools its buffers for reuse and allocates them on the stack per request. This becomes rather heavy on performance, especially as response sizes grow. You can see in the Get benchmarks for the three clients that **GoRedis** allocates the result no less than three times on the stack and **Redigo** allocates once. (Reference, *Get10000b* benchmark)
Neither **Redigo** nor **GoRedis** pools its buffers for reuse and allocates them on the heap per request. This becomes rather heavy on performance, especially as response sizes grow. You can see in the Get benchmarks for the three clients that **GoRedis** allocates the result no less than three times on the heap and **Redigo** allocates once. (Reference, *Get10000b* benchmark)

### Allows for zero-copy parsing of response?

Expand Down
35 changes: 0 additions & 35 deletions benchmarks/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import (
"strconv"
"syscall"
"testing"
"time"

"github.com/dropbox/godropbox/net2"
goredis "github.com/go-redis/redis"
redigo "github.com/gomodule/redigo/redis"
"github.com/mikn/greddis"
"google.golang.org/grpc/benchmark/stats"
)

func BenchmarkNetSingleBufIO(b *testing.B) {
Expand Down Expand Up @@ -175,7 +173,6 @@ func BenchmarkRedigoSingle(b *testing.B) {

func BenchmarkNetChanPool(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = newChanPool("tcp", "localhost:6379", 10)
var conn, err = pool.Get()
if err != nil {
Expand All @@ -187,7 +184,6 @@ func BenchmarkNetChanPool(b *testing.B) {
conn.Read(bytes[:5])
pool.Put(conn)
for i := 0; i < b.N; i++ {
var t = time.Now()
conn, _ = pool.Get()
bytes = bytes[:0]
bytes = append(bytes, "get testkey\r\n"...)
Expand All @@ -198,7 +194,6 @@ func BenchmarkNetChanPool(b *testing.B) {
bytes = bytes[:length+2]
conn.Read(bytes)
pool.Put(conn)
s.Add(time.Now().Sub(t))
}
conn, _ = pool.Get()
conn.Write([]byte("del testkey\r\n"))
Expand All @@ -207,7 +202,6 @@ func BenchmarkNetChanPool(b *testing.B) {

func BenchmarkNetSyncPool(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = newSyncPool("tcp", "localhost:6379", 10)
var conn, err = pool.Get()
if err != nil {
Expand All @@ -219,7 +213,6 @@ func BenchmarkNetSyncPool(b *testing.B) {
conn.Read(bytes[:5])
pool.Put(conn)
for i := 0; i < b.N; i++ {
var t = time.Now()
conn, _ = pool.Get()
bytes = bytes[:0]
bytes = append(bytes, "get testkey\r\n"...)
Expand All @@ -230,7 +223,6 @@ func BenchmarkNetSyncPool(b *testing.B) {
bytes = bytes[:length+2]
conn.Read(bytes)
pool.Put(conn)
s.Add(time.Now().Sub(t))
}
conn, _ = pool.Get()
conn.Write([]byte("del testkey\r\n"))
Expand All @@ -239,7 +231,6 @@ func BenchmarkNetSyncPool(b *testing.B) {

func BenchmarkNetAtomicPool(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = newAtomicPool("tcp", "localhost:6379", 10)
var conn, err = pool.Get()
if err != nil {
Expand All @@ -251,7 +242,6 @@ func BenchmarkNetAtomicPool(b *testing.B) {
conn.Read(bytes[:5])
pool.Put(conn)
for i := 0; i < b.N; i++ {
var t = time.Now()
conn, _ = pool.Get()
bytes = bytes[:0]
bytes = append(bytes, "get testkey\r\n"...)
Expand All @@ -262,7 +252,6 @@ func BenchmarkNetAtomicPool(b *testing.B) {
bytes = bytes[:length+2]
conn.Read(bytes)
pool.Put(conn)
s.Add(time.Now().Sub(t))
}
conn, _ = pool.Get()
conn.Write([]byte("del testkey\r\n"))
Expand All @@ -271,7 +260,6 @@ func BenchmarkNetAtomicPool(b *testing.B) {

func BenchmarkNetSemPool(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = newSemPool("tcp", "localhost:6379", 10)
var conn, err = pool.Get()
if err != nil {
Expand All @@ -283,7 +271,6 @@ func BenchmarkNetSemPool(b *testing.B) {
conn.Read(bytes[:5])
pool.Put(conn)
for i := 0; i < b.N; i++ {
var t = time.Now()
conn, _ = pool.Get()
bytes = bytes[:0]
bytes = append(bytes, "get testkey\r\n"...)
Expand All @@ -294,7 +281,6 @@ func BenchmarkNetSemPool(b *testing.B) {
bytes = bytes[:length+2]
conn.Read(bytes)
pool.Put(conn)
s.Add(time.Now().Sub(t))
}
conn, _ = pool.Get()
conn.Write([]byte("del testkey\r\n"))
Expand All @@ -303,7 +289,6 @@ func BenchmarkNetSemPool(b *testing.B) {

func BenchmarkNet2Pool(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = net2.NewSimpleConnectionPool(net2.ConnectionOptions{
MaxActiveConnections: 10,
MaxIdleConnections: 10,
Expand All @@ -319,7 +304,6 @@ func BenchmarkNet2Pool(b *testing.B) {
conn.Read(bytes[:5])
conn.ReleaseConnection()
for i := 0; i < b.N; i++ {
var t = time.Now()
conn, _ = pool.Get("tcp", "localhost:6379")
bytes = bytes[:0]
bytes = append(bytes, "get testkey\r\n"...)
Expand All @@ -330,7 +314,6 @@ func BenchmarkNet2Pool(b *testing.B) {
bytes = bytes[:length+2]
conn.Read(bytes)
conn.ReleaseConnection()
s.Add(time.Now().Sub(t))
}
conn, _ = pool.Get("tcp", "localhost:6379")
conn.Write([]byte("del testkey\r\n"))
Expand All @@ -340,20 +323,17 @@ func BenchmarkNet2Pool(b *testing.B) {
func greddisGet(addr string, key string, value string) func(*testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var ctx = context.Background()
client, _ := greddis.NewClient(ctx, &greddis.PoolOptions{URL: fmt.Sprintf("tcp://%s", addr)})
client.Set(ctx, key, []byte(value), 0)
var buf = &bytes.Buffer{}
for i := 0; i < b.N; i++ {
var t = time.Now()
var res, err = client.Get(ctx, key)
if err != nil {
fmt.Println(err)
}
res.Scan(buf)
buf.Reset()
s.Add(time.Now().Sub(t))
}
client.Del(ctx, key)
}
Expand All @@ -362,16 +342,13 @@ func greddisGet(addr string, key string, value string) func(*testing.B) {
func goredisGet(addr string, key string, value string) func(*testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var client = goredis.NewClient(&goredis.Options{
Addr: addr,
PoolSize: 10,
})
client.Set(key, value, 0)
for i := 0; i < b.N; i++ {
var t = time.Now()
_ = client.Get(key).String()
s.Add(time.Now().Sub(t))
}
client.Del(key)
}
Expand All @@ -380,7 +357,6 @@ func goredisGet(addr string, key string, value string) func(*testing.B) {
func redigoGet(addr string, key string, value string) func(*testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = redigo.Pool{
MaxIdle: 10,
MaxActive: 10,
Expand All @@ -391,12 +367,10 @@ func redigoGet(addr string, key string, value string) func(*testing.B) {
conn.Close()
var buf = make([][]byte, 1)
for i := 0; i < b.N; i++ {
var t = time.Now()
conn = pool.Get()
var val, _ = redigo.Values(conn.Do("get", key))
redigo.Scan(val, &buf)
conn.Close()
s.Add(time.Now().Sub(t))
buf[0] = buf[0][:0]
}
conn.Do("del", key)
Expand All @@ -406,15 +380,12 @@ func redigoGet(addr string, key string, value string) func(*testing.B) {
func greddisSet(addr string, key string, value string) func(*testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var ctx = context.Background()
client, _ := greddis.NewClient(ctx, &greddis.PoolOptions{URL: fmt.Sprintf("tcp://%s", addr)})
var strPtr = &value
//byteVal := []byte(value)
for i := 0; i < b.N; i++ {
var t = time.Now()
client.Set(ctx, key, strPtr, 0)
s.Add(time.Now().Sub(t))
}
client.Del(ctx, key)
}
Expand All @@ -423,15 +394,12 @@ func greddisSet(addr string, key string, value string) func(*testing.B) {
func goredisSet(addr string, key string, value string) func(*testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var client = goredis.NewClient(&goredis.Options{
Addr: addr,
PoolSize: 10,
})
for i := 0; i < b.N; i++ {
var t = time.Now()
client.Set(key, value, 0)
s.Add(time.Now().Sub(t))
}
client.Del(key)
}
Expand All @@ -440,18 +408,15 @@ func goredisSet(addr string, key string, value string) func(*testing.B) {
func redigoSet(addr string, key string, value string) func(*testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
var s = stats.AddStats(b, 10)
var pool = redigo.Pool{
MaxIdle: 10,
MaxActive: 10,
Dial: func() (redigo.Conn, error) { return redigo.Dial("tcp", addr) },
}
for i := 0; i < b.N; i++ {
var t = time.Now()
var conn = pool.Get()
conn.Do("set", key, value)
conn.Close()
s.Add(time.Now().Sub(t))
}
var conn = pool.Get()
conn.Do("del", key)
Expand Down
14 changes: 7 additions & 7 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
)

type conn struct {
conn net.Conn
arrw *ArrayWriter
res *Result
r *Reader
created time.Time
toBeClosed int64 // only for use with atomics
inUse int64 // only for use with atomics
conn net.Conn
arrw *ArrayWriter
res *Result
r *Reader
created time.Time
toBeClosed int64 // only for use with atomics
inUse int64 // only for use with atomics
}

func newConn(c net.Conn, initBufSize int) *conn {
Expand Down
11 changes: 11 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,49 @@ import (
)

// ErrRetryable is an error that can be retried
// TODO investigate if we can use e.Temporary() or some subset of that class of errors
var ErrRetryable = errors.New("Temporary error, please retry!")

// ErrMalformedString received expected length, but it isn't terminated properly
// TODO Find an appropriate error to sub-type here
var ErrMalformedString = errors.New("Expected CRLF terminated string, but did not receive one")

// ErrWrongPrefix returns when we expected a certain type prefix, but received another
// TODO Find an appropriate error to sub-type here
var ErrWrongPrefix = errors.New("Wrong prefix on string")

// ErrConnRead received error when reading from connection
// TODO Find an appropriate error to sub-type here
var ErrConnRead = errors.New("Received error whilst reading from connection")

// ErrConnWrite error when writing to connection
// TODO Find an appropriate error to sub-type here
var ErrConnWrite = errors.New("Received error whilst writing to connection")

// ErrConnDial error during dial
// TODO Find an appropriate error to sub-type here
var ErrConnDial = errors.New("Received error whilst establishing connection")

// ErrOptsDialAndURL cannot combine both dial and URL for pool options
// TODO Find an appropriate error to sub-type here
var ErrOptsDialAndURL = errors.New("Both Dial and URL is set, can only set one")

// ErrNoMoreRows is returned when an ArrayResult does not contain any more entries when Next() is called
// TODO Find an appropriate error to sub-type here
var ErrNoMoreRows = errors.New("No more rows")

// ErrMixedTopicTypes is given when you pass in arguments of both RedisPattern and String
// TODO Find an appropriate error to sub-type here
var ErrMixedTopicTypes = errors.New("All the topics need to be either of type string or of RedisPattern, but not of both")

// ErrWrongType is returned when the function receives an unsupported type
// TODO Find an appropriate error to sub-type here
func ErrWrongType(v interface{}, expected string) error {
return fmt.Errorf("Received an unsupported type of %t, expected %s", v, expected)
}

// ErrWrongToken is used internally in the Redis Reader when it checks whether the token expected and this is not the case
// TODO Find an appropriate error to sub-type here
func ErrWrongToken(expToken byte, token byte) error {
return fmt.Errorf("Expected token: %s but received %s", string(expToken), string(token))
}
20 changes: 8 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@ module github.com/mikn/greddis
require (
github.com/beorn7/perks v1.0.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dropbox/godropbox v0.0.0-20190205061219-becbb8a577f3
github.com/go-redis/redis v6.15.6+incompatible
github.com/golang/mock v1.3.1
github.com/golang/protobuf v1.3.1 // indirect
github.com/gomodule/redigo v1.7.0
github.com/kr/pretty v0.1.0 // indirect
github.com/dropbox/godropbox v0.0.0-20200228041828-52ad444d3502
github.com/go-redis/redis v6.15.9+incompatible
github.com/golang/mock v1.5.0
github.com/gomodule/redigo v1.8.4
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/stretchr/testify v1.4.0
golang.org/x/net v0.0.0-20191003171128-d98b1b443823 // indirect
golang.org/x/sync v0.0.0-20190423024810-112230192c58
github.com/stretchr/testify v1.7.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/grpc v1.21.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

go 1.14
go 1.16
Loading