Esempio n. 1
0
func (c *Cluster) doGET(w http.ResponseWriter, r *http.Request) {
	var err error

	// path is already read because we are using http.StripPrefix
	path := r.URL.Path

	if path == "" {
		http.NotFound(w, r)
		return
	}

	slave := c.getReadSlave()

	// this makes it easy to debug with curl -v
	w.Header().Add("X-RRPROXY-SERVER", slave.Addr)

	// get a redis client from the pool
	client, err := slave.Get()
	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// make sure we return it
	defer slave.Put(client)

	var resp *redis.Resp

	// check to see if the key exists
	resp = client.Cmd("EXISTS", path)
	v, _ := resp.Int()

	if v == 0 {
		http.NotFound(w, r)
		return
	}

	// here we introduce some artifical latency. without it, and with redis
	// running on the same laptop as the server, other layers become the bottle
	// neck rather than redis, which kinda defeats the purpose of this lab
	if ForcedLatency > 0 {
		resp = client.Cmd("DEBUG", "sleep", ForcedLatency.Seconds())
		if resp.Err != nil {
			log.Fatal(resp.Err)
		}
	}

	// get the raw value as bytes and write it out
	resp = client.Cmd("GET", path)
	data, err := resp.Bytes()
	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(data)
}
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
}
Esempio n. 3
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
}
Esempio n. 4
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
}
Esempio n. 5
0
func (r *rutil) printKey(key string, fld []string, json bool) {
	cli := r.Client()
	var res *redis.Resp

	res = cli.Cmd("TYPE", key)
	checkErr(res.Err)
	key_t, err := res.Str()
	checkErr(err)

	fmt.Printf("KEY: %s\nTYP: %s\n", key, key_t)
	switch key_t {
	case "set":
		res = cli.Cmd("SMEMBERS", key)
		checkErr(res.Err)
		set, err := res.List()
		checkErr(err)
		fmt.Println("VAL:", set, "\n")
	case "hash":
		if len(fld) == 0 {
			res = cli.Cmd("HGETALL", key)
			checkErr(res.Err)
			hash, err := res.Map()
			checkErr(err)
			ppHash(hash, json)
		} else {
			res = cli.Cmd("HMGET", key, fld)
			arr, err := res.List()
			checkErr(err)
			hash := map[string]string{}
			for i, k := range fld {
				hash[k] = arr[i]
			}
			ppHash(hash, json)
		}
	case "string":
		res = cli.Cmd("GET", key)
		checkErr(res.Err)
		str, err := res.Str()
		checkErr(err)
		ppString(str, json)
	case "zset":
		res = cli.Cmd("ZRANGE", key, 0, -1)
		checkErr(res.Err)
		set, err := res.List()
		checkErr(err)
		fmt.Println("VAL:", set, "\n")
	case "list":
		res = cli.Cmd("LRANGE", key, 0, -1)
		checkErr(res.Err)
		list, err := res.List()
		checkErr(err)
		fmt.Println("VAL:", list, "\n")
	default:
		checkErr(key_t)
	}
}