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
}
Beispiel #2
0
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
}
Beispiel #3
0
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
}