func (mc *MultiKeyCmd) CoalesceRsp() *PipelineResponse { plRsp := &PipelineResponse{} var rsp *resp.Data switch mc.CmdType() { case MGET: rsp = &resp.Data{T: resp.T_Array, Array: make([]*resp.Data, mc.numSubCmds)} case MSET: rsp = OK_DATA case DEL: rsp = &resp.Data{T: resp.T_Integer} default: panic("invalid multi key cmd name") } for i, subCmdRsp := range mc.subCmdRsps { if subCmdRsp.err != nil { rsp = &resp.Data{T: resp.T_Error, String: []byte(subCmdRsp.err.Error())} break } reader := bufio.NewReader(bytes.NewReader(subCmdRsp.rsp.Raw())) data, err := resp.ReadData(reader) if err != nil { log.Errorf("re-parse response err=%s", err) rsp = &resp.Data{T: resp.T_Error, String: []byte(err.Error())} break } if data.T == resp.T_Error { rsp = data break } switch mc.CmdType() { case MGET: rsp.Array[i] = data case MSET: case DEL: rsp.Integer += data.Integer default: panic("invalid multi key cmd name") } } plRsp.rsp = resp.NewObjectFromData(rsp) return plRsp }
// handleResp handles MOVED and ASK redirection and call write response func (s *Session) handleResp(plRsp *PipelineResponse) error { if plRsp.ctx.seq != s.rspSeq { panic("impossible") } plRsp.ctx.wg.Done() if plRsp.ctx.parentCmd == nil { s.rspSeq++ } if plRsp.err != nil { s.dispatcher.TriggerReloadSlots() rsp := &resp.Data{T: resp.T_Error, String: []byte(plRsp.err.Error())} plRsp.rsp = resp.NewObjectFromData(rsp) } else { raw := plRsp.rsp.Raw() if raw[0] == resp.T_Error { if bytes.HasPrefix(raw, MOVED) { _, server := ParseRedirectInfo(string(raw)) s.dispatcher.TriggerReloadSlots() s.redirect(server, plRsp, false) } else if bytes.HasPrefix(raw, ASK) { _, server := ParseRedirectInfo(string(raw)) s.redirect(server, plRsp, true) } } } if plRsp.err != nil { return plRsp.err } if !s.closed { if err := s.writeResp(plRsp); err != nil { return err } } return nil }
func (s *Session) ReadingLoop() { for { cmd, err := resp.ReadCommand(s.r) if err != nil { log.Error(err) break } // convert all command name to upper case cmd.Args[0] = strings.ToUpper(cmd.Args[0]) if n := atomic.AddUint64(&accessLogCount, 1); n%LogEveryN == 0 { if len(cmd.Args) > 1 { log.Infof("access %s %s %s", s.RemoteAddr(), cmd.Name(), cmd.Args[1]) } else { log.Infof("access %s %s", s.RemoteAddr(), cmd.Name()) } } // check if command is supported cmdFlag := CmdFlag(cmd) if cmdFlag&CMD_FLAG_BLACK != 0 { plReq := &PipelineRequest{ seq: s.getNextReqSeq(), wg: s.reqWg, } s.reqWg.Add(1) rsp := &resp.Data{T: resp.T_Error, String: BLACK_CMD_ERR} plRsp := &PipelineResponse{ rsp: resp.NewObjectFromData(rsp), ctx: plReq, } s.backQ <- plRsp continue } // check if is multi key cmd if yes, numKeys := IsMultiCmd(cmd); yes && numKeys > 1 { s.handleMultiKeyCmd(cmd, numKeys) continue } // other general cmd key := cmd.Value(1) slot := Key2Slot(key) plReq := &PipelineRequest{ cmd: cmd, readOnly: cmdFlag&CMD_FLAG_READONLY != 0, slot: slot, seq: s.getNextReqSeq(), backQ: s.backQ, wg: s.reqWg, } s.reqWg.Add(1) s.dispatcher.Schedule(plReq) } // wait for all request done s.reqWg.Wait() // notify writer close(s.backQ) s.closeSignal.Wait() }