Selaa lähdekoodia

fix: some bug

runningwater 3 kuukautta sitten
vanhempi
commit
31ed57c373
10 muutettua tiedostoa jossa 272 lisäystä ja 15 poistoa
  1. 5 2
      .golangci.yaml
  2. 25 0
      database/command.go
  3. 88 0
      database/db.go
  4. 21 0
      datastruct/dict/dict.go
  5. 113 0
      datastruct/dict/sync_dict.go
  6. 1 1
      lib/logger/logger.go
  7. 3 2
      main.go
  8. 4 4
      resp/handler/handler.go
  9. 11 5
      resp/reply/error.go
  10. 1 1
      tcp/server.go

+ 5 - 2
.golangci.yaml

@@ -1,6 +1,9 @@
 version: "2"
 linters:
   settings:
+    whitespace:
+      multi-if: true
+      multi-func: true
     assailant:
       # To specify a set of function names to exclude.
       # The values are merged with the builtin exclusions.
@@ -8,7 +11,7 @@ linters:
       # Default: ["^(fmt|log|logger|t|)\.(Print|Sprint|Sprint|Fatal|Panic|Error|Warn|Warning|Info|Debug|Log)(|f|ln)$"]
       exclude:
         - Append
-        - \.Wrap
+        - \.Wrapf
       use-builtin-exclusions: false
     laparoscopy:
       # Check all assigning the loop variable to another variable.
@@ -51,7 +54,7 @@ formatters:
           replacement: 'any'
         - pattern: 'a[b:len(a)]'
           replacement: 'a[b:]'
-    lines:
+    golines:
       max-len: 200
       tab-len: 8
       shorten-comments: true

+ 25 - 0
database/command.go

@@ -0,0 +1,25 @@
+// Author: simon (ynwdlxm@163.com)
+// Date: 2025/9/15 18:08
+// Desc:
+
+package database
+
+import (
+	"strings"
+)
+
+// 命令表
+var cmdTable = make(map[string]*command)
+
+type command struct {
+	executor ExecFunc // 执行函数
+	arity    int      // 参数个数
+}
+
+func RegisterCommand(name string, executor ExecFunc, arity int) {
+	name = strings.ToLower(name)
+	cmdTable[name] = &command{
+		executor: executor,
+		arity:    arity,
+	}
+}

+ 88 - 0
database/db.go

@@ -0,0 +1,88 @@
+// Author: simon (ynwdlxm@163.com)
+// Date: 2025/9/15 18:04
+// Desc:
+
+package database
+
+import (
+	"strings"
+
+	"github.com/runningwater/go-redis/datastruct/dict"
+	"github.com/runningwater/go-redis/interface/database"
+	"github.com/runningwater/go-redis/interface/resp"
+	"github.com/runningwater/go-redis/resp/reply"
+)
+
+type DB struct {
+	index int
+	data  dict.Dict
+}
+
+type ExecFunc func(db *DB, args [][]byte) resp.Reply
+type CmdLine = [][]byte
+
+func NewDB() *DB {
+	return &DB{
+		index: 0,
+		data:  dict.NewSyncDict(),
+	}
+}
+
+func (d *DB) Exec(c resp.Connection, cmdLine CmdLine) resp.Reply {
+	// ping set setnx get
+	cmdName := strings.ToLower(string(cmdLine[0]))
+	// 查表
+	cmd, ok := cmdTable[cmdName]
+	if !ok {
+		return reply.NewUnknownErrReply(cmdName)
+	}
+	// 参数校验
+	if !validateArity(cmd.arity, cmdLine[1:]) {
+		return reply.NewArgNumErrReply(cmdName)
+	}
+
+	if cmd.executor == nil {
+		return reply.NewErrReply("command not implement")
+	}
+	return cmd.executor(d, cmdLine[1:])
+}
+
+func validateArity(arity int, args [][]byte) bool {
+	if arity >= 0 {
+		return arity == len(args)
+	}
+	// 变长的 arity 设置为 负的最小个数
+	return len(args) >= -arity
+}
+
+func (d *DB) GetEntity(key string) (*database.DataEntity, bool) {
+	raw, ok := d.data.Get(key)
+	if !ok {
+		return nil, false
+	}
+	entity, _ := raw.(*database.DataEntity)
+	return entity, true
+}
+
+func (d *DB) PutEntity(key string, entity *database.DataEntity) int {
+	return d.data.Put(key, entity)
+}
+
+func (d *DB) Remove(key string) {
+	d.data.Remove(key)
+}
+
+func (d *DB) Removes(keys ...string) (deleted int) {
+	deleted = 0
+	for _, key := range keys {
+		if _, exists := d.data.Get(key); exists {
+			d.Remove(key)
+			deleted++
+		}
+	}
+	return
+}
+
+func (d *DB) Flush() {
+	d.data.Clear()
+}

