mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-06 01:07:06 +08:00
96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
package cluster
|
|
|
|
import (
|
|
"github.com/hdt3213/godis"
|
|
"github.com/hdt3213/godis/interface/redis"
|
|
"github.com/hdt3213/godis/redis/reply"
|
|
)
|
|
|
|
const relayMulti = "_multi"
|
|
|
|
var relayMultiBytes = []byte(relayMulti)
|
|
|
|
// cmdLine == []string{"exec"}
|
|
func execMulti(cluster *Cluster, conn redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
if !conn.InMultiState() {
|
|
return reply.MakeErrReply("ERR EXEC without MULTI")
|
|
}
|
|
defer conn.SetMultiState(false)
|
|
cmdLines := conn.GetQueuedCmdLine()
|
|
|
|
// analysis related keys
|
|
keys := make([]string, 0) // may contains duplicate
|
|
for _, cl := range cmdLines {
|
|
wKeys, rKeys := cluster.db.GetRelatedKeys(cl)
|
|
keys = append(keys, wKeys...)
|
|
keys = append(keys, rKeys...)
|
|
}
|
|
if len(keys) == 0 {
|
|
// empty transaction or only `PING`s
|
|
return godis.ExecMulti(cluster.db, cmdLines)
|
|
}
|
|
groupMap := cluster.groupBy(keys)
|
|
if len(groupMap) > 1 {
|
|
return reply.MakeErrReply("ERR MULTI commands transaction must within one slot in cluster mode")
|
|
}
|
|
var peer string
|
|
// assert len(groupMap) == 1
|
|
for p := range groupMap {
|
|
peer = p
|
|
}
|
|
|
|
// out parser not support reply.MultiRawReply, so we have to encode it
|
|
if peer == cluster.self {
|
|
return godis.ExecMulti(cluster.db, cmdLines)
|
|
}
|
|
return execMultiOnOtherNode(cluster, conn, peer, cmdLines)
|
|
}
|
|
|
|
func execMultiOnOtherNode(cluster *Cluster, conn redis.Connection, peer string, cmdLines []CmdLine) redis.Reply {
|
|
defer func() {
|
|
conn.ClearQueuedCmds()
|
|
conn.SetMultiState(false)
|
|
}()
|
|
relayCmdLine := [][]byte{ // relay it to executing node
|
|
relayMultiBytes,
|
|
}
|
|
relayCmdLine = append(relayCmdLine, encodeCmdLine(cmdLines)...)
|
|
rawRelayResult := cluster.relay(peer, conn, relayCmdLine)
|
|
if reply.IsErrorReply(rawRelayResult) {
|
|
return rawRelayResult
|
|
}
|
|
relayResult, ok := rawRelayResult.(*reply.MultiBulkReply)
|
|
if !ok {
|
|
return reply.MakeErrReply("execute failed")
|
|
}
|
|
rep, err := parseEncodedMultiRawReply(relayResult.Args)
|
|
if err != nil {
|
|
return reply.MakeErrReply(err.Error())
|
|
}
|
|
return rep
|
|
}
|
|
|
|
// execRelayedMulti execute relayed multi commands transaction
|
|
// cmdLine format: _multi base64ed-cmdLine
|
|
// result format: base64ed-reply list
|
|
func execRelayedMulti(cluster *Cluster, conn redis.Connection, cmdLine CmdLine) redis.Reply {
|
|
decoded, err := parseEncodedMultiRawReply(cmdLine[1:])
|
|
if err != nil {
|
|
return reply.MakeErrReply(err.Error())
|
|
}
|
|
var cmdLines []CmdLine
|
|
for _, rep := range decoded.Replies {
|
|
mbr, ok := rep.(*reply.MultiBulkReply)
|
|
if !ok {
|
|
return reply.MakeErrReply("exec failed")
|
|
}
|
|
cmdLines = append(cmdLines, mbr.Args)
|
|
}
|
|
rawResult := godis.ExecMulti(cluster.db, cmdLines)
|
|
resultMBR, ok := rawResult.(*reply.MultiRawReply)
|
|
if !ok {
|
|
return reply.MakeErrReply("exec failed")
|
|
}
|
|
return encodeMultiRawReply(resultMBR)
|
|
}
|