func (bc *BackendConn) verifyAuth(c *redis.Conn) error { if bc.passwd == "" { return nil } resp := redis.NewArray([]*redis.Resp{ redis.NewBulkBytes([]byte("AUTH")), redis.NewBulkBytes([]byte(bc.passwd)), }) if err := c.Writer.Encode(resp, true); err != nil { return err } resp, err := c.Reader.Decode() if err != nil { return err } if resp == nil { return errors.New(fmt.Sprintf("error resp: nil response")) } if resp.IsError() { return errors.New(fmt.Sprintf("error resp: %s", resp.Value)) } if resp.IsString() { return nil } else { return errors.New(fmt.Sprintf("error resp: should be string, but got %s", resp.Type)) } }
func (s *Slot) slotsmgrt(r *Request, key []byte) error { if len(key) == 0 || s.migrate.bc == nil { return nil } m := &Request{ Owner: r.Owner, Wait: &sync.WaitGroup{}, Resp: redis.NewArray([]*redis.Resp{ redis.NewBulkBytes([]byte("SLOTSMGRTTAGONE")), redis.NewBulkBytes(s.backend.host), redis.NewBulkBytes(s.backend.port), redis.NewBulkBytes([]byte("3000")), redis.NewBulkBytes(key), }), } m.Wait.Add(1) s.migrate.bc.PushBack(m) m.Wait.Wait() resp, err := m.Response.Resp, m.Response.Err if err != nil { return err } if resp == nil { return ErrRespIsRequired } if resp.IsError() { return errors.New(fmt.Sprintf("error resp: %s", resp.Value)) } if resp.IsInt() { log.Debugf("slot-%04d migrate from %s to %s: key = %s, resp = %s", s.Id, s.migrate.from, s.backend.addr, key, resp.Value) return nil } else { return errors.New(fmt.Sprintf("error resp: should be integer, but got %s", resp.Type)) } }
func (s *Session) handleRequest(resp *redis.Resp, d Dispatcher) (*Request, error) { opstr, err := getOpStr(resp) if err != nil { return nil, err } if isNotAllowed(opstr) { return nil, errors.New(fmt.Sprintf("command <%s> is not allowed", opstr)) } usnow := microseconds() s.LastOpUnix = usnow / 1e6 s.Ops++ r := &Request{ OpStr: opstr, Start: usnow, Resp: resp, Wait: &sync.WaitGroup{}, Failed: &s.failed, } if opstr == "QUIT" { return s.handleQuit(r) } if opstr == "AUTH" { return s.handleAuth(r) } if !s.authorized { if s.auth != "" { r.Response.Resp = redis.NewError([]byte("NOAUTH Authentication required.")) return r, nil } s.authorized = true } switch opstr { case "SELECT": return s.handleSelect(r) case "PING": return s.handlePing(r) case "MGET": return s.handleRequestMGet(r, d) case "MSET": return s.handleRequestMSet(r, d) case "DEL": return s.handleRequestMDel(r, d) } return r, d.Dispatch(r) }
func (s *Session) handleRequestMSet(r *Request, d Dispatcher) (*Request, error) { nblks := len(r.Resp.Array) - 1 if nblks <= 2 { return r, d.Dispatch(r) } if nblks%2 != 0 { r.Response.Resp = redis.NewError([]byte("ERR wrong number of arguments for MSET")) return r, nil } var sub = make([]*Request, nblks/2) for i := 0; i < len(sub); i++ { sub[i] = &Request{ Owner: r.Owner, OpSeq: -r.OpSeq, OpStr: r.OpStr, Start: r.Start, Wait: r.Wait, Resp: redis.NewArray([]*redis.Resp{ r.Resp.Array[0], r.Resp.Array[i*2+1], r.Resp.Array[i*2+2], }), } if err := d.Dispatch(sub[i]); err != nil { return nil, err } } r.Coalesce = func() error { for _, x := range sub { if err := x.Response.Err; err != nil { return err } resp := x.Response.Resp if resp == nil { return ErrRespIsRequired } if !resp.IsString() { return errors.New(fmt.Sprintf("bad mset resp: %s value.len = %d", resp.Type, len(resp.Value))) } r.Response.Resp = resp } return nil } return r, nil }
func (s *Session) loopReader(tasks chan<- *Request, d Dispatcher) error { if d == nil { return errors.New("nil dispatcher") } for !s.quit { resp, err := s.Reader.Decode() if err != nil { return err } r, err := s.handleRequest(resp, d) if err != nil { return err } else { tasks <- r } } return nil }
func SetSlotRange(zkConn zkhelper.Conn, productName string, fromSlot, toSlot, groupId int, status SlotStatus) error { if status != SLOT_STATUS_OFFLINE && status != SLOT_STATUS_ONLINE { return errors.Errorf("invalid status") } ok, err := GroupExists(zkConn, productName, groupId) if err != nil { return errors.Trace(err) } if !ok { return errors.Errorf("group %d is not found", groupId) } for i := fromSlot; i <= toSlot; i++ { s, err := GetSlot(zkConn, productName, i) if err != nil { return errors.Trace(err) } if s.State.Status != SLOT_STATUS_OFFLINE { return errors.New(fmt.Sprintf("slot %d is not offline, if you want to change the group for a slot, use migrate", s.Id)) } s.GroupId = groupId s.State.Status = status data, err := json.Marshal(s) if err != nil { return errors.Trace(err) } zkPath := GetSlotPath(productName, i) _, err = zkhelper.CreateOrUpdate(zkConn, zkPath, string(data), 0, zkhelper.DefaultFileACLs(), true) if err != nil { return errors.Trace(err) } } param := SlotMultiSetParam{ From: fromSlot, To: toSlot, GroupId: groupId, Status: status, } err = NewAction(zkConn, productName, ACTION_TYPE_MULTI_SLOT_CHANGED, param, "", true) return errors.Trace(err) }
func (s *Session) handleRequestMDel(r *Request, d Dispatcher) (*Request, error) { nkeys := len(r.Resp.Array) - 1 if nkeys <= 1 { return r, d.Dispatch(r) } var sub = make([]*Request, nkeys) for i := 0; i < len(sub); i++ { sub[i] = &Request{ Owner: r.Owner, OpSeq: -r.OpSeq, OpStr: r.OpStr, Start: r.Start, Wait: r.Wait, Resp: redis.NewArray([]*redis.Resp{ r.Resp.Array[0], r.Resp.Array[i+1], }), } if err := d.Dispatch(sub[i]); err != nil { return nil, err } } r.Coalesce = func() error { var n int for _, x := range sub { if err := x.Response.Err; err != nil { return err } resp := x.Response.Resp if resp == nil { return ErrRespIsRequired } if !resp.IsInt() || len(resp.Value) != 1 { return errors.New(fmt.Sprintf("bad mdel resp: %s value.len = %d", resp.Type, len(resp.Value))) } if resp.Value[0] != '0' { n++ } } r.Response.Resp = redis.NewInt([]byte(strconv.Itoa(n))) return nil } return r, nil }
func (s *Session) handleRequestMGet(r *Request, d Dispatcher) (*Request, error) { nkeys := len(r.Resp.Array) - 1 if nkeys <= 1 { return r, d.Dispatch(r) } var sub = make([]*Request, nkeys) for i := 0; i < len(sub); i++ { sub[i] = &Request{ Owner: r.Owner, OpSeq: -r.OpSeq, OpStr: r.OpStr, Start: r.Start, Wait: r.Wait, Resp: redis.NewArray([]*redis.Resp{ r.Resp.Array[0], r.Resp.Array[i+1], }), } if err := d.Dispatch(sub[i]); err != nil { return nil, err } } r.Coalesce = func() error { var array = make([]*redis.Resp, len(sub)) for i, x := range sub { if err := x.Response.Err; err != nil { return err } resp := x.Response.Resp if resp == nil { return ErrRespIsRequired } if !resp.IsArray() || len(resp.Array) != 1 { return errors.New(fmt.Sprintf("bad mget resp: %s array.len = %d", resp.Type, len(resp.Array))) } array[i] = resp.Array[0] } r.Response.Resp = redis.NewArray(array) return nil } return r, nil }
func createDashboardNode() error { // make sure root dir is exists rootDir := fmt.Sprintf("/zk/codis/db_%s", globalEnv.ProductName()) zkhelper.CreateRecursive(safeZkConn, rootDir, "", 0, zkhelper.DefaultDirACLs()) zkPath := fmt.Sprintf("%s/dashboard", rootDir) // make sure we're the only one dashboard if exists, _, _ := safeZkConn.Exists(zkPath); exists { data, _, _ := safeZkConn.Get(zkPath) return errors.New("dashboard already exists: " + string(data)) } content := fmt.Sprintf(`{"addr": "%v", "pid": %v}`, globalEnv.DashboardAddr(), os.Getpid()) pathCreated, err := safeZkConn.Create(zkPath, []byte(content), 0, zkhelper.DefaultFileACLs()) createdDashboardNode = true log.Infof("dashboard node created: %v, %s", pathCreated, string(content)) return errors.Trace(err) }
func createDashboardNode() error { // make sure root dir is exists rootDir := fmt.Sprintf("/zk/codis/db_%s", globalEnv.ProductName()) zkhelper.CreateRecursive(safeZkConn, rootDir, "", 0, zkhelper.DefaultDirACLs()) zkPath := fmt.Sprintf("%s/dashboard", rootDir) // make sure we're the only one dashboard if exists, _, _ := safeZkConn.Exists(zkPath); exists { data, _, _ := safeZkConn.Get(zkPath) return errors.New("dashboard already exists: " + string(data)) } content := fmt.Sprintf(`{"addr": "%v", "pid": %v}`, globalEnv.DashboardAddr(), os.Getpid()) pathCreated, err := safeZkConn.Create(zkPath, []byte(content), 0, zkhelper.DefaultFileACLs()) createdDashboardNode = true log.Infof("dashboard node created: %v, %s", pathCreated, string(content)) log.Warn("********** Attention **********") log.Warn("You should use `kill {pid}` rather than `kill -9 {pid}` to stop me,") log.Warn("or the node resisted on zk will not be cleaned when I'm quiting and you must remove it manually") log.Warn("*******************************") return errors.Trace(err) }
func (s *Session) handleRequest(resp *redis.Resp, d Dispatcher) (*Request, error) { opstr, err := getOpStr(resp) if err != nil { return nil, err } if isNotAllowed(opstr) { return nil, errors.New(fmt.Sprintf("command <%s> is not allowed", opstr)) } usnow := microseconds() s.LastOpUnix.Set(usnow / 1e6) r := &Request{ Owner: s, OpSeq: s.Ops.Incr(), OpStr: opstr, Start: usnow, Wait: &sync.WaitGroup{}, Resp: resp, } switch opstr { case "QUIT": s.quit = true fallthrough case "AUTH", "SELECT": r.Response.Resp = redis.NewString([]byte("OK")) return r, nil case "MGET": return s.handleRequestMGet(r, d) case "MSET": return s.handleRequestMSet(r, d) case "DEL": return s.handleRequestMDel(r, d) } return r, d.Dispatch(r) }
func (t *MigrateTask) run() error { log.Infof("migration start: %+v", t.MigrateTaskInfo) to := t.NewGroupId t.UpdateStatus(MIGRATE_TASK_MIGRATING) err := t.migrateSingleSlot(t.SlotId, to) if err != nil { log.ErrorErrorf(err, "migrate single slot failed") t.UpdateStatus(MIGRATE_TASK_ERR) return err } t.UpdateFinish() log.Infof("migration finished: %+v", t.MigrateTaskInfo) return nil } var ErrGroupMasterNotFound = errors.New("group master not found") // will block until all keys are migrated func (task *MigrateTask) Migrate(slot *models.Slot, fromGroup, toGroup int, onProgress func(SlotMigrateProgress)) (err error) { groupFrom, err := models.GetGroup(task.zkConn, task.productName, fromGroup) if err != nil { return err } groupTo, err := models.GetGroup(task.zkConn, task.productName, toGroup) if err != nil { return err } fromMaster, err := groupFrom.Master(task.zkConn) if err != nil { return err
} func GetActionObject(zkConn zkhelper.Conn, productName string, seq int64, act interface{}, provider string) error { data, _, err := zkConn.Get(path.Join(GetWatchActionPath(productName), zkConn.Seq2Str(seq))) if err != nil { return errors.Trace(err) } if err := json.Unmarshal(data, act); err != nil { return errors.Trace(err) } return nil } var ErrReceiverTimeout = errors.New("receiver timeout") func WaitForReceiverWithTimeout(zkConn zkhelper.Conn, productName string, actionZkPath string, proxies []ProxyInfo, timeoutInMs int) error { if len(proxies) == 0 { return nil } times := 0 proxyIds := make(map[string]bool) for _, p := range proxies { proxyIds[p.Id] = true } // check every 500ms for times < timeoutInMs/500 { if times >= 6 && (times*500)%1000 == 0 { log.Warnf("abnormal waiting time for receivers: %s %v", actionZkPath, proxyIds)
// Copyright 2014 Wandoujia Inc. All Rights Reserved. // Licensed under the MIT (MIT-LICENSE.txt) license. package redis import ( "bufio" "bytes" "io" "strconv" "github.com/wandoulabs/codis/pkg/utils/errors" ) var ( ErrBadRespCRLFEnd = errors.New("bad resp CRLF end") ErrBadRespBytesLen = errors.New("bad resp bytes len") ErrBadRespArrayLen = errors.New("bad resp array len") ) func btoi(b []byte) (int64, error) { if len(b) != 0 && len(b) < 10 { var neg, i = false, 0 switch b[0] { case '-': neg = true fallthrough case '+': i++ } if len(b) != i {
"BLPOP", "BRPOP", "BRPOPLPUSH", "PSUBSCRIBE", "PUBLISH", "PUNSUBSCRIBE", "SUBSCRIBE", "RANDOMKEY", "UNSUBSCRIBE", "DISCARD", "EXEC", "MULTI", "UNWATCH", "WATCH", "SCRIPT", "BGREWRITEAOF", "BGSAVE", "CLIENT", "CONFIG", "DBSIZE", "DEBUG", "FLUSHALL", "FLUSHDB", "LASTSAVE", "MONITOR", "SAVE", "SHUTDOWN", "SLAVEOF", "SLOWLOG", "SYNC", "TIME", "SLOTSINFO", "SLOTSDEL", "SLOTSMGRTSLOT", "SLOTSMGRTONE", "SLOTSMGRTTAGSLOT", "SLOTSMGRTTAGONE", "SLOTSCHECK", } { blacklist[s] = true } } func isNotAllowed(opstr string) bool { return blacklist[opstr] } var ( ErrBadRespType = errors.New("bad resp type for command") ErrBadOpStrLen = errors.New("bad command length, too short or too long") ) func getOpStr(resp *redis.Resp) (string, error) { if !resp.IsArray() || len(resp.Array) == 0 { return "", ErrBadRespType } for _, r := range resp.Array { if r.IsBulkBytes() { continue } return "", ErrBadRespType } var upper [64]byte
Wait: &sync.WaitGroup{}, Resp: redis.NewArray([]*redis.Resp{ redis.NewBulkBytes([]byte("PING")), }), } r.Wait.Add(1) select { case bc.input <- r: return true default: return false } } var ErrZombieRequest = errors.New("request from zombie session") func (bc *BackendConn) loopWriter() error { r, ok := <-bc.input if ok { c, tasks, err := bc.newBackendReader() if err != nil { return bc.setResponse(r, nil, err) } defer close(tasks) p := &FlushPolicy{ Encoder: c.Writer, MaxBuffered: 64, MaxInterval: 300, }
children, _, err := zkConn.Children(GetProxyFencePath(productName)) if err != nil { if err.Error() == zk.ErrNoNode.Error() { return make(map[string]bool), nil } else { return nil, err } } m := make(map[string]bool, len(children)) for _, fenceNode := range children { m[fenceNode] = true } return m, nil } var ErrUnknownProxyStatus = errors.New("unknown status, should be (online offline)") func SetProxyStatus(zkConn zkhelper.Conn, productName string, proxyName string, status string) error { p, err := GetProxyInfo(zkConn, productName, proxyName) if err != nil { return errors.Trace(err) } if status != PROXY_STATE_ONLINE && status != PROXY_STATE_MARK_OFFLINE && status != PROXY_STATE_OFFLINE { return errors.Errorf("%v, %s", ErrUnknownProxyStatus, status) } // check slot status before setting proxy online if status == PROXY_STATE_ONLINE { slots, err := Slots(zkConn, productName) if err != nil {
const ( B = 1 << (10 * iota) KB MB GB TB PB ) var ( BytesizeRegexp = regexp.MustCompile(`(?i)^\s*(\-?[\d\.]+)\s*([KMGTP]?B|[BKMGTP]|)\s*$`) digitsRegexp = regexp.MustCompile(`^\-?\d+$`) ) var ( ErrBadBytesize = errors.New("invalid byte size") ErrBadBytesizeUnit = errors.New("invalid byte size unit") ) func Parse(s string) (int64, error) { if !BytesizeRegexp.MatchString(s) { return 0, errors.Trace(ErrBadBytesize) } subs := BytesizeRegexp.FindStringSubmatch(s) if len(subs) != 3 { return 0, errors.Trace(ErrBadBytesize) } size := int64(0) switch strings.ToUpper(string(subs[2])) {
} r := &Request{ Resp: redis.NewArray([]*redis.Resp{ redis.NewBulkBytes([]byte("PING")), }), } select { case bc.input <- r: return true default: return false } } var ErrFailedRequest = errors.New("discard failed request") func (bc *BackendConn) loopWriter() error { r, ok := <-bc.input if ok { c, tasks, err := bc.newBackendReader() if err != nil { return bc.setResponse(r, nil, err) } defer close(tasks) p := &FlushPolicy{ Encoder: c.Writer, MaxBuffered: 64, MaxInterval: 300, }
return errors.Trace(err) } return nil } func (self *ServerGroup) Exists(zkConn zkhelper.Conn) (bool, error) { zkPath := fmt.Sprintf("/zk/codis/db_%s/servers/group_%d", self.ProductName, self.Id) b, err := zkhelper.NodeExists(zkConn, zkPath) if err != nil { return false, errors.Trace(err) } return b, nil } var ErrNodeExists = errors.New("node already exists") func (self *ServerGroup) AddServer(zkConn zkhelper.Conn, s *Server, passwd string) error { s.GroupId = self.Id servers, err := self.GetServers(zkConn) if err != nil { return errors.Trace(err) } var masterAddr string for _, server := range servers { if server.Type == SERVER_TYPE_MASTER { masterAddr = server.Addr } }
MaxBuffered: 32, MaxInterval: 300, } for r := range tasks { resp, err := s.handleResponse(r) if err != nil { return err } if err := p.Encode(resp, len(tasks) == 0); err != nil { return err } } return nil } var ErrRespIsRequired = errors.New("resp is required") func (s *Session) handleResponse(r *Request) (*redis.Resp, error) { r.Wait.Wait() if r.Coalesce != nil { if err := r.Coalesce(); err != nil { return nil, err } } resp, err := r.Response.Resp, r.Response.Err if err != nil { return nil, err } if resp == nil { return nil, ErrRespIsRequired }
} func (s *Router) Close() error { s.mu.Lock() defer s.mu.Unlock() if s.closed { return nil } for i := 0; i < len(s.slots); i++ { s.resetSlot(i) } s.closed = true return nil } var errClosedRouter = errors.New("use of closed router") func (s *Router) ResetSlot(i int) error { s.mu.Lock() defer s.mu.Unlock() if s.closed { return errClosedRouter } s.resetSlot(i) return nil } func (s *Router) FillSlot(i int, addr, from string, lock bool) error { s.mu.Lock() defer s.mu.Unlock() if s.closed {
s.migrate.bc = nil } func (s *Slot) forward(r *Request, key []byte) error { s.lock.RLock() bc, err := s.prepare(r, key) s.lock.RUnlock() if err != nil { return err } else { bc.PushBack(r) return nil } } var ErrSlotIsNotReady = errors.New("slot is not ready, may be offline") func (s *Slot) prepare(r *Request, key []byte) (*SharedBackendConn, error) { if s.backend.bc == nil { log.Infof("slot-%04d is not ready: key = %s", s.Id, key) return nil, ErrSlotIsNotReady } if err := s.slotsmgrt(r, key); err != nil { log.Warnf("slot-%04d migrate from = %s to %s failed: key = %s, error = %s", s.Id, s.migrate.from, s.backend.addr, key, err) return nil, err } else { r.slot = &s.wait r.slot.Add(1) return s.backend.bc, nil }
if err != nil { return nil, errors.Trace(err) } var slotid, slotsize int if _, err := redis.Scan(info, &slotid, &slotsize); err != nil { return nil, errors.Trace(err) } else { slots[slotid] = slotsize } } } return slots, nil } var ( ErrInvalidAddr = errors.New("invalid addr") ErrStopMigrateByUser = errors.New("migration stopped by user") ) func SlotsMgrtTagSlot(c redis.Conn, slotId int, toAddr string) (int, int, error) { addrParts := strings.Split(toAddr, ":") if len(addrParts) != 2 { return -1, -1, errors.Trace(ErrInvalidAddr) } reply, err := redis.Values(c.Do("SLOTSMGRTTAGSLOT", addrParts[0], addrParts[1], 30000, slotId)) if err != nil { return -1, -1, errors.Trace(err) } var succ, remain int
"path" "github.com/wandoulabs/codis/pkg/utils/errors" "github.com/wandoulabs/zkhelper" ) type SlotStatus string const ( SLOT_STATUS_ONLINE SlotStatus = "online" SLOT_STATUS_OFFLINE SlotStatus = "offline" SLOT_STATUS_MIGRATE SlotStatus = "migrate" SLOT_STATUS_PRE_MIGRATE SlotStatus = "pre_migrate" ) var ErrSlotAlreadyExists = errors.New("slots already exists") var ErrUnknownSlotStatus = errors.New("unknown slot status, slot status should be (online, offline, migrate, pre_migrate)") type SlotMigrateStatus struct { From int `json:"from"` To int `json:"to"` } type SlotMultiSetParam struct { From int `json:"from"` To int `json:"to"` Status SlotStatus `json:"status"` GroupId int `json:"group_id"` } type SlotState struct {
type rollingFile struct { mu sync.Mutex closed bool maxFileFrag int maxFragSize int64 file *os.File basePath string filePath string fileFrag int fragSize int64 } var ErrClosedRollingFile = errors.New("rolling file is closed") func (r *rollingFile) roll() error { if r.file != nil { if r.fragSize < r.maxFragSize { return nil } r.file.Close() r.file = nil } r.fragSize = 0 r.fileFrag = (r.fileFrag + 1) % r.maxFileFrag r.filePath = fmt.Sprintf("%s.%d", r.basePath, r.fileFrag) f, err := os.OpenFile(r.filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) if err != nil {