Browse Source

Handle temporary errors from underlying net conn.

Update the code to close the underlying network connection on any error
returned from the network connection. This ensures that the Redigo
connection will return errors on subsequent calls to the Redigo
connection. It will also unblock a reader when pipelines are used.
Gary Burd 12 years ago
parent
commit
5ad4a147d9
3 changed files with 15 additions and 54 deletions
  1. 9 5
      redis/conn.go
  2. 0 48
      redis/conn_test.go
  3. 6 1
      redis/test_test.go

+ 9 - 5
redis/conn.go

@@ -88,12 +88,13 @@ func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
 }
 
 func (c *conn) Close() error {
-	err := c.conn.Close()
-	if err != nil {
-		c.fatal(err)
-	} else {
-		c.fatal(errors.New("redigo: closed"))
+	c.mu.Lock()
+	err := c.err
+	if c.err == nil {
+		c.err = errors.New("redigo: closed")
+		err = c.conn.Close()
 	}
+	c.mu.Unlock()
 	return err
 }
 
@@ -101,6 +102,9 @@ func (c *conn) fatal(err error) error {
 	c.mu.Lock()
 	if c.err == nil {
 		c.err = err
+		// Close connection to force errors on subsequent calls and to unblock
+		// other reader or writer.
+		c.conn.Close()
 	}
 	c.mu.Unlock()
 	return err

+ 0 - 48
redis/conn_test.go

@@ -401,54 +401,6 @@ func TestReadDeadline(t *testing.T) {
 	}
 }
 
-func TestCloseHandling(t *testing.T) {
-	l, err := net.Listen("tcp", "127.0.0.1:0")
-	if err != nil {
-		t.Fatalf("net.Listen returned %v", err)
-	}
-	defer l.Close()
-
-	go func() {
-		for {
-			c, err := l.Accept()
-			if err != nil {
-				return
-			}
-			c.Close()
-		}
-	}()
-
-	c1, err := redis.DialTimeout(l.Addr().Network(), l.Addr().String(), 0, time.Millisecond, 0)
-	if err != nil {
-		t.Fatalf("redis.Dial returned %v", err)
-	}
-	defer c1.Close()
-
-	_, err = c1.Do("PING")
-	if err == nil {
-		t.Fatalf("c1.Do() returned nil, expect error")
-	}
-	if c1.Err() == nil {
-		t.Fatalf("c1.Err() = nil, expect error")
-	}
-
-	c2, err := redis.DialTimeout(l.Addr().Network(), l.Addr().String(), 0, time.Millisecond, 0)
-	if err != nil {
-		t.Fatalf("redis.Dial returned %v", err)
-	}
-	defer c2.Close()
-
-	c2.Send("PING")
-	c2.Flush()
-	_, err = c2.Receive()
-	if err == nil {
-		t.Fatalf("c2.Receive() returned nil, expect error")
-	}
-	if c2.Err() == nil {
-		t.Fatalf("c2.Err() = nil, expect error")
-	}
-}
-
 // Connect to local instance of Redis running on the default port.
 func ExampleDial(x int) {
 	c, err := redis.Dial("tcp", ":6379")

+ 6 - 1
redis/test_test.go

@@ -16,9 +16,14 @@ package redis
 
 import (
 	"bufio"
+	"net"
 )
 
+type dummyClose struct{ net.Conn }
+
+func (dummyClose) Close() error { return nil }
+
 // NewConnBufio is a hook for tests.
 func NewConnBufio(rw bufio.ReadWriter) Conn {
-	return &conn{br: rw.Reader, bw: rw.Writer}
+	return &conn{br: rw.Reader, bw: rw.Writer, conn: dummyClose{}}
 }