func redisListResponseExpected(w http.ResponseWriter, r *redis.Resp, context string) ([]string, bool) { if r.IsType(redis.Str) { errStr, _ := r.Str() log.Printf("%s: redis => %s\n", context, errStr) w.WriteHeader(500) data := make([]string, 0) return data, false } data, err := r.List() if err != nil { log.Printf("%s => %v\n", context, err) w.WriteHeader(500) data := make([]string, 0) return data, false } return data, true }
func (c *Cluster) clientCmd( client *redis.Client, cmd string, args []interface{}, ask bool, tried map[string]bool, haveReset bool, ) *redis.Resp { var err error var r *redis.Resp defer c.Put(client) if ask { r = client.Cmd("ASKING") ask = false } // If we asked and got an error, we continue on with error handling as we // would normally do. If we didn't ask or the ask succeeded we do the // command normally, and see how that goes if r == nil || r.Err == nil { r = client.Cmd(cmd, args...) } if err = r.Err; err == nil { return r } // At this point we have some kind of error we have to deal with. The above // code is what will be run 99% of the time and is pretty streamlined, // everything after this point is allowed to be hairy and gross haveTriedBefore := haveTried(tried, client.Addr) tried = justTried(tried, client.Addr) // Deal with network error if r.IsType(redis.IOErr) { // If this is the first time trying this node, try it again if !haveTriedBefore { if client, try2err := c.getConn("", client.Addr); try2err == nil { return c.clientCmd(client, cmd, args, false, tried, haveReset) } } // Otherwise try calling Reset() and getting a random client if !haveReset { if resetErr := c.Reset(); resetErr != nil { return errorRespf("Could not get cluster info: %s", resetErr) } client, getErr := c.getConn("", "") if getErr != nil { return errorResp(getErr) } return c.clientCmd(client, cmd, args, false, tried, true) } // Otherwise give up and return the most recent error return r } // Here we deal with application errors that are either MOVED or ASK msg := err.Error() moved := strings.HasPrefix(msg, "MOVED ") ask = strings.HasPrefix(msg, "ASK ") if moved || ask { _, addr := redirectInfo(msg) c.callCh <- func(c *Cluster) { select { case c.MissCh <- struct{}{}: default: } } // If we've already called Reset and we're getting MOVED again than the // cluster is having problems, likely telling us to try a node which is // not reachable. Not much which can be done at this point if haveReset { return errorRespf("Cluster doesn't make sense, %s might be gone", addr) } if resetErr := c.Reset(); resetErr != nil { return errorRespf("Could not get cluster info: %s", resetErr) } haveReset = true // At this point addr is whatever redis told us it should be. However, // if we can't get a connection to it we'll never actually mark it as // tried, resulting in an infinite loop. Here we mark it as tried // regardless of if it actually was or not tried = justTried(tried, addr) client, getErr := c.getConn("", addr) if getErr != nil { return errorResp(getErr) } return c.clientCmd(client, cmd, args, ask, tried, haveReset) } // It's a normal application error (like WRONG KEY TYPE or whatever), return // that to the client return r }
func (c *SubClient) parseResp(resp *redis.Resp) *SubResp { sr := &SubResp{Resp: resp} var elems []*redis.Resp switch { case resp.IsType(redis.Array): elems, _ = resp.Array() if len(elems) < 3 { sr.Err = errors.New("resp is not formatted as a subscription resp") sr.Type = Error return sr } case resp.IsType(redis.Err): sr.Err = resp.Err sr.Type = Error return sr default: sr.Err = errors.New("resp is not formatted as a subscription resp") sr.Type = Error return sr } rtype, err := elems[0].Str() if err != nil { sr.Err = fmt.Errorf("resp type: %s", err) sr.Type = Error return sr } //first element switch rtype { case "subscribe", "psubscribe": sr.Type = Subscribe count, err := elems[2].Int() if err != nil { sr.Err = fmt.Errorf("subscribe count: %s", err) sr.Type = Error } else { sr.SubCount = int(count) } case "unsubscribe", "punsubscribe": sr.Type = Unsubscribe count, err := elems[2].Int() if err != nil { sr.Err = fmt.Errorf("unsubscribe count: %s", err) sr.Type = Error } else { sr.SubCount = int(count) } case "message", "pmessage": var chanI, msgI int if rtype == "message" { chanI, msgI = 1, 2 } else { // "pmessage" chanI, msgI = 2, 3 pattern, err := elems[1].Str() if err != nil { sr.Err = fmt.Errorf("message pattern: %s", err) sr.Type = Error return sr } sr.Pattern = pattern } sr.Type = Message channel, err := elems[chanI].Str() if err != nil { sr.Err = fmt.Errorf("message channel: %s", err) sr.Type = Error return sr } sr.Channel = channel msg, err := elems[msgI].Str() if err != nil { sr.Err = fmt.Errorf("message msg: %s", err) sr.Type = Error } else { sr.Message = msg } default: sr.Err = errors.New("suscription multiresp has invalid type: " + rtype) sr.Type = Error } return sr }