// SLOTSRESTORE key ttlms value [key ttlms value ...] func SlotsRestoreCmd(s Session, args [][]byte) (redis.Resp, error) { if err := s.Store().SlotsRestore(s.DB(), args); err != nil { return toRespError(err) } else { return redis.NewString("OK"), nil } }
// REPLCONF listening-port port / ack sync-offset func ReplConfCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 2 { return toRespErrorf("len(args) = %d, expect = 2", len(args)) } c, _ := s.(*conn) if c == nil { return nil, errors.New("invalid connection") } switch strings.ToLower(string(args[0])) { case "listening-port": if port, err := strconv.ParseInt(string(args[1]), 10, 16); err != nil { return toRespErrorf("invalid port REPLCONF listening-port, err: %v", err) } else { c.listeningPort.Set(int64(port)) } case "ack": if ack, err := strconv.ParseInt(string(args[1]), 10, 64); err != nil { return toRespErrorf("invalid port REPLCONF ACK, err: %v", err) } else { c.backlogACKOffset.Set(ack) c.backlogACKTime.Set(time.Now().Unix()) // ACK will not reply anything return nil, nil } default: return toRespErrorf("Unrecognized REPLCONF option:%s", args[0]) } return redis.NewString("OK"), nil }
// PING func PingCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 0 { return toRespErrorf("len(args) = %d, expect = 0", len(args)) } return redis.NewString("PONG"), nil }
// TYPE key func TypeCmd(s Session, args [][]byte) (redis.Resp, error) { if c, err := s.Store().Type(s.DB(), args); err != nil { return toRespError(err) } else { return redis.NewString(c.String()), nil } }
// BGSAVETO path func BgsaveToCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 1 { return toRespErrorf("len(args) = %d, expect = 1", len(args)) } c, _ := s.(*conn) if c == nil { return nil, errors.New("invalid connection") } if ok := c.h.bgSaveSem.AcquireTimeout(time.Second); !ok { return toRespErrorf("wait others do bgsave timeout") } defer c.h.bgSaveSem.Release() sp, err := c.Store().NewSnapshot() if err != nil { return toRespError(err) } defer c.Store().ReleaseSnapshot(sp) if err := c.h.bgsaveTo(sp, string(args[0])); err != nil { return toRespError(err) } else { return redis.NewString("OK"), nil } }
// SLAVEOF host port func SlaveOfCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 2 { return toRespErrorf("len(args) = %d, expect = 2", len(args)) } addr := fmt.Sprintf("%s:%s", string(args[0]), string(args[1])) log.Infof("set slave of %s", addr) c, _ := s.(*conn) if c == nil { return nil, errors.New("invalid connection") } var cc *conn var err error if strings.ToLower(addr) != "no:one" { if cc, err = c.h.replicationConnectMaster(addr); err != nil { return toRespError(errors.Trace(err)) } } select { case <-c.h.signal: if cc != nil { cc.Close() } return toRespErrorf("sync master has been closed") case c.h.master <- cc: <-c.h.slaveofReply return redis.NewString("OK"), nil } }
// SET key value [EX seconds] [PX milliseconds] [NX|XX] func SetCmd(s Session, args [][]byte) (redis.Resp, error) { if err := s.Store().Set(s.DB(), args); err != nil && errors2.ErrorNotEqual(err, store.ErrSetAborted) { return toRespError(err) } else if errors2.ErrorEqual(err, store.ErrSetAborted) { return redis.NewBulkBytes(nil), nil } else { return redis.NewString("OK"), nil } }
// SELECT db func SelectCmd(s Session, args [][]byte) (redis.Resp, error) { if db, err := store.ParseUint(args[0]); err != nil { return toRespError(err) } else if db > math.MaxUint32 { return toRespErrorf("parse db = %d", db) } else { s.SetDB(uint32(db)) return redis.NewString("OK"), nil } }
// COMPACTALL func CompactAllCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 0 { return toRespErrorf("len(args) = %d, expect = 0", len(args)) } if err := s.Store().CompactAll(); err != nil { return toRespError(err) } else { return redis.NewString("OK"), nil } }
func RegisterStringReply(name string, f CommandSimpleStringFunc, flag CommandFlag) { v := func(s Session, args [][]byte) (redis.Resp, error) { r, err := f(s, args) if err != nil { return toRespError(err) } return redis.NewString(r), nil } register(name, v, flag) }
func RegisterOKReply(name string, f CommandOKFunc) { v := func(s Session, args [][]byte) (redis.Resp, error) { err := f(s, args) if err != nil { return toRespError(err) } return redis.NewString("OK"), nil } register(name, v) }
// CONFIG get key / set key value func ConfigCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 2 && len(args) != 3 { return toRespErrorf("len(args) = %d, expect = 2 or 3", len(args)) } c, _ := s.(*conn) if c == nil { return nil, errors.New("invalid connection") } sub := strings.ToLower(string(args[0])) switch sub { default: return toRespErrorf("unknown sub-command = %s", sub) case "get": if len(args) != 2 { return toRespErrorf("len(args) = %d, expect = 2", len(args)) } switch e := strings.ToLower(string(args[1])); e { default: return toRespErrorf("unknown entry %s", e) case "maxmemory": return redis.NewString("0"), nil } case "set": if len(args) != 3 { return toRespErrorf("len(args) = %d, expect = 3", len(args)) } switch e := strings.ToLower(string(args[1])); e { default: return toRespErrorf("unknown entry %s", e) case "requirepass": auth := string(args[2]) c.h.config.Auth = auth return redis.NewString("OK"), nil } } }
func (h *Handler) handleSyncCommand(opt string, c *conn, args [][]byte) (redis.Resp, error) { if h.isSlave(c) { // ignore SYNC if already slave return nil, nil } if opt == "psync" { // first try whether full resync or not need, syncOffset := h.needFullReSync(c, args) if !need { // write CONTINUE and resume replication if err := c.writeRESP(redis.NewString("CONTINUE")); err != nil { log.Errorf("reply slave %s psync CONTINUE err - %s", c, err) c.Close() return nil, errors.Trace(err) } h.counters.syncPartialOK.Add(1) h.startSlaveReplication(c, syncOffset) return nil, nil } // we must handle full resync if err := h.replicationReplyFullReSync(c); err != nil { return nil, errors.Trace(err) } // slave will use ? to force resync, this is not error if !bytes.Equal(args[0], []byte{'?'}) { h.counters.syncPartialErr.Add(1) } } offset, resp, err := h.replicationSlaveFullSync(c) if err != nil { return resp, errors.Trace(err) } h.startSlaveReplication(c, offset) return nil, nil }
// AUTH password func AuthCmd(s Session, args [][]byte) (redis.Resp, error) { if len(args) != 1 { return toRespErrorf("len(args) = %d, expect = 1", len(args)) } c, _ := s.(*conn) if c == nil { return nil, errors.New("invalid connection") } if len(c.h.config.Auth) == 0 { return toRespErrorf("Client sent AUTH, but no password is set") } else if c.h.config.Auth == string(args[0]) { c.authenticated = true return redis.NewString("OK"), nil } else { c.authenticated = false return toRespErrorf("invalid password") } }
func (h *Handler) replicationReplyFullReSync(c *conn) error { // lock all to get the current master replication offset if err := c.Store().Acquire(); err != nil { return errors.Trace(err) } syncOffset := h.repl.masterOffset if h.repl.backlogBuf == nil { // we will increment the master offset by one when backlog buffer created syncOffset++ } c.Store().Release() if err := c.writeRESP(redis.NewString(fmt.Sprintf("FULLRESYNC %s %d", h.runID, syncOffset))); err != nil { log.Errorf("reply slave %s psync FULLRESYNC err - %s", c, err) c.Close() return errors.Trace(err) } return nil }
func (s *testStoreSuite) checkSlotsMgrt(c *C, r *bufio.Reader, w *bufio.Writer, cc chan error, expect ...interface{}) { if len(expect) != 0 { req1, err := redis.Decode(r) c.Assert(err, IsNil) cmd1, args1, err := redis.ParseArgs(req1) c.Assert(err, IsNil) c.Assert(cmd1, Equals, "select") c.Assert(len(args1), Equals, 1) err = redis.Encode(w, redis.NewString("OK")) c.Assert(err, IsNil) err = w.Flush() c.Assert(err, IsNil) req2, err := redis.Decode(r) cmd2, args2, err := redis.ParseArgs(req2) c.Assert(err, IsNil) c.Assert(cmd2, Equals, "slotsrestore") c.Assert(len(args2), Equals, len(expect)) m := make(map[string]*struct { key, value string ttlms uint64 }) for i := 0; i < len(expect)/3; i++ { v := &struct { key, value string ttlms uint64 }{key: expect[i*3].(string), value: expect[i*3+2].(string)} v.ttlms, err = ParseUint(expect[i*3+1]) c.Assert(err, IsNil) m[v.key] = v } for i := 0; i < len(expect)/3; i++ { key := args2[i*3] ttlms := args2[i*3+1] value := args2[i*3+2] v := m[string(key)] c.Assert(v, NotNil) c.Assert(string(key), Equals, v.key) b, err := rdb.DecodeDump(value) c.Assert(err, IsNil) c.Assert(string(b.(rdb.String)), Equals, v.value) x, err := strconv.Atoi(string(ttlms)) c.Assert(err, IsNil) if v.ttlms == 0 { c.Assert(x, Equals, 0) } else { c.Assert(x, Not(Equals), 0) c.Assert(math.Abs(float64(x)-float64(v.ttlms)) < 1000, Equals, true) } } err = redis.Encode(w, redis.NewString("OK")) c.Assert(err, IsNil) err = w.Flush() c.Assert(err, IsNil) } select { case err := <-cc: c.Assert(err, IsNil) case <-time.After(time.Second): c.Fatal("timeout error") } }