// SLOTSRESTORE key ttlms value [key ttlms value ...] func (b *Rpdb) SlotsRestore(db uint32, args ...interface{}) 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++ { var key, value []byte var ttlms int64 for j, ref := range []interface{}{&key, &ttlms, &value} { if err := parseArgument(args[i*3+j], ref); err != nil { return errArguments("parse args[%d] failed, %s", i*3+j, err) } } expireat := uint64(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: expireat, Value: obj, } } if err := b.acquire(); err != nil { return err } defer b.release() ms := &markSet{} bt := store.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 := b.restore(bt, e.DB, e.Key, e.ExpireAt, e.Value); err != nil { log.DebugErrorf(err, "restore object failed, db = %d, key = %v", e.DB, e.Key) return err } ms.Set(e.Key) } fw := &Forward{DB: db, Op: "SlotsRestore", Args: args} return b.commit(bt, fw) }
// SLOTSMGRTTAGONE host port timeout key func (b *Rpdb) SlotsMgrtTagOne(db uint32, args ...interface{}) (int64, error) { if len(args) != 4 { return 0, errArguments("len(args) = %d, expect = 4", len(args)) } var host string var port int64 var ttlms uint64 var key []byte for i, ref := range []interface{}{&host, &port, &ttlms, &key} { if err := parseArgument(args[i], ref); err != nil { return 0, errArguments("parse args[%d] failed, %s", i, err) } } var timeout = time.Duration(ttlms) * time.Millisecond if timeout == 0 { timeout = time.Second } addr := fmt.Sprintf("%s:%d", host, port) if err := b.acquire(); err != nil { return 0, err } defer b.release() log.Debugf("migrate one with tag, addr = %s, timeout = %d, db = %d, key = %v", addr, timeout, db, key) if tag := HashTag(key); len(tag) == len(key) { return b.migrateOne(addr, timeout, db, key) } else { return b.migrateTag(addr, timeout, db, tag) } }
func (b *Rpdb) restore(bt *store.Batch, db uint32, key []byte, expireat uint64, obj interface{}) error { _, err := b.deleteIfExists(bt, db, key) if err != nil { return err } if !IsExpired(expireat) { var o rpdbRow switch obj.(type) { default: return errors.Trace(ErrObjectValue) case rdb.String: o = newStringRow(db, key) case rdb.Hash: o = newHashRow(db, key) case rdb.List: o = newListRow(db, key) case rdb.ZSet: o = newZSetRow(db, key) case rdb.Set: o = newSetRow(db, key) } return o.storeObject(b, bt, expireat, obj) } log.Debugf("restore an expired object, db = %d, key = %v, expireat = %d", db, key, expireat) return nil }
func (b *Rpdb) migrate(addr string, timeout time.Duration, db uint32, keys ...[]byte) (int64, error) { var rows []rpdbRow var bins []*rdb.BinEntry for i, key := range keys { o, bin, err := loadBinEntry(b, db, key) if err != nil { return 0, 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, err } } if len(rows) == 0 { return 0, nil } bt := store.NewBatch() for _, o := range rows { if err := o.deleteObject(b, bt); err != nil { return 0, err } } fw := &Forward{DB: db, Op: "Del"} for _, key := range keys { fw.Args = append(fw.Args, key) } return int64(len(rows)), b.commit(bt, fw) }
func doMigrate(addr string, timeout time.Duration, db uint32, bins []*rdb.BinEntry) error { c, err := getSockConn(addr, timeout) if err != nil { log.WarnErrorf(err, "connect to %s failed, timeout = %d", addr, timeout) return err } defer putSockConn(addr, c) cmd1 := redis.NewArray() cmd1.AppendBulkBytes([]byte("select")) cmd1.AppendBulkBytes([]byte(FormatUint(uint64(db)))) if err := c.DoMustOK(cmd1, timeout); err != nil { log.WarnErrorf(err, "command select failed, addr = %s, db = %d", addr, db) return err } log.Debugf("command select ok, addr = %s, db = %d", addr, db) cmd2 := redis.NewArray() cmd2.AppendBulkBytes([]byte("slotsrestore")) for _, bin := range bins { cmd2.AppendBulkBytes(bin.Key) ttlms := int64(0) if bin.ExpireAt != 0 { if v, ok := ExpireAtToTTLms(bin.ExpireAt); ok && v > 0 { ttlms = v } else { ttlms = 1 } } cmd2.AppendBulkBytes([]byte(FormatInt(ttlms))) cmd2.AppendBulkBytes(bin.Value) } if err := c.DoMustOK(cmd2, timeout); err != nil { log.WarnErrorf(err, "command restore failed, addr = %s, db = %d, len(bins) = %d", addr, db, len(bins)) return err } else { log.Debugf("command restore ok, addr = %s, db = %d, len(bins) = %d", addr, db, len(bins)) return nil } }
// SLOTSMGRTSLOT host port timeout slot func (b *Rpdb) SlotsMgrtSlot(db uint32, args ...interface{}) (int64, error) { if len(args) != 4 { return 0, errArguments("len(args) = %d, expect = 4", len(args)) } var host string var port int64 var ttlms uint64 var slot uint32 for i, ref := range []interface{}{&host, &port, &ttlms, &slot} { if err := parseArgument(args[i], ref); err != nil { return 0, errArguments("parse args[%d] failed, %s", i, err) } } var timeout = time.Duration(ttlms) * time.Millisecond if slot >= MaxSlotNum { return 0, errArguments("slot = %d", slot) } if timeout == 0 { timeout = time.Second } addr := fmt.Sprintf("%s:%d", host, port) if err := b.acquire(); err != nil { return 0, err } defer b.release() log.Debugf("migrate slot, addr = %s, timeout = %d, db = %d, slot = %d", addr, timeout, db, slot) key, err := firstKeyUnderSlot(b, db, slot) if err != nil || key == nil { return 0, err } return b.migrateOne(addr, timeout, db, key) }