gomodule/redigo

Add DoWithCancel

Open

#351 opened on Jun 27, 2018

View on GitHub
 (1 comment) (5 reactions) (0 assignees)Go (7,782 stars) (1,116 forks)batch import
EnhancementHelp wanted

Description

Add DoWithCancel helper function after the Redis CLIENT UNBLOCK command is released.

// DoWithCancel executes cmd with args. If the context is canceled, then
// DoWithCancel unblocks c by executing the CLIENT UNBLOCK command using a
// connection returned from the get function.  Example:
//
//  result, err := redis.Values(redis.DoWithCancel(c, ctx, pool.Get, "BRPOP", "key", 0))
func DoWithCancel(c Conn, ctx context.Context, get func() Conn, cmd string, args ...interface{}) (interface{}, error) {
	id, err := redis.Int(redis.Do("CLIENT", "ID"))
	if err != nil {
		return err
	}
	stop := make(chan struct{})
	wait := make(chan struct{})

	go func() {
		defer close(wait)
		select {
		case <-stop:
		case <-ctx.Done():
                          // TODO: avoid deadlock. If argument c is from pool with Wait = true and the get argument
                         // is the Get() method from that same pool, then the following line can cause a deadlock.
			c := get()
			defer c.Close()
			_, err := c.Do("CLIENT", "UNBLOCK", id)
			// TODO: handle error, possibly by closing the network connection on argument c
		}
	}()

	r, err := c.Do(cmd, args...)
	close(stop)
	<-wait
	return r, err
}

To reduce extra roundtrips to the server, cache the result fo the CLIENT ID command in the low-level connection.

func (c *conn) DoWithTimeout(readTimeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
    ...
    isClientIDCmd := cmd == "CLIENT" && len(args) == 1 && args[0] == "ID" 
    if isClientIDCmd && c.clientID != nil {
        return c.clientID, nil
    }
    ...
    if isClientIDCmd && err == nil {
        c.clientID = reply
    }
    ...
}

EDIT: An alternative to caching the client ID in the low-level connection is to pipeline CLIENT ID with the application's command. This approach localizes the implementation to this one function and should have little impact on performance.

Contributor guide