func (tr *taskRunner) readloop() { for { resp, err := parser.Parse(tr.c.BufioReader()) if err != nil { tr.out <- err return } tr.out <- resp } }
func (s *Server) handleMigrateState(slotIndex int, key []byte) error { shd := s.slots[slotIndex] if shd.slotInfo.State.Status != models.SLOT_STATUS_MIGRATE { return nil } if shd.migrateFrom == nil { log.Fatalf("migrateFrom not exist %+v", shd) } if shd.dst.Master() == shd.migrateFrom.Master() { log.Fatalf("the same migrate src and dst, %+v", shd) } redisConn, err := s.pools.GetConn(shd.migrateFrom.Master()) if err != nil { return errors.Trace(err) } defer s.pools.ReleaseConn(redisConn) redisReader := redisConn.(*redispool.PooledConn).BufioReader() err = WriteMigrateKeyCmd(redisConn.(*redispool.PooledConn), shd.dst.Master(), 30*1000, key) if err != nil { redisConn.Close() log.Warningf("migrate key %s error, from %s to %s", string(key), shd.migrateFrom.Master(), shd.dst.Master()) return errors.Trace(err) } //handle migrate result resp, err := parser.Parse(redisReader) if err != nil { redisConn.Close() return errors.Trace(err) } result, err := resp.Bytes() log.Debug("migrate", string(key), "from", shd.migrateFrom.Master(), "to", shd.dst.Master(), string(result)) if resp.Type == parser.ErrorResp { redisConn.Close() log.Error(string(key), string(resp.Raw), "migrateFrom", shd.migrateFrom.Master()) return errors.New(string(resp.Raw)) } s.counter.Add("Migrate", 1) return nil }
func (s *Server) sendBack(c *session, op []byte, keys [][]byte, resp *parser.Resp, result []byte) { c.pipelineSeq++ pr := &PipelineRequest{ op: op, keys: keys, seq: c.pipelineSeq, backQ: c.backQ, req: resp, } resp, err := parser.Parse(bufio.NewReader(bytes.NewReader(result))) //just send to backQ c.backQ <- &PipelineResponse{ctx: pr, err: err, resp: resp} }
func write2Client(redisReader *bufio.Reader, clientWriter io.Writer) (redisErr error, clientErr error) { resp, err := parser.Parse(redisReader) if err != nil { return errors.Trace(err), errors.Trace(err) } b, err := resp.Bytes() if err != nil { return errors.Trace(err), errors.Trace(err) } _, err = clientWriter.Write(b) return nil, errors.Trace(err) }
func TestForward(t *testing.T) { client := &fakeDeadlineReadWriter{r: bufio.NewReader(bytes.NewBuffer([]byte(simple_request))), w: bufio.NewWriter(&bytes.Buffer{})} redis := &fakeDeadlineReadWriter{r: bufio.NewReader(bytes.NewBuffer([]byte(simple_request))), w: bufio.NewWriter(&bytes.Buffer{})} resp, err := parser.Parse(bufio.NewReader(bytes.NewBuffer([]byte(simple_request)))) if err != nil { t.Error(err) } _, clientErr := forward(client, redis, resp) if clientErr != nil { t.Error(clientErr) } }
func getRespOpKeys(c *session) (*parser.Resp, []byte, [][]byte, error) { resp, err := parser.Parse(c.r) // read client request if err != nil { return nil, nil, nil, errors.Trace(err) } op, keys, err := resp.GetOpKeys() if err != nil { return nil, nil, nil, errors.Trace(err) } if len(keys) == 0 { keys = [][]byte{[]byte("fakeKey")} } return resp, op, keys, nil }
func TestWrite2Redis(t *testing.T) { var result bytes.Buffer var input bytes.Buffer input.WriteString(simple_request) resp, err := parser.Parse(bufio.NewReader(&input)) if err != nil { t.Error(err) } err = write2Redis(resp, &result) if err != nil { t.Error(err) } if string(result.Bytes()) != simple_request { t.Error("not match") } }
func TestHandleSpecCommand(t *testing.T) { var tbl = map[string]string{ "PING": "+PONG\r\n", "QUIT": string(OK_BYTES), "SELECT": string(OK_BYTES), "AUTH": string(OK_BYTES), } for k, v := range tbl { resp, err := parser.Parse(bufio.NewReader(bytes.NewBufferString(k + string(parser.NEW_LINE)))) if err != nil { t.Error(err) } _, keys, err := resp.GetOpKeys() if err != nil { t.Error(errors.ErrorStack(err)) } result := &bytes.Buffer{} w := &fakeDeadlineReadWriter{w: bufio.NewWriter(result)} _, _, err = handleSpecCommand(k, w, keys) if err != nil { t.Error(err) } w.w.Flush() if string(result.Bytes()) != v { t.Error("result not match", string(result.Bytes())) } } //"ECHO xxxx": "xxxx\r\n", { resp, err := parser.Parse(bufio.NewReader(bytes.NewBufferString("ECHO xxxx\r\n"))) if err != nil { t.Error(errors.ErrorStack(err)) } result := &bytes.Buffer{} w := &fakeDeadlineReadWriter{w: bufio.NewWriter(result)} _, keys, _ := resp.GetOpKeys() _, _, err = handleSpecCommand("ECHO", w, keys) if err != nil { t.Error(errors.ErrorStack(err)) } w.w.Flush() if string(result.Bytes()) != "$4\r\nxxxx\r\n" { t.Error("result not match", string(result.Bytes())) } } //test empty key { resp, err := parser.Parse(bufio.NewReader(bytes.NewBufferString("ECHO\r\n"))) if err != nil { t.Error(errors.ErrorStack(err)) } result := &bytes.Buffer{} w := &fakeDeadlineReadWriter{w: bufio.NewWriter(result)} _, keys, _ := resp.GetOpKeys() shouldClose, _, err := handleSpecCommand("ECHO", w, keys) if !shouldClose { t.Error(errors.ErrorStack(err)) } } //test not specific command { result := &bytes.Buffer{} w := &fakeDeadlineReadWriter{w: bufio.NewWriter(result)} _, handled, err := handleSpecCommand("get", w, nil) if handled { t.Error(errors.ErrorStack(err)) } } }
func (s *Server) redisTunnel(c *session) error { resp, err := parser.Parse(c.r) // read client request if err != nil { return errors.Trace(err) } op, keys, err := resp.GetOpKeys() if err != nil { return errors.Trace(err) } if len(keys) == 0 { keys = [][]byte{[]byte("fakeKey")} } start := time.Now() k := keys[0] opstr := strings.ToUpper(string(op)) //log.Debugf("op: %s, %s", opstr, keys[0]) next, err := s.filter(opstr, keys, c) if err != nil { return errors.Trace(err) } s.counter.Add(opstr, 1) s.counter.Add("ops", 1) if !next { return nil } i := mapKey2Slot(k) token := s.concurrentLimiter.Get() check_state: s.mu.RLock() if s.slots[i] == nil { s.mu.Unlock() return errors.Errorf("should never happend, slot %d is empty", i) } //wait for state change, should be soon if s.slots[i].slotInfo.State.Status == models.SLOT_STATUS_PRE_MIGRATE { s.mu.RUnlock() time.Sleep(10 * time.Millisecond) goto check_state } defer func() { s.mu.RUnlock() sec := time.Since(start).Seconds() if sec > 2 { log.Warningf("op: %s, key:%s, on: %s, too long %d", opstr, string(k), s.slots[i].dst.Master(), int(sec)) } recordResponseTime(s.counter, time.Duration(sec)*1000) s.concurrentLimiter.Put(token) }() if err := s.handleMigrateState(i, k); err != nil { return errors.Trace(err) } //get redis connection redisConn, err := s.pools.GetConn(s.slots[i].dst.Master()) if err != nil { return errors.Trace(err) } redisErr, clientErr := forward(c, redisConn.(*redispool.PooledConn), resp) if redisErr != nil { redisConn.Close() } s.pools.ReleaseConn(redisConn) return errors.Trace(clientErr) }