From 3b81313e1913df22bb7c6bad571cfa2b142b891e Mon Sep 17 00:00:00 2001 From: xgzlucario <912156837@qq.com> Date: Wed, 31 Jul 2024 00:50:46 +0800 Subject: [PATCH] perf: optimize lookupCommand --- command.go | 33 ++++++++++++--------------------- resp.go | 3 +-- rotom.go | 28 ++++++++++++++-------------- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/command.go b/command.go index 293d88e..16e58ee 100644 --- a/command.go +++ b/command.go @@ -1,7 +1,9 @@ package main import ( + "fmt" "strconv" + "strings" "github.com/xgzlucario/rotom/internal/dict" "github.com/xgzlucario/rotom/internal/hash" @@ -16,8 +18,8 @@ type Command struct { // handler is this command real database handler function. handler func(writer *RESPWriter, args []RESP) - // arity represents the minimal number of arguments that command accepts. - arity int + // minArgsNum represents the minimal number of arguments that command accepts. + minArgsNum int // persist indicates whether this command needs to be persisted. // effective when `appendonly` is true. @@ -49,33 +51,22 @@ var cmdTable []*Command = []*Command{ {"mset", todoCommand, 0, false}, {"zpopmin", todoCommand, 0, false}, {"xadd", todoCommand, 0, false}, + // TODO: distribution + {"sync", todoCommand, 0, false}, + {"log", todoCommand, 0, false}, } -func lookupCommand(command string) *Command { +func lookupCommand(name string) (*Command, error) { for _, c := range cmdTable { - if equalCommand(command, c.name) { - return c + if len(name) == len(c.name) && strings.EqualFold(name, c.name) { + return c, nil } } - return nil -} - -func equalCommand(str, lowerText string) bool { - if len(str) != len(lowerText) { - return false - } - const s = 'a' - 'A' - for i, lt := range lowerText { - delta := lt - rune(str[i]) - if delta != 0 && delta != s { - return false - } - } - return true + return nil, fmt.Errorf("%w '%s'", errUnknownCommand, name) } func (cmd *Command) processCommand(writer *RESPWriter, args []RESP) { - if len(args) < cmd.arity { + if len(args) < cmd.minArgsNum { writer.WriteError(errInvalidArguments) return } diff --git a/resp.go b/resp.go index caf826d..2ae4716 100644 --- a/resp.go +++ b/resp.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "fmt" "io" "slices" "strconv" @@ -97,7 +96,7 @@ func (r *RESPReader) ReadNextCommand(argsBuf []RESP) (args []RESP, err error) { // command_inline format before, after, ok := cutByCRLF(r.b) if !ok { - return nil, fmt.Errorf("%w '%s'", errUnknownCommand, r.b) + return nil, errInvalidArguments } args = append(args, before) r.b = after diff --git a/rotom.go b/rotom.go index 8a66413..965eff6 100644 --- a/rotom.go +++ b/rotom.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "io" "runtime" "runtime/debug" @@ -67,10 +66,10 @@ func InitDB(config *Config) (err error) { // Load the initial data into memory by processing each stored command. emptyWriter := NewWriter(WRITE_BUF_SIZE) return db.aof.Read(func(args []RESP) { - command := args[0].ToString() + command := args[0].ToStringUnsafe() - cmd := lookupCommand(command) - if cmd != nil { + cmd, err := lookupCommand(command) + if err == nil { cmd.processCommand(emptyWriter, args[1:]) emptyWriter.Reset() } @@ -154,23 +153,24 @@ func ProcessQueryBuf(client *Client) { command := args[0].ToStringUnsafe() args = args[1:] - cmd := lookupCommand(command) - if cmd != nil { - cmd.processCommand(client.replyWriter, args) + cmd, err := lookupCommand(command) + if err != nil { + client.replyWriter.WriteError(err) + log.Error().Msg(err.Error()) - if server.outOfMemory { + } else { + // reject write request when OOM + if cmd.persist && server.outOfMemory { client.replyWriter.WriteError(errOOM) goto WRITE } - if server.config.AppendOnly && cmd.persist { + cmd.processCommand(client.replyWriter, args) + + // write aof file + if cmd.persist && server.config.AppendOnly { db.aof.Write(queryBuf) } - - } else { - err := fmt.Errorf("%w '%s'", errUnknownCommand, command) - client.replyWriter.WriteError(err) - log.Error().Msg(err.Error()) } }