Skip to content

Commit 89065de

Browse files
committed
client constructor overhaul + configuration exposure
1 parent e6dc13d commit 89065de

6 files changed

+39
-64
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Version 2 [github.com/pascaldekloe/redis/v2] utilizes generics.
2626

2727
```go
2828
// Redis is a thread-safe connection establishment.
29-
var Redis = redis.NewClient[string,string]("rds1.example.com", time.Second/2, 0)
29+
var Redis = redis.NewDefaultClient[string,string]("rds1.example.com")
3030

3131
// Grow adds a string to a list.
3232
func Grow() {

client.go

+15-35
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const (
2424
var errConnLost = errors.New("redis: connection lost while awaiting response")
2525

2626
// ClientConfig defines a Client setup.
27-
type ClientConfig[Key, Value String] struct {
27+
type ClientConfig struct {
2828
// The host defaults to localhost, and the port defaults to 6379.
2929
// Thus, the empty string defaults to "localhost:6379". Use an
3030
// absolute file path (e.g. "/var/run/redis.sock") for Unix
@@ -51,21 +51,13 @@ type ClientConfig[Key, Value String] struct {
5151
DB int64
5252
}
5353

54-
// NewClient launches a managed connection to a node (address).
55-
func (c *ClientConfig[Key, Value]) NewClient() *Client[Key, Value] {
56-
return newClient(*c)
57-
}
58-
5954
// Client manages a connection to a Redis node until Close. Broken connection
6055
// states cause automated reconnects.
6156
//
6257
// Multiple goroutines may invoke methods on a Client simultaneously. Command
6358
// invocation applies <https://redis.io/topics/pipelining> on concurrency.
6459
type Client[Key, Value String] struct {
65-
// Normalized node address in use. This field is read-only.
66-
Addr string
67-
68-
config ClientConfig[Key, Value]
60+
ClientConfig // read-only attributes
6961

7062
noCopy noCopy
7163

@@ -85,29 +77,18 @@ type Client[Key, Value String] struct {
8577
readTerm chan struct{}
8678
}
8779

88-
// NewClient launches a managed connection to a node (address).
89-
// The host defaults to localhost, and the port defaults to 6379.
90-
// Thus, the empty string defaults to "localhost:6379". Use an
91-
// absolute file path (e.g. "/var/run/redis.sock") for Unix
92-
// domain sockets.
93-
//
94-
// A command time-out limits execution duration when nonzero. Expiry causes a
95-
// reconnect (to prevent stale connections) and a net.Error with Timeout() true.
96-
//
97-
// The dial time-out limits the duration for network connection establishment.
98-
// Expiry causes an abort + retry. Zero defaults to one second. Any command
99-
// submission blocks on the first attempt. When connection establishment fails,
100-
// then command submission receives the error of the last attempt, until the
101-
// connection restores.
102-
func NewClient[Key, Value String](addr string, commandTimeout, dialTimeout time.Duration) *Client[Key, Value] {
103-
return newClient(ClientConfig[Key, Value]{
80+
// NewDefaultClient launches a managed connection to a node (address).
81+
// Both CommandTimeout and DialTimeout are set to one second.
82+
func NewDefaultClient[Key, Value String](addr string) *Client[Key, Value] {
83+
return NewClient[Key, Value](ClientConfig{
10484
Addr: addr,
105-
CommandTimeout: commandTimeout,
106-
DialTimeout: dialTimeout,
85+
CommandTimeout: time.Second,
86+
DialTimeout: time.Second,
10787
})
10888
}
10989

110-
func newClient[Key, Value String](config ClientConfig[Key, Value]) *Client[Key, Value] {
90+
// NewClient launches a managed connection to a node (address).
91+
func NewClient[Key, Value String](config ClientConfig) *Client[Key, Value] {
11192
config.Addr = normalizeAddr(config.Addr)
11293
if config.DialTimeout == 0 {
11394
config.DialTimeout = time.Second
@@ -119,8 +100,7 @@ func newClient[Key, Value String](config ClientConfig[Key, Value]) *Client[Key,
119100
}
120101

121102
c := &Client[Key, Value]{
122-
Addr: config.Addr, // decouple
123-
config: config,
103+
ClientConfig: config,
124104

125105
connSem: make(chan *redisConn, 1),
126106
readQueue: make(chan chan<- *bufio.Reader, queueSize),
@@ -172,7 +152,7 @@ func (c *Client[Key, Value]) Close() error {
172152
func (c *Client[Key, Value]) connectOrClosed() {
173153
var retryDelay time.Duration
174154
for {
175-
conn, reader, err := c.config.connect(conservativeMSS)
155+
conn, reader, err := c.connect(conservativeMSS)
176156
if err != nil {
177157
retry := time.NewTimer(retryDelay)
178158

@@ -237,8 +217,8 @@ func (c *Client[Key, Value]) exchange(req *request) (*bufio.Reader, error) {
237217

238218
// apply time-out if set
239219
var deadline time.Time
240-
if c.config.CommandTimeout != 0 {
241-
deadline = time.Now().Add(c.config.CommandTimeout)
220+
if c.CommandTimeout != 0 {
221+
deadline = time.Now().Add(c.CommandTimeout)
242222
conn.SetWriteDeadline(deadline)
243223
}
244224

@@ -430,7 +410,7 @@ func (c *Client[Key, Value]) dropConnFromRead() {
430410
}
431411
}
432412

433-
func (c *ClientConfig[Key, Value]) connect(readBufferSize int) (net.Conn, *bufio.Reader, error) {
413+
func (c *ClientConfig) connect(readBufferSize int) (net.Conn, *bufio.Reader, error) {
434414
network := "tcp"
435415
if isUnixAddr(c.Addr) {
436416
network = "unix"

client_test.go

+13-18
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,29 @@ import (
1414
"time"
1515
)
1616

17-
var testConfig ClientConfig[string, string]
1817
var testClient, benchClient *Client[string, string]
1918

2019
func init() {
2120
addr, ok := os.LookupEnv("TEST_REDIS_ADDR")
2221
if !ok {
2322
log.Fatal("Need TEST_REDIS_ADDR evironment variable with an address of a test server.\nCAUTION! Tests insert, modify and delete data.")
2423
}
25-
testConfig.Addr = addr
26-
24+
config := ClientConfig{Addr: addr}
2725
if s, ok := os.LookupEnv("TEST_REDIS_PASSWORD"); ok {
28-
testConfig.Password = []byte(s)
26+
config.Password = []byte(s)
2927
}
3028

31-
benchClient = testConfig.NewClient()
29+
benchClient = NewClient[string, string](config)
3230

33-
testConfig.CommandTimeout = time.Second
34-
testClient = testConfig.NewClient()
31+
config.CommandTimeout = time.Second
32+
testClient = NewClient[string, string](config)
3533

3634
// make random keys vary
3735
rand.Seed(time.Now().UnixNano())
3836
}
3937

4038
func byteValueClient(t testing.TB) *Client[string, []byte] {
41-
config := ClientConfig[string, []byte]{
42-
Addr: testConfig.Addr,
43-
Password: testConfig.Password,
44-
}
45-
c := config.NewClient()
39+
c := NewClient[string, []byte](testClient.ClientConfig)
4640
t.Cleanup(func() {
4741
err := c.Close()
4842
if err != nil {
@@ -58,7 +52,7 @@ func randomKey(prefix string) string {
5852

5953
func TestClose(t *testing.T) {
6054
t.Parallel()
61-
c := NewClient[string, string](testClient.Addr, 0, 0)
55+
c := NewDefaultClient[string, string](testClient.Addr)
6256
if err := c.Close(); err != nil {
6357
t.Fatal("close got error:", err)
6458
}
@@ -74,7 +68,7 @@ func TestClose(t *testing.T) {
7468

7569
func TestCloseBussy(t *testing.T) {
7670
t.Parallel()
77-
c := testConfig.NewClient()
71+
c := NewClient[string, string](testClient.ClientConfig)
7872
key := randomKey("counter")
7973

8074
timeout := time.NewTimer(time.Second)
@@ -115,9 +109,10 @@ func TestCloseBussy(t *testing.T) {
115109
func TestUnavailable(t *testing.T) {
116110
t.Parallel()
117111

118-
connectTimeout := 100 * time.Millisecond
119-
120-
c := NewClient[string, string]("doesnotexist.example.com:70", 0, connectTimeout)
112+
config := testClient.ClientConfig
113+
config.Addr = "doesnotexist.example.com:70"
114+
config.DialTimeout = 100 * time.Millisecond
115+
c := NewClient[string, string](config)
121116
defer func() {
122117
if err := c.Close(); err != nil {
123118
t.Error("close got error:", err)
@@ -136,7 +131,7 @@ func TestUnavailable(t *testing.T) {
136131
}
137132

138133
// let the Client retry…
139-
time.Sleep(2 * connectTimeout)
134+
time.Sleep(2 * config.DialTimeout)
140135

141136
_, err = c.GET("arbitrary")
142137
if e := new(net.OpError); !errors.As(err, &e) {

example_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
func ExampleClient_SETWithOptions() {
1212
// connection setup
13-
var Redis = redis.NewClient[string, string]("rds1.example.com", time.Second/2, 0)
13+
var Redis = redis.NewDefaultClient[string, string]("rds1.example.com")
1414
defer Redis.Close()
1515

1616
// execute command

pubsub.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ func (l *Listener) connectLoop() {
222222

223223
var retryDelay time.Duration
224224
for {
225-
config := ClientConfig[string, string]{
225+
config := ClientConfig{
226226
Addr: l.Addr,
227227
CommandTimeout: l.CommandTimeout,
228228
DialTimeout: l.DialTimeout,

pubsub_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ func newTestListener(t *testing.T) (*Listener, <-chan *listenerCall) {
4747
}
4848
},
4949

50-
Addr: testConfig.Addr,
51-
CommandTimeout: testConfig.CommandTimeout,
52-
DialTimeout: testConfig.DialTimeout,
53-
Password: testConfig.Password,
50+
Addr: testClient.Addr,
51+
CommandTimeout: testClient.CommandTimeout,
52+
DialTimeout: testClient.DialTimeout,
53+
Password: testClient.Password,
5454
})
5555

5656
t.Cleanup(func() {
@@ -329,10 +329,10 @@ func benchmarkPubSub(b *testing.B, size, routineN int) {
329329
close(done)
330330
}
331331
},
332-
Addr: testConfig.Addr,
333-
CommandTimeout: testConfig.CommandTimeout,
334-
DialTimeout: testConfig.DialTimeout,
335-
Password: testConfig.Password,
332+
Addr: testClient.Addr,
333+
CommandTimeout: testClient.CommandTimeout,
334+
DialTimeout: testClient.DialTimeout,
335+
Password: testClient.Password,
336336
})
337337
defer l.Close()
338338

0 commit comments

Comments
 (0)