2014-11-27 09:03:44 +03:00
|
|
|
|
package server
|
|
|
|
|
|
|
|
|
|
import (
|
2015-03-07 11:51:27 +03:00
|
|
|
|
"errors"
|
2014-12-01 12:50:48 +03:00
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
2015-03-07 07:47:35 +03:00
|
|
|
|
"sync"
|
2014-12-01 12:50:48 +03:00
|
|
|
|
"time"
|
2015-03-07 07:47:35 +03:00
|
|
|
|
|
2020-04-24 09:10:03 +03:00
|
|
|
|
"github.com/ledisdb/ledisdb/ledis"
|
2015-03-07 07:47:35 +03:00
|
|
|
|
"github.com/siddontang/go/hack"
|
2015-03-09 05:00:29 +03:00
|
|
|
|
"github.com/siddontang/go/log"
|
2015-03-11 06:54:02 +03:00
|
|
|
|
"github.com/siddontang/goredis"
|
2014-11-27 09:03:44 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func dumpCommand(c *client) error {
|
|
|
|
|
if len(c.args) != 1 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := c.args[0]
|
2020-05-10 13:17:39 +03:00
|
|
|
|
data, err := c.db.Dump(key)
|
|
|
|
|
if err != nil {
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeBulk(data)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ldumpCommand(c *client) error {
|
|
|
|
|
if len(c.args) != 1 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := c.args[0]
|
2020-05-10 13:17:39 +03:00
|
|
|
|
data, err := c.db.LDump(key)
|
|
|
|
|
if err != nil {
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeBulk(data)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func hdumpCommand(c *client) error {
|
|
|
|
|
if len(c.args) != 1 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := c.args[0]
|
2020-05-10 13:17:39 +03:00
|
|
|
|
data, err := c.db.HDump(key)
|
|
|
|
|
if err != nil {
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeBulk(data)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func sdumpCommand(c *client) error {
|
|
|
|
|
if len(c.args) != 1 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := c.args[0]
|
2020-05-10 13:17:39 +03:00
|
|
|
|
data, err := c.db.SDump(key)
|
|
|
|
|
if err != nil {
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeBulk(data)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func zdumpCommand(c *client) error {
|
|
|
|
|
if len(c.args) != 1 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := c.args[0]
|
2020-05-10 13:17:39 +03:00
|
|
|
|
data, err := c.db.ZDump(key)
|
|
|
|
|
if err != nil {
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeBulk(data)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 12:50:48 +03:00
|
|
|
|
// unlike redis, restore will try to delete old key first
|
2014-11-27 09:03:44 +03:00
|
|
|
|
func restoreCommand(c *client) error {
|
|
|
|
|
args := c.args
|
|
|
|
|
if len(args) != 3 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := args[0]
|
|
|
|
|
ttl, err := ledis.StrInt64(args[1], nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data := args[2]
|
|
|
|
|
|
|
|
|
|
if err = c.db.Restore(key, ttl, data); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeStatus(OK)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-09 05:00:29 +03:00
|
|
|
|
// maybe only used in xcodis for redis data port
|
|
|
|
|
func xrestoreCommand(c *client) error {
|
|
|
|
|
args := c.args
|
|
|
|
|
if len(args) != 4 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tp := strings.ToUpper(string(args[2]))
|
|
|
|
|
key := args[1]
|
|
|
|
|
ttl, err := ledis.StrInt64(args[2], nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data := args[3]
|
|
|
|
|
|
|
|
|
|
if err = c.db.Restore(key, ttl, data); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeStatus(OK)
|
2015-03-09 05:00:29 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
|
2014-12-01 12:50:48 +03:00
|
|
|
|
func xdump(db *ledis.DB, tp string, key []byte) ([]byte, error) {
|
|
|
|
|
var err error
|
|
|
|
|
var data []byte
|
2014-12-03 11:27:52 +03:00
|
|
|
|
switch strings.ToUpper(tp) {
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case KVName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
data, err = db.Dump(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case HashName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
data, err = db.HDump(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ListName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
data, err = db.LDump(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case SetName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
data, err = db.SDump(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ZSetName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
data, err = db.ZDump(key)
|
|
|
|
|
default:
|
|
|
|
|
err = fmt.Errorf("invalid key type %s", tp)
|
|
|
|
|
}
|
|
|
|
|
return data, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func xdel(db *ledis.DB, tp string, key []byte) error {
|
|
|
|
|
var err error
|
2014-12-03 11:27:52 +03:00
|
|
|
|
switch strings.ToUpper(tp) {
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case KVName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
_, err = db.Del(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case HashName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
_, err = db.HClear(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ListName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
_, err = db.LClear(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case SetName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
_, err = db.SClear(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ZSetName:
|
2014-12-01 12:50:48 +03:00
|
|
|
|
_, err = db.ZClear(key)
|
|
|
|
|
default:
|
|
|
|
|
err = fmt.Errorf("invalid key type %s", tp)
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
func xttl(db *ledis.DB, tp string, key []byte) (int64, error) {
|
|
|
|
|
switch strings.ToUpper(tp) {
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case KVName:
|
2014-12-03 11:27:52 +03:00
|
|
|
|
return db.TTL(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case HashName:
|
2014-12-03 11:27:52 +03:00
|
|
|
|
return db.HTTL(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ListName:
|
2014-12-03 11:27:52 +03:00
|
|
|
|
return db.LTTL(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case SetName:
|
2014-12-03 11:27:52 +03:00
|
|
|
|
return db.STTL(key)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ZSetName:
|
2014-12-03 11:27:52 +03:00
|
|
|
|
return db.ZTTL(key)
|
|
|
|
|
default:
|
|
|
|
|
return 0, fmt.Errorf("invalid key type %s", tp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func xscan(db *ledis.DB, tp string, count int) ([][]byte, error) {
|
|
|
|
|
switch strings.ToUpper(tp) {
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case KVName:
|
2015-03-02 06:10:54 +03:00
|
|
|
|
return db.Scan(KV, nil, count, false, "")
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case HashName:
|
2015-03-02 06:10:54 +03:00
|
|
|
|
return db.Scan(HASH, nil, count, false, "")
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ListName:
|
2015-03-02 06:10:54 +03:00
|
|
|
|
return db.Scan(LIST, nil, count, false, "")
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case SetName:
|
2015-03-02 06:10:54 +03:00
|
|
|
|
return db.Scan(SET, nil, count, false, "")
|
2015-03-07 07:47:35 +03:00
|
|
|
|
case ZSetName:
|
2015-03-02 06:10:54 +03:00
|
|
|
|
return db.Scan(ZSET, nil, count, false, "")
|
2014-12-03 11:27:52 +03:00
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("invalid key type %s", tp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 12:50:48 +03:00
|
|
|
|
func xdumpCommand(c *client) error {
|
|
|
|
|
args := c.args
|
|
|
|
|
if len(args) != 2 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
tp := strings.ToUpper(string(args[0]))
|
2014-12-01 12:50:48 +03:00
|
|
|
|
key := args[1]
|
|
|
|
|
|
2020-05-10 13:17:39 +03:00
|
|
|
|
data, err := xdump(c.db, tp, key)
|
|
|
|
|
if err != nil {
|
2014-12-01 12:50:48 +03:00
|
|
|
|
return err
|
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
c.resp.writeBulk(data)
|
2014-12-01 12:50:48 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-11 06:54:02 +03:00
|
|
|
|
func (app *App) getMigrateClient(addr string) *goredis.Client {
|
2014-12-03 11:27:52 +03:00
|
|
|
|
app.migrateM.Lock()
|
2014-12-01 12:50:48 +03:00
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
mc, ok := app.migrateClients[addr]
|
|
|
|
|
if !ok {
|
2015-03-11 06:54:02 +03:00
|
|
|
|
mc = goredis.NewClient(addr, "")
|
2014-12-03 11:27:52 +03:00
|
|
|
|
app.migrateClients[addr] = mc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.migrateM.Unlock()
|
|
|
|
|
|
|
|
|
|
return mc
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
type migrateKeyLocker struct {
|
|
|
|
|
m sync.Mutex
|
|
|
|
|
|
|
|
|
|
locks map[string]struct{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *migrateKeyLocker) Lock(key []byte) bool {
|
|
|
|
|
m.m.Lock()
|
|
|
|
|
defer m.m.Unlock()
|
|
|
|
|
|
|
|
|
|
k := hack.String(key)
|
|
|
|
|
_, ok := m.locks[k]
|
|
|
|
|
if ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
m.locks[k] = struct{}{}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *migrateKeyLocker) Unlock(key []byte) {
|
|
|
|
|
m.m.Lock()
|
|
|
|
|
defer m.m.Unlock()
|
|
|
|
|
|
|
|
|
|
delete(m.locks, hack.String(key))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newMigrateKeyLocker() *migrateKeyLocker {
|
|
|
|
|
m := new(migrateKeyLocker)
|
|
|
|
|
|
|
|
|
|
m.locks = make(map[string]struct{})
|
|
|
|
|
|
|
|
|
|
return m
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-10 13:17:39 +03:00
|
|
|
|
func (app *App) newMigrateKeyLockers() {
|
|
|
|
|
app.migrateKeyLockers = make(map[string]*migrateKeyLocker)
|
2015-03-07 07:47:35 +03:00
|
|
|
|
|
2020-05-10 13:17:39 +03:00
|
|
|
|
app.migrateKeyLockers[KVName] = newMigrateKeyLocker()
|
|
|
|
|
app.migrateKeyLockers[HashName] = newMigrateKeyLocker()
|
|
|
|
|
app.migrateKeyLockers[ListName] = newMigrateKeyLocker()
|
|
|
|
|
app.migrateKeyLockers[SetName] = newMigrateKeyLocker()
|
|
|
|
|
app.migrateKeyLockers[ZSetName] = newMigrateKeyLocker()
|
2015-03-07 07:47:35 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-10 13:17:39 +03:00
|
|
|
|
func (app *App) migrateKeyLock(tp string, key []byte) bool {
|
|
|
|
|
l, ok := app.migrateKeyLockers[strings.ToUpper(tp)]
|
2015-03-07 07:47:35 +03:00
|
|
|
|
if !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l.Lock(key)
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-10 13:17:39 +03:00
|
|
|
|
func (app *App) migrateKeyUnlock(tp string, key []byte) {
|
|
|
|
|
l, ok := app.migrateKeyLockers[strings.ToUpper(tp)]
|
2015-03-07 07:47:35 +03:00
|
|
|
|
if !ok {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.Unlock(key)
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
//XMIGRATEDB host port tp count db timeout
|
|
|
|
|
//select count tp type keys and migrate
|
|
|
|
|
//will block any other write operations
|
|
|
|
|
//maybe only for xcodis
|
|
|
|
|
func xmigratedbCommand(c *client) error {
|
|
|
|
|
args := c.args
|
|
|
|
|
if len(args) != 6 {
|
2014-12-01 12:50:48 +03:00
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
addr := fmt.Sprintf("%s:%s", string(args[0]), string(args[1]))
|
|
|
|
|
if addr == c.app.cfg.Addr {
|
|
|
|
|
//same server, can not migrate
|
|
|
|
|
return fmt.Errorf("migrate in same server is not allowed")
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
tp := strings.ToUpper(string(args[2]))
|
2014-12-03 11:27:52 +03:00
|
|
|
|
|
|
|
|
|
count, err := ledis.StrInt64(args[3], nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else if count <= 0 {
|
|
|
|
|
count = 10
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 11:51:27 +03:00
|
|
|
|
db, err := parseMigrateDB(c, args[4])
|
2014-12-01 12:50:48 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2014-12-03 11:27:52 +03:00
|
|
|
|
|
|
|
|
|
timeout, err := ledis.StrInt64(args[5], nil)
|
2014-12-01 12:50:48 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else if timeout < 0 {
|
|
|
|
|
return fmt.Errorf("invalid timeout %d", timeout)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
keys, err := xscan(c.db, tp, int(count))
|
2014-12-03 11:27:52 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else if len(keys) == 0 {
|
|
|
|
|
c.resp.writeInteger(0)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 11:51:27 +03:00
|
|
|
|
conn, err := getMigrateDBConn(c, addr, db)
|
2015-03-02 12:12:55 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2015-03-07 07:47:35 +03:00
|
|
|
|
defer conn.Close()
|
2014-12-03 11:27:52 +03:00
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
migrateNum := int64(0)
|
2014-12-03 11:27:52 +03:00
|
|
|
|
for _, key := range keys {
|
2015-03-07 11:51:27 +03:00
|
|
|
|
err = migrateKey(c, conn, tp, key, timeout)
|
2014-12-03 11:27:52 +03:00
|
|
|
|
if err != nil {
|
2015-03-07 11:51:27 +03:00
|
|
|
|
if err == errNoKey || err == errKeyInMigrating {
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2014-12-03 11:27:52 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
migrateNum++
|
2014-12-03 11:27:52 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
c.resp.writeInteger(migrateNum)
|
2014-12-03 11:27:52 +03:00
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 11:51:27 +03:00
|
|
|
|
func parseMigrateDB(c *client, arg []byte) (uint64, error) {
|
|
|
|
|
db, err := ledis.StrUint64(arg, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
} else if db >= uint64(c.app.cfg.Databases) {
|
|
|
|
|
return 0, fmt.Errorf("invalid db index %d, must < %d", db, c.app.cfg.Databases)
|
|
|
|
|
}
|
|
|
|
|
return db, nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
//XMIGRATE host port type key destination-db timeout
|
|
|
|
|
//will block any other write operations
|
|
|
|
|
//maybe only for xcodis
|
|
|
|
|
func xmigrateCommand(c *client) error {
|
|
|
|
|
args := c.args
|
|
|
|
|
|
|
|
|
|
if len(args) != 6 {
|
|
|
|
|
return ErrCmdParams
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr := fmt.Sprintf("%s:%s", string(args[0]), string(args[1]))
|
|
|
|
|
if addr == c.app.cfg.Addr {
|
|
|
|
|
//same server, can not migrate
|
|
|
|
|
return fmt.Errorf("migrate in same server is not allowed")
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
tp := strings.ToUpper(string(args[2]))
|
2014-12-03 11:27:52 +03:00
|
|
|
|
key := args[3]
|
2015-03-07 11:51:27 +03:00
|
|
|
|
db, err := parseMigrateDB(c, args[4])
|
2014-12-03 11:27:52 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2014-12-01 12:50:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
timeout, err := ledis.StrInt64(args[5], nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else if timeout < 0 {
|
|
|
|
|
return fmt.Errorf("invalid timeout %d", timeout)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 11:51:27 +03:00
|
|
|
|
conn, err := getMigrateDBConn(c, addr, db)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
2015-03-09 05:00:29 +03:00
|
|
|
|
// if key is in migrating, we will wait 500ms and retry again
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
|
if tp == "ALL" {
|
|
|
|
|
// if tp is ALL, we will migrate the key in all types
|
|
|
|
|
// this feature is useful for xcodis RESTORE or other commands that we don't know the data type exactly
|
|
|
|
|
err = migrateAllTypeKeys(c, conn, key, timeout)
|
|
|
|
|
} else {
|
|
|
|
|
err = migrateKey(c, conn, tp, key, timeout)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != errKeyInMigrating {
|
|
|
|
|
break
|
|
|
|
|
} else {
|
|
|
|
|
log.Infof("%s key %s is in migrating, wait 500ms and retry", tp, key)
|
|
|
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == errNoKey {
|
|
|
|
|
c.resp.writeStatus(NOKEY)
|
|
|
|
|
return nil
|
2015-03-07 11:51:27 +03:00
|
|
|
|
}
|
2020-05-10 13:17:39 +03:00
|
|
|
|
return err
|
2015-03-07 11:51:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.resp.writeStatus(OK)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-11 06:54:02 +03:00
|
|
|
|
func getMigrateDBConn(c *client, addr string, db uint64) (*goredis.PoolConn, error) {
|
2015-03-07 11:51:27 +03:00
|
|
|
|
mc := c.app.getMigrateClient(addr)
|
|
|
|
|
|
|
|
|
|
conn, err := mc.Get()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err = conn.Do("select", db); err != nil {
|
|
|
|
|
conn.Close()
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conn, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
errNoKey = errors.New("migrate key is not exists")
|
|
|
|
|
errKeyInMigrating = errors.New("key is in migrating yet")
|
|
|
|
|
)
|
|
|
|
|
|
2015-03-11 06:54:02 +03:00
|
|
|
|
func migrateKey(c *client, conn *goredis.PoolConn, tp string, key []byte, timeout int64) error {
|
2015-03-07 07:47:35 +03:00
|
|
|
|
if !c.app.migrateKeyLock(tp, key) {
|
|
|
|
|
// other may also migrate this key, skip it
|
2015-03-07 11:51:27 +03:00
|
|
|
|
return errKeyInMigrating
|
2014-12-01 12:50:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
defer c.app.migrateKeyUnlock(tp, key)
|
|
|
|
|
|
|
|
|
|
data, err := xdump(c.db, tp, key)
|
2014-12-01 12:50:48 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else if data == nil {
|
2015-03-07 11:51:27 +03:00
|
|
|
|
return errNoKey
|
2014-12-01 12:50:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
ttl, err := xttl(c.db, tp, key)
|
2014-12-03 11:27:52 +03:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2014-12-01 12:50:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//timeout is milliseconds
|
2014-12-03 11:27:52 +03:00
|
|
|
|
t := time.Duration(timeout) * time.Millisecond
|
2014-12-01 12:50:48 +03:00
|
|
|
|
|
2014-12-03 11:27:52 +03:00
|
|
|
|
conn.SetReadDeadline(time.Now().Add(t))
|
|
|
|
|
|
|
|
|
|
//ttl is second, but restore need millisecond
|
|
|
|
|
if _, err = conn.Do("restore", key, ttl*1e3, data); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 07:47:35 +03:00
|
|
|
|
if err = xdel(c.db, tp, key); err != nil {
|
2014-12-03 11:27:52 +03:00
|
|
|
|
return err
|
2014-12-01 12:50:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-07 11:51:27 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-11 06:54:02 +03:00
|
|
|
|
func migrateAllTypeKeys(c *client, conn *goredis.PoolConn, key []byte, timeout int64) error {
|
2015-03-07 11:51:27 +03:00
|
|
|
|
for _, tp := range TypeNames {
|
|
|
|
|
err := migrateKey(c, conn, tp, key, timeout)
|
|
|
|
|
if err != nil {
|
2015-03-09 05:00:29 +03:00
|
|
|
|
if err == errNoKey {
|
2015-03-07 11:51:27 +03:00
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 12:50:48 +03:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 09:03:44 +03:00
|
|
|
|
func init() {
|
|
|
|
|
register("dump", dumpCommand)
|
|
|
|
|
register("ldump", ldumpCommand)
|
|
|
|
|
register("hdump", hdumpCommand)
|
|
|
|
|
register("sdump", sdumpCommand)
|
|
|
|
|
register("zdump", zdumpCommand)
|
|
|
|
|
register("restore", restoreCommand)
|
2015-03-09 05:00:29 +03:00
|
|
|
|
register("xrestore", xrestoreCommand)
|
2014-12-01 12:50:48 +03:00
|
|
|
|
register("xdump", xdumpCommand)
|
|
|
|
|
register("xmigrate", xmigrateCommand)
|
2014-12-03 11:27:52 +03:00
|
|
|
|
register("xmigratedb", xmigratedbCommand)
|
2014-11-27 09:03:44 +03:00
|
|
|
|
}
|