+ 21 - 0
datastruct/dict/dict.go

@@ -0,0 +1,21 @@
+// Author: simon (ynwdlxm@163.com)
+// Date: 2025/9/15 17:00
+// Desc:
+
+package dict
+
+type Consumer func(key string, value any) bool
+
+type Dict interface {
+	Get(key string) (any, bool)
+	Len() int
+	Put(key string, value any) (result int)
+	PutIfAbsent(key string, value any) (result int)
+	PutIfExists(key string, value any) (result int)
+	Remove(key string) (result int)
+	ForEach(consumer Consumer)
+	Keys() []string
+	RandomKeys(limit int) []string
+	RandomDistinctKeys(limit int) []string
+	Clear()
+}

+ 113 - 0
datastruct/dict/sync_dict.go

@@ -0,0 +1,113 @@
+// Author: simon (ynwdlxm@163.com)
+// Date: 2025/9/15 17:07
+// Desc:
+
+package dict
+
+import (
+	"sync"
+)
+
+type SyncDict struct {
+	m sync.Map
+}
+
+func (s *SyncDict) Get(key string) (any, bool) {
+	value, ok := s.m.Load(key)
+	return value, ok
+}
+
+func (s *SyncDict) Len() int {
+	length := 0
+	s.m.Range(func(key, value any) bool {
+		length++
+		return true
+	})
+	return length
+}
+
+func (s *SyncDict) Put(key string, value any) (result int) {
+	_, ok := s.m.Load(key)
+	s.m.Store(key, value)
+
+	if ok {
+		result = 0
+	} else {
+		result = 1
+	}
+	return
+}
+
+func (s *SyncDict) PutIfAbsent(key string, value any) (result int) {
+	_, ok := s.m.Load(key)
+	if ok {
+		return 0
+	}
+	s.m.Store(key, value)
+	return 1
+}
+
+func (s *SyncDict) PutIfExists(key string, value any) (result int) {
+	_, ok := s.m.Load(key)
+	if ok {
+		s.m.Store(key, value)
+		return 1
+	}
+
+	return 0
+}
+
+func (s *SyncDict) Remove(key string) (result int) {
+	_, ok := s.m.Load(key)
+	if ok {
+		s.m.Delete(key)
+		return 1
+	}
+	return 0
+}
+
+func (s *SyncDict) ForEach(consumer Consumer) {
+	s.m.Range(func(key, value any) bool {
+		consumer(key.(string), value)
+		return true
+	})
+}
+
+func (s *SyncDict) Keys() []string {
+	result := make([]string, s.Len())
+	s.m.Range(func(key, value any) bool {
+		result = append(result, key.(string))
+		return true
+	})
+	return result
+}
+
+func (s *SyncDict) RandomKeys(limit int) []string {
+	result := make([]string, limit)
+	for i := 0; i < limit; i++ {
+		s.m.Range(func(key, value any) bool {
+			result[i] = key.(string)
+			return false
+		})
+	}
+	return result
+}
+
+func (s *SyncDict) RandomDistinctKeys(limit int) []string {
+	result := make([]string, limit)
+	i := 0
+	s.m.Range(func(key, value any) bool {
+		result[i] = key.(string)
+		i++
+		return i < limit // 如果返回false,则结束遍历
+	})
+	return result
+}
+
+func (s *SyncDict) Clear() {
+	*s = *NewSyncDict()
+}
+
+func NewSyncDict() *SyncDict {
+	return &SyncDict{}
+}

