// PERSIST key func (s *Store) Persist(db uint32, args [][]byte) (int64, error) { if len(args) != 1 { return 0, errArguments("len(args) = %d, expect = 1", len(args)) } key := args[0] if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadStoreRow(db, key) if err != nil || o == nil { return 0, errors.Trace(err) } if o.GetExpireAt() == 0 { return 0, nil } fw := &Forward{DB: db, Op: "Persist", Args: args} bt := engine.NewBatch() o.SetExpireAt(0) bt.Set(o.MetaKey(), o.MetaValue()) return 1, s.commit(bt, fw) }
func (s *Store) incrInt(db uint32, key []byte, delta int64) (int64, error) { o, err := s.loadStringRow(db, key) if err != nil { return 0, errors.Trace(err) } bt := engine.NewBatch() if o != nil { _, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } v, err := ParseInt(o.Value) if err != nil { return 0, errors.Trace(err) } delta += v } else { o = newStringRow(db, key) bt.Set(o.MetaKey(), o.MetaValue()) } o.Value = FormatInt(delta) bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "IncrBy", Args: [][]byte{key, FormatInt(delta)}} return delta, s.commit(bt, fw) }
// SPOP key func (s *Store) SPop(db uint32, args [][]byte) ([]byte, error) { if len(args) != 1 { return nil, errArguments("len(args) = %d, expect = 1", len(args)) } key := args[0] if err := s.acquire(); err != nil { return nil, errors.Trace(err) } defer s.release() o, err := s.loadSetRow(db, key) if err != nil || o == nil { return nil, errors.Trace(err) } members, err := o.getMembers(s, 1) if err != nil || len(members) == 0 { return nil, errors.Trace(err) } o.Member = members[0] bt := engine.NewBatch() bt.Del(o.DataKey()) if o.Size--; o.Size > 0 { bt.Set(o.MetaKey(), o.MetaValue()) } else { bt.Del(o.MetaKey()) } fw := &Forward{DB: db, Op: "SRem", Args: [][]byte{key, members[0]}} return o.Member, s.commit(bt, fw) }
// MSETNX key value [key value ...] func (s *Store) MSetNX(db uint32, args [][]byte) (int64, error) { if len(args) == 0 || len(args)%2 != 0 { return 0, errArguments("len(args) = %d, expect != 0 && mod 2 = 0", len(args)) } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() for i := 0; i < len(args); i += 2 { o, err := s.loadStoreRow(db, args[i]) if err != nil || o != nil { return 0, errors.Trace(err) } } ms := &markSet{} bt := engine.NewBatch() for i := len(args)/2 - 1; i >= 0; i-- { key, value := args[i*2], args[i*2+1] if !ms.Has(key) { o := newStringRow(db, key) o.Value = value bt.Set(o.DataKey(), o.DataValue()) bt.Set(o.MetaKey(), o.MetaValue()) ms.Set(key) } } fw := &Forward{DB: db, Op: "MSet", Args: args} return 1, s.commit(bt, fw) }
// SLOTSRESTORE key ttlms value [key ttlms value ...] func (s *Store) SlotsRestore(db uint32, args [][]byte) error { if len(args) == 0 || len(args)%3 != 0 { return errArguments("len(args) = %d, expect != 0 && mod 3 = 0", len(args)) } objs := make([]*rdb.ObjEntry, len(args)/3) for i := 0; i < len(objs); i++ { key := args[i*3] ttlms, err := ParseInt(args[i*3+1]) if err != nil { return errArguments("parse args failed - %s", err) } value := args[i*3+2] expireat := int64(0) if ttlms != 0 { if v, ok := TTLmsToExpireAt(ttlms); ok && v > 0 { expireat = v } else { return errArguments("parse args[%d] ttlms = %d", i*3+1, ttlms) } } obj, err := rdb.DecodeDump(value) if err != nil { return errArguments("decode args[%d] failed, %s", i*3+2, err) } objs[i] = &rdb.ObjEntry{ DB: db, Key: key, ExpireAt: uint64(expireat), Value: obj, } } if err := s.acquire(); err != nil { return errors.Trace(err) } defer s.release() ms := &markSet{} bt := engine.NewBatch() for i := len(objs) - 1; i >= 0; i-- { e := objs[i] if ms.Has(e.Key) { log.Debugf("[%d] restore batch, db = %d, key = %v, ignore", i, e.DB, e.Key) continue } else { log.Debugf("[%d] restore batch, db = %d, key = %v", i, e.DB, e.Key) } if err := s.restore(bt, e.DB, e.Key, int64(e.ExpireAt), e.Value); err != nil { log.Warningf("restore object failed, db = %d, key = %v, err = %s", e.DB, e.Key, err) return errors.Trace(err) } ms.Set(e.Key) } fw := &Forward{DB: db, Op: "SlotsRestore", Args: args} return s.commit(bt, fw) }
// RPOP key func (s *Store) RPop(db uint32, args [][]byte) ([]byte, error) { if len(args) != 1 { return nil, errArguments("len(args) = %d, expect = 1", len(args)) } key := args[0] if err := s.acquire(); err != nil { return nil, errors.Trace(err) } defer s.release() o, err := s.loadListRow(db, key) if err != nil || o == nil { return nil, errors.Trace(err) } o.Index = o.Rindex - 1 if _, err := o.LoadDataValue(s); err != nil { return nil, errors.Trace(err) } else { bt := engine.NewBatch() bt.Del(o.DataKey()) if o.Rindex--; o.Lindex < o.Rindex { bt.Set(o.MetaKey(), o.MetaValue()) } else { bt.Del(o.MetaKey()) } fw := &Forward{DB: db, Op: "RPop", Args: args} return o.Value, s.commit(bt, fw) } }
// DEL key [key ...] func (s *Store) Del(db uint32, args [][]byte) (int64, error) { if len(args) == 0 { return 0, errArguments("len(args) = %d, expect != 0", len(args)) } keys := args if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() ms := &markSet{} bt := engine.NewBatch() for _, key := range keys { if !ms.Has(key) { exists, err := s.deleteIfExists(bt, db, key) if err != nil { return 0, errors.Trace(err) } if exists { ms.Set(key) } } } fw := &Forward{DB: db, Op: "Del", Args: args} return ms.Len(), s.commit(bt, fw) }
func (s *Store) incrFloat(db uint32, key []byte, delta float64) (float64, error) { o, err := s.loadStringRow(db, key) if err != nil { return 0, errors.Trace(err) } bt := engine.NewBatch() if o != nil { _, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } v, err := ParseFloat(o.Value) if err != nil { return 0, errors.Trace(err) } delta += v } else { o = newStringRow(db, key) bt.Set(o.MetaKey(), o.MetaValue()) } if math.IsNaN(delta) || math.IsInf(delta, 0) { return 0, errors.New("increment would produce NaN or Infinity") } o.Value = FormatFloat(delta) bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "IncrByFloat", Args: [][]byte{key, FormatFloat(delta)}} return delta, s.commit(bt, fw) }
// ZINCRBY key delta member func (s *Store) ZIncrBy(db uint32, args [][]byte) (float64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect = 3", len(args)) } key := args[0] delta, err := ParseFloat(args[1]) if err != nil { return 0, errArguments("parse args failed - %s", err) } member := args[2] if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadZSetRow(db, key) if err != nil { return 0, errors.Trace(err) } bt := engine.NewBatch() var exists bool = false if o != nil { o.Member = member exists, err = o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } else if exists { bt.Del(o.IndexKey()) } } else { o = newZSetRow(db, key) o.Member = member } if exists { delta += o.Score } else { o.Size++ bt.Set(o.MetaKey(), o.MetaValue()) } o.Score = delta if math.IsNaN(delta) { return 0, errors.New("invalid nan score") } bt.Set(o.DataKey(), o.DataValue()) bt.Set(o.IndexKey(), o.IndexValue()) fw := &Forward{DB: db, Op: "ZIncrBy", Args: args} return delta, s.commit(bt, fw) }
// HINCRBYFLOAT key field delta func (s *Store) HIncrByFloat(db uint32, args [][]byte) (float64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect = 2", len(args)) } key := args[0] field := args[1] delta, err := ParseFloat(args[2]) if err != nil { return 0, errArguments("parse args failed - %s", err) } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadHashRow(db, key) if err != nil { return 0, errors.Trace(err) } var exists bool = false if o != nil { o.Field = field exists, err = o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } } else { o = newHashRow(db, key) o.Field = field } bt := engine.NewBatch() if exists { v, err := ParseFloat(o.Value) if err != nil { return 0, errors.Trace(err) } delta += v } else { o.Size++ bt.Set(o.MetaKey(), o.MetaValue()) } if math.IsNaN(delta) || math.IsInf(delta, 0) { return 0, errors.New("increment would produce NaN or Infinity") } o.Value = FormatFloat(delta) bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "HIncrByFloat", Args: args} return delta, s.commit(bt, fw) }
// ZREMRANGEBYSCORE key min max func (s *Store) ZRemRangeByScore(db uint32, args [][]byte) (int64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect 3", len(args)) } key := args[0] min := args[1] max := args[2] r, err := parseRangeSpec(min, max) if err != nil { return 0, errors.Trace(err) } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadZSetRow(db, key) if err != nil { return 0, errors.Trace(err) } else if o == nil { return 0, nil } bt := engine.NewBatch() n := int64(0) f := func(o *zsetRow) error { bt.Del(o.DataKey()) bt.Del(o.IndexKey()) n++ return nil } if err := o.travelInRange(s, r, f); err != nil { return 0, errors.Trace(err) } if n > 0 { if o.Size -= n; o.Size > 0 { bt.Set(o.MetaKey(), o.MetaValue()) } else { bt.Del(o.MetaKey()) } } fw := &Forward{DB: db, Op: "ZRemRangeByScore", Args: args} return n, s.commit(bt, fw) }
// HMSET key field value [field value ...] func (s *Store) HMSet(db uint32, args [][]byte) error { if len(args) == 1 || len(args)%2 != 1 { return errArguments("len(args) = %d, expect != 1 && mod 2 = 1", len(args)) } key := args[0] var eles = make([]*rdb.HashElement, len(args)/2) for i := 0; i < len(eles); i++ { e := &rdb.HashElement{} e.Field = args[i*2+1] e.Value = args[i*2+2] eles[i] = e } if err := s.acquire(); err != nil { return errors.Trace(err) } defer s.release() o, err := s.loadHashRow(db, key) if err != nil { return errors.Trace(err) } if o == nil { o = newHashRow(db, key) } ms := &markSet{} bt := engine.NewBatch() for _, e := range eles { o.Field, o.Value = e.Field, e.Value exists, err := o.TestDataValue(s) if err != nil { return errors.Trace(err) } if !exists { ms.Set(o.Field) } bt.Set(o.DataKey(), o.DataValue()) } n := ms.Len() if n != 0 { o.Size += n bt.Set(o.MetaKey(), o.MetaValue()) } fw := &Forward{DB: db, Op: "HMSet", Args: args} return s.commit(bt, fw) }
// LTRIM key beg end func (s *Store) LTrim(db uint32, args [][]byte) error { if len(args) != 3 { return errArguments("len(args) = %d, expect = 2", len(args)) } key := args[0] beg, err := ParseInt(args[1]) if err != nil { return errArguments("parse args failed - %s", err) } end, err := ParseInt(args[2]) if err != nil { return errArguments("parse args failed - %s", err) } if err := s.acquire(); err != nil { return errors.Trace(err) } defer s.release() o, err := s.loadListRow(db, key) if err != nil || o == nil { return errors.Trace(err) } beg = maxIntValue(adjustIndex(beg, o.Lindex, o.Rindex), o.Lindex) end = minIntValue(adjustIndex(end, o.Lindex, o.Rindex), o.Rindex-1) if beg == o.Lindex && end == o.Rindex-1 { return nil } bt := engine.NewBatch() if beg <= end { for o.Index = o.Lindex; o.Index < beg; o.Index++ { bt.Del(o.DataKey()) } for o.Index = o.Rindex - 1; o.Index > end; o.Index-- { bt.Del(o.DataKey()) } o.Lindex, o.Rindex = beg, end+1 bt.Set(o.MetaKey(), o.MetaValue()) } else { for o.Index = o.Lindex; o.Index < o.Rindex; o.Index++ { bt.Del(o.DataKey()) } bt.Del(o.MetaKey()) } fw := &Forward{DB: db, Op: "LTrim", Args: args} return s.commit(bt, fw) }
func (s *Store) loadStoreRow(db uint32, key []byte) (storeRow, error) { o, err := loadStoreRow(s, db, key) if err != nil || o == nil { return nil, errors.Trace(err) } if s.needDeleteIfExpired() && o.IsExpired() { bt := engine.NewBatch() if err := o.deleteObject(s, bt); err != nil { return nil, errors.Trace(err) } fw := &Forward{DB: db, Op: "Del", Args: [][]byte{key}} return nil, s.commit(bt, fw) } return o, nil }
// SETRANGE key offset value func (s *Store) SetRange(db uint32, args [][]byte) (int64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect = 3", len(args)) } key := args[0] offset, err := ParseUint(args[1]) if err != nil { return 0, errArguments("parse args failed - %s", err) } value := args[2] if offset > maxVarbytesLen { return 0, errArguments("offset = %d", offset) } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadStringRow(db, key) if err != nil { return 0, errors.Trace(err) } bt := engine.NewBatch() if o != nil { _, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } } else { o = newStringRow(db, key) bt.Set(o.MetaKey(), o.MetaValue()) } if n := int(offset) + len(value); n > len(o.Value) { o.Value = append(o.Value, make([]byte, n-len(o.Value))...) } copy(o.Value[offset:], value) bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "SetRange", Args: args} return int64(len(o.Value)), s.commit(bt, fw) }
// HSET key field value func (s *Store) HSet(db uint32, args [][]byte) (int64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect = 2", len(args)) } key := args[0] field := args[1] value := args[2] if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadHashRow(db, key) if err != nil { return 0, errors.Trace(err) } var exists bool = false if o != nil { o.Field = field exists, err = o.TestDataValue(s) if err != nil { return 0, errors.Trace(err) } } else { o = newHashRow(db, key) o.Field = field } var n int64 bt := engine.NewBatch() if exists { n, o.Value = 0, value bt.Set(o.DataKey(), o.DataValue()) } else { o.Size++ n, o.Value = 1, value bt.Set(o.DataKey(), o.DataValue()) bt.Set(o.MetaKey(), o.MetaValue()) } fw := &Forward{DB: db, Op: "HSet", Args: args} return n, s.commit(bt, fw) }
func (s *Store) migrate(addr string, timeout time.Duration, db uint32, keys ...[]byte) (int64, error) { var rows []storeRow var bins []*rdb.BinEntry for i, key := range keys { o, bin, err := loadBinEntry(s, db, key) if err != nil { return 0, errors.Trace(err) } if o == nil { log.Debugf("[%d] missing, db = %d, key = %v", i, db, key) continue } rows = append(rows, o) if bin != nil { log.Debugf("[%d] migrate, db = %d, key = %v, expireat = %d", i, db, key, o.GetExpireAt()) bins = append(bins, bin) } else { log.Debugf("[%d] expired, db = %d, key = %v, expireat = %d", i, db, key, o.GetExpireAt()) } } if len(bins) != 0 { if err := doMigrate(addr, timeout, db, bins); err != nil { return 0, errors.Trace(err) } } if len(rows) == 0 { return 0, nil } bt := engine.NewBatch() for _, o := range rows { if err := o.deleteObject(s, bt); err != nil { return 0, errors.Trace(err) } } fw := &Forward{DB: db, Op: "Del"} for _, key := range keys { fw.Args = append(fw.Args, key) } return int64(len(rows)), s.commit(bt, fw) }
// ZREM key member [member ...] func (s *Store) ZRem(db uint32, args [][]byte) (int64, error) { if len(args) < 2 { return 0, errArguments("len(args) = %d, expect >= 2", len(args)) } key := args[0] members := args[1:] if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadZSetRow(db, key) if err != nil || o == nil { return 0, errors.Trace(err) } ms := &markSet{} bt := engine.NewBatch() for _, o.Member = range members { if !ms.Has(o.Member) { exists, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } if exists { bt.Del(o.DataKey()) bt.Del(o.IndexKey()) ms.Set(o.Member) } } } n := ms.Len() if n != 0 { if o.Size -= n; o.Size > 0 { bt.Set(o.MetaKey(), o.MetaValue()) } else { bt.Del(o.MetaKey()) } } fw := &Forward{DB: db, Op: "ZRem", Args: args} return n, s.commit(bt, fw) }
// SADD key member [member ...] func (s *Store) SAdd(db uint32, args [][]byte) (int64, error) { if len(args) < 2 { return 0, errArguments("len(args) = %d, expect >= 2", len(args)) } key := args[0] members := args[1:] if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadSetRow(db, key) if err != nil { return 0, errors.Trace(err) } if o == nil { o = newSetRow(db, key) } ms := &markSet{} bt := engine.NewBatch() for _, o.Member = range members { exists, err := o.TestDataValue(s) if err != nil { return 0, errors.Trace(err) } if !exists { ms.Set(o.Member) } bt.Set(o.DataKey(), o.DataValue()) } n := ms.Len() if n != 0 { o.Size += n bt.Set(o.MetaKey(), o.MetaValue()) } fw := &Forward{DB: db, Op: "SAdd", Args: args} return n, s.commit(bt, fw) }
func (s *Store) setExpireAt(db uint32, key []byte, expireat int64) (int64, error) { o, err := s.loadStoreRow(db, key) if err != nil || o == nil { return 0, errors.Trace(err) } bt := engine.NewBatch() if !IsExpired(expireat) { o.SetExpireAt(expireat) bt.Set(o.MetaKey(), o.MetaValue()) fw := &Forward{DB: db, Op: "PExpireAt", Args: [][]byte{key, FormatInt(expireat)}} return 1, s.commit(bt, fw) } else { _, err := s.deleteIfExists(bt, db, key) if err != nil { return 0, errors.Trace(err) } fw := &Forward{DB: db, Op: "Del", Args: [][]byte{key}} return 1, s.commit(bt, fw) } }
// GETSET key value func (s *Store) GetSet(db uint32, args [][]byte) ([]byte, error) { if len(args) != 2 { return nil, errArguments("len(args) = %d, expect = 2", len(args)) } key := args[0] value := args[1] if err := s.acquire(); err != nil { return nil, errors.Trace(err) } defer s.release() o, err := s.loadStringRow(db, key) if err != nil { return nil, errors.Trace(err) } bt := engine.NewBatch() if o != nil { _, err := o.LoadDataValue(s) if err != nil { return nil, errors.Trace(err) } if o.ExpireAt != 0 { o.ExpireAt = 0 bt.Set(o.MetaKey(), o.MetaValue()) } } else { o = newStringRow(db, key) bt.Set(o.MetaKey(), o.MetaValue()) } o.Value, value = value, o.Value bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "Set", Args: args} return value, s.commit(bt, fw) }
// RESTORE key ttlms value func (s *Store) Restore(db uint32, args [][]byte) error { if len(args) != 3 { return errArguments("len(args) = %d, expect = 3", len(args)) } key := args[0] ttlms, err := ParseInt(args[1]) if err != nil { return errArguments("parse args failed - %s", err) } value := args[2] expireat := int64(0) if ttlms != 0 { if v, ok := TTLmsToExpireAt(ttlms); ok && v > 0 { expireat = v } else { return errArguments("parse ttlms = %d", ttlms) } } obj, err := rdb.DecodeDump(value) if err != nil { return errors.Trace(err) } if err := s.acquire(); err != nil { return errors.Trace(err) } defer s.release() fw := &Forward{DB: db, Op: "Restore", Args: args} bt := engine.NewBatch() if err := s.restore(bt, db, key, expireat, obj); err != nil { return errors.Trace(err) } return s.commit(bt, fw) }
// LSET key index value func (s *Store) LSet(db uint32, args [][]byte) error { if len(args) != 3 { return errArguments("len(args) = %d, expect = 2", len(args)) } key := args[0] index, err := ParseInt(args[1]) if err != nil { return errArguments("parse args failed - %s", err) } value := args[2] if err := s.acquire(); err != nil { return errors.Trace(err) } defer s.release() o, err := s.loadListRow(db, key) if err != nil { return errors.Trace(err) } if o == nil { return errors.Trace(ErrNoSuchList) } o.Index = adjustIndex(index, o.Lindex, o.Rindex) if o.Index >= o.Lindex && o.Index < o.Rindex { o.Value = value bt := engine.NewBatch() bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "LSet", Args: args} return s.commit(bt, fw) } else { return errors.Trace(ErrOutOfRange) } }
// APPEND key value func (s *Store) Append(db uint32, args [][]byte) (int64, error) { if len(args) != 2 { return 0, errArguments("len(args) = %d, expect = 2", len(args)) } key := args[0] value := args[1] if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadStringRow(db, key) if err != nil { return 0, errors.Trace(err) } bt := engine.NewBatch() if o != nil { _, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } o.Value = append(o.Value, value...) } else { o = newStringRow(db, key) o.Value = value bt.Set(o.MetaKey(), o.MetaValue()) } bt.Set(o.DataKey(), o.DataValue()) fw := &Forward{DB: db, Op: "Append", Args: args} return int64(len(o.Value)), s.commit(bt, fw) }
func (s *Store) rpush(db uint32, key []byte, create bool, values ...[]byte) (int64, error) { o, err := s.loadListRow(db, key) if err != nil { return 0, errors.Trace(err) } if o == nil { if !create { return 0, nil } o = newListRow(db, key) } fw := &Forward{DB: db, Op: "RPush", Args: [][]byte{key}} bt := engine.NewBatch() for _, value := range values { o.Index, o.Value = o.Rindex, value o.Rindex++ bt.Set(o.DataKey(), o.DataValue()) fw.Args = append(fw.Args, value) } bt.Set(o.MetaKey(), o.MetaValue()) return o.Rindex - o.Lindex, s.commit(bt, fw) }
// ZADD key score member [score member ...] func (s *Store) ZAdd(db uint32, args [][]byte) (int64, error) { if len(args) == 1 || len(args)%2 != 1 { return 0, errArguments("len(args) = %d, expect != 1 && mod 2 = 1", len(args)) } key := args[0] var eles = make([]struct { Member []byte Score float64 }, len(args)/2) var err error for i := 0; i < len(eles); i++ { e := &eles[i] e.Score, err = ParseFloat(args[i*2+1]) if err != nil { return 0, errArguments("parse args failed - %s", err) } e.Member = args[i*2+2] if len(e.Member) == 0 { return 0, errArguments("parse args[%d] failed, empty empty", i*2+2) } } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadZSetRow(db, key) if err != nil { return 0, errors.Trace(err) } if o == nil { o = newZSetRow(db, key) } ms := &markSet{} bt := engine.NewBatch() for _, e := range eles { o.Member = e.Member exists, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } if !exists { ms.Set(o.Member) } else { // if old exists, remove index key first bt.Del(o.IndexKey()) } o.Score = e.Score bt.Set(o.DataKey(), o.DataValue()) bt.Set(o.IndexKey(), o.IndexValue()) } n := ms.Len() if n != 0 { o.Size += n bt.Set(o.MetaKey(), o.MetaValue()) } fw := &Forward{DB: db, Op: "ZAdd", Args: args} return n, s.commit(bt, fw) }
// BITOP op destkey key [key ...] func (s *Store) BitOp(db uint32, args [][]byte) (int64, error) { if len(args) < 3 { return 0, errArguments("len(args) = %d, expect >= 3", len(args)) } op := args[0] destKey := args[1] srcKeys := args[2:] if string(op) == BitNot && len(srcKeys) > 1 { return 0, errArguments("BITOP NOT must be called with a single source key, len(srcKeys)=%d", len(srcKeys)) } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() var value []byte o, err := s.loadStringRow(db, srcKeys[0]) if err != nil { return 0, errors.Trace(err) } if o != nil { _, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } if string(op) == BitNot { for i := 0; i < len(o.Value); i++ { o.Value[i] = ^o.Value[i] } } value = o.Value } for i := 1; i < len(srcKeys); i++ { ro, err := s.loadStringRow(db, srcKeys[i]) if err != nil { return 0, errors.Trace(err) } if ro != nil { _, err = ro.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } } else { ro = newStringRow(db, srcKeys[i]) } if len(value) < len(ro.Value) { value, ro.Value = ro.Value, value } for j := 0; j < len(ro.Value); j++ { switch string(op) { case BitAND: value[j] &= ro.Value[j] case BitOR: value[j] |= ro.Value[j] case BitXOR: value[j] ^= ro.Value[j] default: return 0, errors.Errorf("invalid op type: %s", op) } } for j := len(ro.Value); j < len(value); j++ { switch string(op) { case BitAND: value[j] &= 0 case BitOR: value[j] |= 0 case BitXOR: value[j] ^= 0 } } } bt := engine.NewBatch() _, err = s.deleteIfExists(bt, db, destKey) if err != nil { return 0, errors.Trace(err) } no := newStringRow(db, destKey) no.Value = value bt.Set(no.DataKey(), no.DataValue()) bt.Set(no.MetaKey(), no.MetaValue()) fw := &Forward{DB: db, Op: "BitOp", Args: args} return int64(len(no.Value)), s.commit(bt, fw) }
// SET key value [EX seconds] [PX milliseconds] [NX|XX] func (s *Store) Set(db uint32, args [][]byte) error { if len(args) < 2 { return errArguments("len(args) = %d, expect >= 2", len(args)) } key := args[0] value := args[1] expireat := int64(0) flag := uint8(0) for i := 2; i < len(args); { switch strings.ToUpper(string(args[i])) { case "EX": if i+1 >= len(args) { return errArguments("invalid set argument for EX") } ttls, err := ParseInt(args[i+1]) if err != nil { return errArguments("parse EX arg failed %v", err) } if v, ok := TTLsToExpireAt(ttls); ok && v > 0 { expireat = v } else { return errArguments("invalid EX seconds = %d", ttls) } i += 2 case "PX": if i+1 >= len(args) { return errArguments("invalid set argument for PX") } ttlms, err := ParseInt(args[i+1]) if err != nil { return errArguments("parse PX arg failed %v", err) } if v, ok := TTLmsToExpireAt(ttlms); ok && v > 0 { expireat = v } else { return errArguments("invalid PX milliseconds = %d", ttlms) } i += 2 case "NX": flag |= setNXFlag i++ case "XX": flag |= setXXFlag i++ default: return errArguments("invalid set argument at %d", i) } } if err := s.acquire(); err != nil { return errors.Trace(err) } defer s.release() bt := engine.NewBatch() if o, err := loadStoreRow(s, db, key); err != nil { return errors.Trace(err) } else { // handle NX and XX flag // NX: key is nil or expired // XX: key is not nil and not expired // otherwise, abort if (flag&setNXFlag > 0) && (o != nil && !o.IsExpired()) { return ErrSetAborted } else if (flag&setXXFlag > 0) && (o == nil || o.IsExpired()) { return ErrSetAborted } // if we are string type, we will overwrite it directly // if not, we may delete it first if o != nil && o.Code() != StringCode { if err := o.deleteObject(s, bt); err != nil { return errors.Trace(err) } } } no := newStringRow(db, key) no.Value = value no.ExpireAt = expireat bt.Set(no.DataKey(), no.DataValue()) bt.Set(no.MetaKey(), no.MetaValue()) fw := &Forward{DB: db, Op: "Set", Args: args} return s.commit(bt, fw) }
// SETBIT key offset value func (s *Store) SetBit(db uint32, args [][]byte) (int64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect = 3", len(args)) } key := args[0] offset, err := ParseUint(args[1]) if err != nil { return 0, errArguments("bit offset is not an integer or out of range - %s", err) } value, err := ParseUint(args[2]) if err != nil { return 0, errArguments("bit is not an integer or out of range - %s", err) } if offset > maxVarbytesLen { return 0, errArguments("bit offset is not an integer or out of range, offset = %d", offset) } if value != 0 && value != 1 { return 0, errArguments("bit is not an integer or out of range, bit = %d", value) } if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadStringRow(db, key) if err != nil { return 0, errors.Trace(err) } bt := engine.NewBatch() if o != nil { _, err := o.LoadDataValue(s) if err != nil { return 0, errors.Trace(err) } } else { o = newStringRow(db, key) bt.Set(o.MetaKey(), o.MetaValue()) } byteOffset := int(uint32(offset) >> 3) extra := byteOffset + 1 - len(o.Value) if extra > 0 { o.Value = append(o.Value, make([]byte, extra)...) } byteVal := o.Value[byteOffset] bit := 7 - uint8(uint32(offset)&0x7) bitVal := byteVal & (1 << bit) byteVal &= ^(1 << bit) byteVal |= (uint8(value&0x1) << bit) o.Value[byteOffset] = byteVal bt.Set(o.DataKey(), o.DataValue()) var n int64 = 0 if bitVal > 0 { n = 1 } fw := &Forward{DB: db, Op: "SetBit", Args: args} return n, s.commit(bt, fw) }
// ZREMRANGEBYRANK key start stop func (s *Store) ZRemRangeByRank(db uint32, args [][]byte) (int64, error) { if len(args) != 3 { return 0, errArguments("len(args) = %d, expect 3", len(args)) } key := args[0] start, err := ParseInt(args[1]) if err != nil { return 0, errArguments("parse args failed - %s", err) } stop, err := ParseInt(args[2]) if err != nil { return 0, errArguments("parse args failed - %s", err) } r := &rangeSpec{Min: math.Inf(-1), Max: math.Inf(1), MinEx: true, MaxEx: true} if err := s.acquire(); err != nil { return 0, errors.Trace(err) } defer s.release() o, err := s.loadZSetRow(db, key) if err != nil { return 0, errors.Trace(err) } else if o == nil { return 0, nil } var rangeLen int64 start, stop, rangeLen = sanitizeIndexes(start, stop, o.Size) if rangeLen == 0 { return 0, nil } bt := engine.NewBatch() n := int64(0) offset := int64(0) f := func(o *zsetRow) error { if offset >= start { bt.Del(o.DataKey()) bt.Del(o.IndexKey()) n++ rangeLen-- if rangeLen <= 0 { return errors.Trace(errTravelBreak) } } offset++ return nil } if err := o.travelInRange(s, r, f); err != nil { return 0, errors.Trace(err) } if n > 0 { if o.Size -= n; o.Size > 0 { bt.Set(o.MetaKey(), o.MetaValue()) } else { bt.Del(o.MetaKey()) } } fw := &Forward{DB: db, Op: "ZRemRangeByRank", Args: args} return n, s.commit(bt, fw) }