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 }
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 }
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) } }