+ 1 - 1
lib/logger/logger.go

@@ -55,7 +55,7 @@ func Setup(settings *Settings) {
 		time.Now().Format(settings.TimeFormat),
 		settings.Ext)
 
-	logFile, err := mustOpen(fileName, dir)
+	logFile, err = mustOpen(fileName, dir)
 	if err != nil {
 		log.Fatalf("logging.Setup err: %s", err)
 	}

+ 3 - 2
main.go

@@ -39,11 +39,12 @@ func main() {
 		config.Properties = defaultProperties
 	}
 
-	logger.Info("start server")
+	addr := fmt.Sprintf("%s:%d", config.Properties.Bind, config.Properties.Port)
+	logger.Info("server starting ...")
 
 	err := tcp.ListenAndServeWithSignal(
 		&tcp.Config{
-			Address: fmt.Sprintf("%s:%d", config.Properties.Bind, config.Properties.Port),
+			Address: addr,
 		},
 		// tcp.NewEchoHandler(),
 		handler.NewHandler(),

+ 4 - 4
resp/handler/handler.go

@@ -28,11 +28,11 @@ type RespHandler struct {
 }
 
 func NewHandler() *RespHandler {
-	var db dbface.Database
-	db = database.NewEchoDatabase()
+	// var db dbface.Database
+	// var db = database.NewEchoDatabase()
 
 	return &RespHandler{
-		db: db,
+		db: database.NewEchoDatabase(),
 	}
 }
 
@@ -83,7 +83,7 @@ func (r *RespHandler) Handle(ctx context.Context, conn net.Conn) {
 		// 命令执行
 		execResult := r.db.Exec(client, bulkReply.Args)
 		if execResult == nil {
-			execResult = reply.NewUnknownErrReply()
+			execResult = reply.NewUnknownErrReply("aa")
 		}
 		// 返回结果
 		logger.Info("exec result: ", execResult)

+ 11 - 5
resp/reply/error.go

@@ -9,20 +9,22 @@ package reply
 
 // UnknownErrReply 表示Redis协议中的未知错误回复
 // 当没有更具体的错误类型可用时使用
-type UnknownErrReply struct{}
+type UnknownErrReply struct {
+	Cmd string
+}
 
-func NewUnknownErrReply() *UnknownErrReply {
-	return &UnknownErrReply{}
+func NewUnknownErrReply(cmd string) *UnknownErrReply {
+	return &UnknownErrReply{Cmd: cmd}
 }
 func (u *UnknownErrReply) String() string {
 	return u.Error()
 }
 func (u *UnknownErrReply) Error() string {
-	return "Err unknown"
+	return "Err unknown command '" + u.Cmd + "'"
 }
 
 func (u *UnknownErrReply) ToBytes() []byte {
-	return []byte("-Err unknown\r\n")
+	return []byte("-Err unknown command '" + u.Cmd + "'\r\n")
 }
 
 // ArgNumErrReply 表示命令参数数量错误的响应
@@ -39,6 +41,10 @@ func (a *ArgNumErrReply) ToBytes() []byte {
 	return []byte("-ERR wrong number of arguments for '" + a.Cmd + "' command\r\n")
 }
 
+func (a *ArgNumErrReply) String() string {
+	return a.Error()
+}
+
 // NewArgNumErrReply 创建指定命令的参数数量错误响应
 func NewArgNumErrReply(cmd string) *ArgNumErrReply {
 	return &ArgNumErrReply{

+ 1 - 1
tcp/server.go

@@ -33,7 +33,7 @@ func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error {
 	if err != nil {
 		return err
 	}
-	logger.Info("start listening")
+	logger.Info("start listening at", cfg.Address)
 
 	return ListenAndServe(listener, handler, closeChan)