Skip to content

Commit 5a5f95c

Browse files
committed
[add] extended testing for pool creation/closing. adds NewClientFromPool and Close methods. includes example
1 parent 58880c3 commit 5a5f95c

File tree

10 files changed

+204
-37
lines changed

10 files changed

+204
-37
lines changed

.circleci/config.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ jobs:
99
- image: circleci/golang:1.12
1010

1111
- image: redislabs/rebloom:edge
12-
command: ['--requirepass', 'SUPERSECRET', '--loadmodule', '/usr/lib/redis/modules/redisbloom.so']
1312
port: 6379:6379
1413

1514
working_directory: /go/src/github.com/RedisBloom/redisbloom-go
@@ -18,8 +17,8 @@ jobs:
1817
- run: go get -v -t -d ./...
1918

2019
#run tests with coverage
21-
- run: go test -v -race -coverprofile=coverage.txt -covermode=atomic
22-
- run: go tool cover -func=coverage.txt
20+
- run: make get
21+
- run: make coverage
2322
- run: bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}
2423

2524
workflows:

.gitignore

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,24 @@
55
*.so
66
*.dylib
77

8-
# Test binary, built with `go test -c`
8+
# Test binary, build with `go test -c`
99
*.test
1010

1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
13+
coverage.txt
1314

14-
# Dependency directories (remove the comment below to include it)
15-
# vendor/
16-
15+
.idea
1716
.project
17+
18+
vendor/
19+
20+
# OS generated files #
21+
######################
22+
.DS_Store
23+
.DS_Store?
24+
._*
25+
.Spotlight-V100
26+
.Trashes
27+
ehthumbs.db
28+
Thumbs.db

Gopkg.lock

