瀏覽代碼

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 年之前
父節點
當前提交
5ad4a147d9
共有 3 個文件被更改,包括 15 次插入54 次删除
  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{}}
 }