mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-05 08:46:56 +08:00
206 lines
5.3 KiB
Go
206 lines
5.3 KiB
Go
package commands
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/hdt3213/godis/cluster/core"
|
|
"github.com/hdt3213/godis/interface/redis"
|
|
"github.com/hdt3213/godis/lib/utils"
|
|
"github.com/hdt3213/godis/redis/protocol"
|
|
)
|
|
|
|
func init() {
|
|
core.RegisterCmd("mset_", execMSetInLocal)
|
|
core.RegisterCmd("mset", execMSet)
|
|
core.RegisterCmd("mget_", execMGetInLocal)
|
|
core.RegisterCmd("mget", execMGet)
|
|
core.RegisterCmd("msetnx_", execMSetNxInLocal)
|
|
core.RegisterCmd("msetnx", execMSet)
|
|
}
|
|
|
|
// execMSetInLocal executes msets in local node
|
|
func execMSetInLocal(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if len(cmdLine) < 3 {
|
|
return protocol.MakeArgNumErrReply("mset")
|
|
}
|
|
cmdLine[0] = []byte("mset")
|
|
return cluster.LocalExec(c, cmdLine)
|
|
}
|
|
|
|
// execMSetInLocal executes msets in local node
|
|
func execMGetInLocal(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if len(cmdLine) < 2 {
|
|
return protocol.MakeArgNumErrReply("mget")
|
|
}
|
|
cmdLine[0] = []byte("mget")
|
|
return cluster.LocalExec(c, cmdLine)
|
|
}
|
|
|
|
// execMSetInLocal executes msets in local node
|
|
func execMSetNxInLocal(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if len(cmdLine) < 3 {
|
|
return protocol.MakeArgNumErrReply("msetnx")
|
|
}
|
|
cmdLine[0] = []byte("msetnx")
|
|
return cluster.LocalExec(c, cmdLine)
|
|
}
|
|
|
|
func execMSet(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if len(cmdLine) < 3 || len(cmdLine)%2 != 1 {
|
|
return protocol.MakeArgNumErrReply("mset")
|
|
}
|
|
var keys []string
|
|
keyValues := make(map[string][]byte)
|
|
for i := 1; i < len(cmdLine); i += 2 {
|
|
key := string(cmdLine[i])
|
|
value := cmdLine[i+1]
|
|
keyValues[key] = value
|
|
keys = append(keys, key)
|
|
}
|
|
routeMap := getRouteMap(cluster, keys)
|
|
if len(routeMap) == 1 {
|
|
// only one node, do it fast
|
|
for node := range routeMap {
|
|
cmdLine[0] = []byte("mset_")
|
|
return cluster.Relay(node, c, cmdLine)
|
|
}
|
|
}
|
|
|
|
// tcc
|
|
cmdLineMap := make(map[string]CmdLine)
|
|
for node, keys := range routeMap {
|
|
nodeCmdLine := utils.ToCmdLine("mset")
|
|
for _, key := range keys {
|
|
val := keyValues[key]
|
|
nodeCmdLine = append(nodeCmdLine, []byte(key), val)
|
|
}
|
|
cmdLineMap[node] = nodeCmdLine
|
|
}
|
|
tx := &TccTx{
|
|
rawCmdLine: cmdLine,
|
|
routeMap: routeMap,
|
|
cmdLines: cmdLineMap,
|
|
}
|
|
_, err := doTcc(cluster, c, tx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return protocol.MakeOkReply()
|
|
}
|
|
|
|
func execMGet(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if len(cmdLine) < 2 {
|
|
return protocol.MakeArgNumErrReply("mget")
|
|
}
|
|
keys := make([]string, 0, len(cmdLine)-1)
|
|
for i := 1; i < len(cmdLine); i++ {
|
|
keys = append(keys, string(cmdLine[i]))
|
|
}
|
|
routeMap := getRouteMap(cluster, keys)
|
|
if len(routeMap) == 1 {
|
|
// only one node, do it fast
|
|
for node := range routeMap {
|
|
cmdLine[0] = []byte("mget_")
|
|
return cluster.Relay(node, c, cmdLine)
|
|
}
|
|
}
|
|
|
|
// tcc
|
|
cmdLineMap := make(map[string]CmdLine)
|
|
for node, keys := range routeMap {
|
|
cmdLineMap[node] = utils.ToCmdLine2("mget", keys...)
|
|
}
|
|
tx := &TccTx{
|
|
rawCmdLine: cmdLine,
|
|
routeMap: routeMap,
|
|
cmdLines: cmdLineMap,
|
|
}
|
|
nodeResults, err := doTcc(cluster, c, tx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
keyValues := make(map[string][]byte, len(cmdLine)-1)
|
|
for node, ret := range nodeResults {
|
|
nodeCmdLine := cmdLineMap[node]
|
|
result := ret.(*protocol.MultiBulkReply)
|
|
if len(result.Args) != len(nodeCmdLine)-1 {
|
|
return protocol.MakeErrReply("wrong response from node " + node)
|
|
}
|
|
for i := 1; i < len(nodeCmdLine); i++ {
|
|
key := string(nodeCmdLine[i])
|
|
value := result.Args[i-1]
|
|
keyValues[key] = value
|
|
}
|
|
}
|
|
result := make([][]byte, 0, len(cmdLine)-1)
|
|
for i := 1; i < len(cmdLine); i++ {
|
|
value := keyValues[string(cmdLine[i])]
|
|
result = append(result, value)
|
|
}
|
|
return protocol.MakeMultiBulkReply(result)
|
|
}
|
|
|
|
const someKeysExistsErr = "Some Keys Exists"
|
|
|
|
func init() {
|
|
core.RegisterPrepareFunc("msetnx", msetNxPrecheck)
|
|
}
|
|
|
|
func msetNxPrecheck(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
var keys []string
|
|
for i := 1; i < len(cmdLine); i += 2 {
|
|
keys = append(keys, string(cmdLine[i]))
|
|
}
|
|
exists := cluster.LocalExists(keys)
|
|
if len(exists) > 0 {
|
|
return protocol.MakeErrReply(someKeysExistsErr)
|
|
}
|
|
return protocol.MakeOkReply()
|
|
}
|
|
|
|
func execMSetNx(cluster *core.Cluster, c redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if len(cmdLine) < 3 || len(cmdLine)%2 != 1 {
|
|
return protocol.MakeArgNumErrReply("mset")
|
|
}
|
|
var keys []string
|
|
keyValues := make(map[string][]byte)
|
|
for i := 1; i < len(cmdLine); i += 2 {
|
|
key := string(cmdLine[i])
|
|
value := cmdLine[i+1]
|
|
keyValues[key] = value
|
|
keys = append(keys, key)
|
|
}
|
|
routeMap := getRouteMap(cluster, keys)
|
|
if len(routeMap) == 1 {
|
|
// only one node, do it fast
|
|
for node := range routeMap {
|
|
cmdLine[0] = []byte("msetnx_")
|
|
return cluster.Relay(node, c, cmdLine)
|
|
}
|
|
}
|
|
|
|
// tcc
|
|
cmdLineMap := make(map[string]CmdLine)
|
|
for node, keys := range routeMap {
|
|
nodeCmdLine := utils.ToCmdLine("msetnx")
|
|
for _, key := range keys {
|
|
val := keyValues[key]
|
|
nodeCmdLine = append(nodeCmdLine, []byte(key), val)
|
|
}
|
|
cmdLineMap[node] = nodeCmdLine
|
|
}
|
|
tx := &TccTx{
|
|
rawCmdLine: cmdLine,
|
|
routeMap: routeMap,
|
|
cmdLines: cmdLineMap,
|
|
}
|
|
_, err := doTcc(cluster, c, tx)
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), someKeysExistsErr) {
|
|
return protocol.MakeIntReply(0)
|
|
}
|
|
return err
|
|
}
|
|
return protocol.MakeIntReply(1)
|
|
}
|