Lines changed: 8 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Go parameters
2+
GOCMD=go
3+
GOBUILD=$(GOCMD) build
4+
GOINSTALL=$(GOCMD) install
5+
GOCLEAN=$(GOCMD) clean
6+
GOTEST=$(GOCMD) test
7+
GOGET=$(GOCMD) get
8+
GOMOD=$(GOCMD) mod
9+
10+
.PHONY: all test coverage
11+
all: test coverage
12+
13+
get:
14+
$(GOGET) -t -v ./...
15+
16+
test: get
17+
$(GOTEST) -race -covermode=atomic ./...
18+
19+
coverage: get test
20+
$(GOTEST) -race -coverprofile=coverage.txt -covermode=atomic .
21+

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ func main() {
3939
// Connect to localhost with no password
4040
var client = redisbloom.NewClient("localhost:6379", "nohelp", nil)
4141

42-
// TS.ADD mytest item
42+
// BF.ADD mytest item
4343
_, err := client.Add("mytest", "myItem")
4444
if err != nil {
4545
fmt.Println("Error:", err)
4646
}
4747

48-
exists, err = client.Exists("mytest", "myItem")
48+
exists, err := client.Exists("mytest", "myItem")
4949
if err != nil {
5050
fmt.Println("Error:", err)
5151
}

client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ func NewClient(addr, name string, authPass *string) *Client {
3535
return ret
3636
}
3737

38+
39+
// NewClientFromPool creates a new Client with the given pool and client name
40+
func NewClientFromPool(pool *redis.Pool, name string) *Client {
41+
ret := &Client{
42+
Pool: pool,
43+
Name: name,
44+
}
45+
return ret
46+
}
47+
3848
// Reserve - Creates an empty Bloom Filter with a given desired error ratio and initial capacity.
3949
// args:
4050
// key - the name of the filter

client_test.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,51 @@ package redis_bloom_go
22

33
import (
44
"os"
5-
// "reflect"
65
"testing"
76
"time"
8-
9-
// "github.com/gomodule/redigo/redis"
7+
"github.com/gomodule/redigo/redis"
108
"github.com/stretchr/testify/assert"
119
)
1210

13-
func createClient() *Client {
14-
valueh, exists := os.LookupEnv("REDISBLOOM_TEST_HOST")
11+
12+
func getTestConnectionDetails() (string, string) {
13+
value, exists := os.LookupEnv("REDISBLOOM_TEST_HOST")
1514
host := "localhost:6379"
16-
if exists && valueh != "" {
17-
host = valueh
15+
password := ""
16+
valuePassword, existsPassword := os.LookupEnv("REDISBLOOM_TEST_PASSWORD")
17+
if exists && value != "" {
18+
host = value
1819
}
19-
valuep, exists := os.LookupEnv("REDISBLOOM_TEST_PASSWORD")
20-
password := "SUPERSECRET"
21-
var ptr *string = nil
22-
if exists {
23-
password = valuep
20+
if existsPassword && valuePassword != "" {
21+
password = valuePassword
2422
}
23+
return host, password
24+
}
25+
26+
func createClient() *Client {
27+
host, password := getTestConnectionDetails()
28+
var ptr *string = nil
2529
if len(password) > 0 {
2630
ptr = &password
2731
}
2832
return NewClient(host, "test_client", ptr)
2933
}
3034

35+
36+
func TestNewClientFromPool(t *testing.T) {
37+
host, password := getTestConnectionDetails()
38+
pool := &redis.Pool{Dial: func() (redis.Conn, error) {
39+
return redis.Dial("tcp", host, redis.DialPassword(password))
40+
}, MaxIdle: maxConns}
41+
client1 := NewClientFromPool(pool, "bloom-client-1")
42+
client2 := NewClientFromPool(pool, "bloom-client-2")
43+
assert.Equal(t, client1.Pool, client2.Pool)
44+
err1 := client1.Pool.Close()
45+
err2 := client2.Pool.Close()
46+
assert.Nil(t, err1)
47+
assert.Nil(t, err2)
48+
}
49+
3150
var client = createClient()
3251
var _ = client.FlushAll()
3352

example_client_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package redis_bloom_go_test
2+
3+
import (
4+
"fmt"
5+
redisbloom "github.com/RedisBloom/redisbloom-go"
6+
)
7+
8+
// exemplifies the NewClient function
9+
func ExampleNewClient() {
10+
host := "localhost:6379"
11+
var client = redisbloom.NewClient(host, "nohelp", nil)
12+
13+
// BF.ADD mytest item
14+
_, err := client.Add("mytest", "myItem")
15+
if err != nil {
16+
fmt.Println("Error:", err)
17+
}
18+
19+
exists, err := client.Exists("mytest", "myItem")
20+
if err != nil {
21+
fmt.Println("Error:", err)
22+
}
23+
fmt.Println("myItem exists in mytest: ", exists)
24+
// myItem exists in mytest: true
25+
26+
}

pool.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package redis_bloom_go
22

33
import (
4+
"fmt"
45
"math/rand"
56
"sync"
67
"time"
@@ -10,11 +11,16 @@ import (
1011

1112
type ConnPool interface {
1213
Get() redis.Conn
14+
Close() error
1315
}
1416

1517
type SingleHostPool struct {
1618
*redis.Pool
1719
}
20+
//
21+
//func (s SingleHostPool) Close() {
22+
// s.Pool.Close()
23+
//}
1824

1925
func NewSingleHostPool(host string, authPass *string) *SingleHostPool {
2026
ret := &redis.Pool{
@@ -33,6 +39,25 @@ type MultiHostPool struct {
3339
authPass *string
3440
}
3541

42+
43+
func (p *MultiHostPool) Close() (err error) {
44+
p.Lock()
45+
defer p.Unlock()
46+
for host, pool := range p.pools {
47+
poolErr := pool.Close()
48+
//preserve pool error if not nil but continue
49+
if poolErr != nil {
50+
if err == nil {
51+
err = fmt.Errorf("Error closing pool for host %s. Got %v.", host, poolErr)
52+
} else {
53+
err = fmt.Errorf("%v Error closing pool for host %s. Got %v.", err, host, poolErr)
54+
}
55+
}
56+
}
57+
return
58+
}
59+
60+
3661
func NewMultiHostPool(hosts []string, authPass *string) *MultiHostPool {
3762
return &MultiHostPool{
3863
pools: make(map[string]*redis.Pool, len(hosts)),

pool_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package redis_bloom_go
22

33
import (
4+
"github.com/gomodule/redigo/redis"
5+
"github.com/stretchr/testify/assert"
46
"testing"
57
)
68

@@ -30,3 +32,65 @@ func TestNewMultiHostPool(t *testing.T) {
3032
})
3133
}
3234
}
35+
36+
func TestMultiHostPool_Close(t *testing.T) {
37+
host, password := getTestConnectionDetails()
38+
// Test a simple flow
39+
if password == "" {
40+
oneMulti := NewMultiHostPool([]string{host}, nil)
41+
conn := oneMulti.Get()
42+
assert.NotNil(t, conn)
43+
err := oneMulti.Close()
44+
assert.Nil(t, err)
45+
err = oneMulti.Close()
46+
assert.NotNil(t, conn)
47+
severalMulti := NewMultiHostPool([]string{host, host}, nil)
48+
connMulti := severalMulti.Get()
49+
assert.NotNil(t, connMulti)
50+
err = severalMulti.Close()
51+
assert.Nil(t, err)
52+
}
53+
// Exhaustive test
54+
dial := func() (redis.Conn, error) {
55+
return redis.Dial("tcp", host, redis.DialPassword(password))
56+
}
57+
pool1 := &redis.Pool{Dial: dial, MaxIdle: maxConns}
58+
pool2 := &redis.Pool{Dial: dial, MaxIdle: maxConns}
59+
pool3 := &redis.Pool{Dial: dial, MaxIdle: maxConns}
60+
//Close pull3 prior to enforce error
61+
pool3.Close()
62+
pool4 := &redis.Pool{Dial: dial, MaxIdle: maxConns}
63+
64+
type fields struct {
65+
pools map[string]*redis.Pool
66+
hosts []string
67+
}
68+
tests := []struct {
69+
name string
70+
fields fields
71+
wantErr bool
72+
}{
73+
{"empty", fields{map[string]*redis.Pool{}, []string{}}, false},
74+
{"normal", fields{map[string]*redis.Pool{"hostpool1": pool1}, []string{"hostpool1"}}, false},
75+
{"pool3-already-close", fields{map[string]*redis.Pool{"hostpool2": pool2, "hostpool3": pool3, "hostpool4": pool4}, []string{"hostpool2", "hostpool3", "hostpool3"}}, false},
76+
}
77+
for _, tt := range tests {
78+
t.Run(tt.name, func(t *testing.T) {
79+
p := &MultiHostPool{
80+
pools: tt.fields.pools,
81+
hosts: tt.fields.hosts,
82+
}
83+
if err := p.Close(); (err != nil) != tt.wantErr {
84+
t.Errorf("Close() error = %v, wantErr %v", err, tt.wantErr)
85+
}
86+
// ensure all connections are really closed
87+
if !tt.wantErr {
88+
for _, pool := range p.pools {
89+
if _, err := pool.Get().Do("PING"); err == nil {
90+
t.Errorf("expected error after connection closed. Got %v", err)
91+
}
92+
}
93+
}
94+
})
95+
}
96+
}

0 commit comments

Comments
 (0)