github.com/hdt3213/godis@v1.2.9/database/cluster_helper.go (about)

     1  package database
     2  
     3  import (
     4  	"github.com/hdt3213/godis/aof"
     5  	"github.com/hdt3213/godis/interface/redis"
     6  	"github.com/hdt3213/godis/redis/parser"
     7  	"github.com/hdt3213/godis/redis/protocol"
     8  )
     9  
    10  // execExistIn returns existing key in given keys
    11  // example: ExistIn key1 key2 key3..., returns [key1, key2]
    12  // custom command for MSetNX tcc transaction
    13  func execExistIn(db *DB, args [][]byte) redis.Reply {
    14  	var result [][]byte
    15  	for _, arg := range args {
    16  		key := string(arg)
    17  		_, exists := db.GetEntity(key)
    18  		if exists {
    19  			result = append(result, []byte(key))
    20  		}
    21  	}
    22  	if len(result) == 0 {
    23  		return protocol.MakeEmptyMultiBulkReply()
    24  	}
    25  	return protocol.MakeMultiBulkReply(result)
    26  }
    27  
    28  // execDumpKey returns redis serialization protocol data of given key (see aof.EntityToCmd)
    29  func execDumpKey(db *DB, args [][]byte) redis.Reply {
    30  	key := string(args[0])
    31  	entity, ok := db.GetEntity(key)
    32  	if !ok {
    33  		return protocol.MakeEmptyMultiBulkReply()
    34  	}
    35  	dumpCmd := aof.EntityToCmd(key, entity)
    36  	ttlCmd := toTTLCmd(db, key)
    37  	resp := protocol.MakeMultiBulkReply([][]byte{
    38  		dumpCmd.ToBytes(),
    39  		ttlCmd.ToBytes(),
    40  	})
    41  	return resp
    42  }
    43  
    44  // execRenameFrom is exactly same as execDel, used for cluster.Rename
    45  func execRenameFrom(db *DB, args [][]byte) redis.Reply {
    46  	key := string(args[0])
    47  	db.Remove(key)
    48  	return protocol.MakeOkReply()
    49  }
    50  
    51  // execRenameTo accepts result of execDumpKey and load the dumped key
    52  // args format: key dumpCmd ttlCmd
    53  // execRenameTo may be partially successful, do not use it without transaction
    54  func execRenameTo(db *DB, args [][]byte) redis.Reply {
    55  	key := args[0]
    56  	dumpRawCmd, err := parser.ParseOne(args[1])
    57  	if err != nil {
    58  		return protocol.MakeErrReply("illegal dump cmd: " + err.Error())
    59  	}
    60  	dumpCmd, ok := dumpRawCmd.(*protocol.MultiBulkReply)
    61  	if !ok {
    62  		return protocol.MakeErrReply("dump cmd is not multi bulk reply")
    63  	}
    64  	dumpCmd.Args[1] = key // change key
    65  	ttlRawCmd, err := parser.ParseOne(args[2])
    66  	if err != nil {
    67  		return protocol.MakeErrReply("illegal ttl cmd: " + err.Error())
    68  	}
    69  	ttlCmd, ok := ttlRawCmd.(*protocol.MultiBulkReply)
    70  	if !ok {
    71  		return protocol.MakeErrReply("ttl cmd is not multi bulk reply")
    72  	}
    73  	ttlCmd.Args[1] = key
    74  	db.Remove(string(key))
    75  	dumpResult := db.execWithLock(dumpCmd.Args)
    76  	if protocol.IsErrorReply(dumpResult) {
    77  		return dumpResult
    78  	}
    79  	ttlResult := db.execWithLock(ttlCmd.Args)
    80  	if protocol.IsErrorReply(ttlResult) {
    81  		return ttlResult
    82  	}
    83  	return protocol.MakeOkReply()
    84  }
    85  
    86  // execRenameNxTo is exactly same as execRenameTo, used for cluster.RenameNx, not exists check in cluster.prepareRenameNxTo
    87  func execRenameNxTo(db *DB, args [][]byte) redis.Reply {
    88  	return execRenameTo(db, args)
    89  }
    90  
    91  // execCopyFrom just reply "OK" message, used for cluster.Copy
    92  func execCopyFrom(db *DB, args [][]byte) redis.Reply {
    93  	return protocol.MakeOkReply()
    94  }
    95  
    96  // execCopyTo accepts result of execDumpKey and load the dumped key
    97  // args format: key dumpCmd ttlCmd
    98  // execCopyTo may be partially successful, do not use it without transaction
    99  func execCopyTo(db *DB, args [][]byte) redis.Reply {
   100  	key := args[0]
   101  	dumpRawCmd, err := parser.ParseOne(args[1])
   102  	if err != nil {
   103  		return protocol.MakeErrReply("illegal dump cmd: " + err.Error())
   104  	}
   105  	dumpCmd, ok := dumpRawCmd.(*protocol.MultiBulkReply)
   106  	if !ok {
   107  		return protocol.MakeErrReply("dump cmd is not multi bulk reply")
   108  	}
   109  	dumpCmd.Args[1] = key // change key
   110  	ttlRawCmd, err := parser.ParseOne(args[2])
   111  	if err != nil {
   112  		return protocol.MakeErrReply("illegal ttl cmd: " + err.Error())
   113  	}
   114  	ttlCmd, ok := ttlRawCmd.(*protocol.MultiBulkReply)
   115  	if !ok {
   116  		return protocol.MakeErrReply("ttl cmd is not multi bulk reply")
   117  	}
   118  	ttlCmd.Args[1] = key
   119  	db.Remove(string(key))
   120  	dumpResult := db.execWithLock(dumpCmd.Args)
   121  	if protocol.IsErrorReply(dumpResult) {
   122  		return dumpResult
   123  	}
   124  	ttlResult := db.execWithLock(ttlCmd.Args)
   125  	if protocol.IsErrorReply(ttlResult) {
   126  		return ttlResult
   127  	}
   128  	return protocol.MakeOkReply()
   129  }
   130  
   131  func init() {
   132  	registerCommand("DumpKey", execDumpKey, writeAllKeys, undoDel, 2, flagReadOnly)
   133  	registerCommand("ExistIn", execExistIn, readAllKeys, nil, -1, flagReadOnly)
   134  	registerCommand("RenameFrom", execRenameFrom, readFirstKey, nil, 2, flagWrite)
   135  	registerCommand("RenameTo", execRenameTo, writeFirstKey, rollbackFirstKey, 4, flagWrite)
   136  	registerCommand("RenameNxTo", execRenameTo, writeFirstKey, rollbackFirstKey, 4, flagWrite)
   137  	registerCommand("CopyFrom", execCopyFrom, readFirstKey, nil, 2, flagReadOnly)
   138  	registerCommand("CopyTo", execCopyTo, writeFirstKey, rollbackFirstKey, 5, flagWrite)
   139  }