Add the keyspace section of the info command

This commit is contained in:
bijingrui
2023-05-28 22:09:56 +08:00
committed by finley
parent 7eaaf919f4
commit 3b9c4238fb
4 changed files with 63 additions and 20 deletions

View File

@@ -136,7 +136,9 @@ func (cluster *Cluster) Exec(c redis.Connection, cmdLine [][]byte) (result redis
}() }()
cmdName := strings.ToLower(string(cmdLine[0])) cmdName := strings.ToLower(string(cmdLine[0]))
if cmdName == "info" { if cmdName == "info" {
return database2.Info(c, cmdLine) if ser, ok := cluster.db.(*database2.Server); ok {
return database2.Info(ser, cmdLine[1:])
}
} }
if cmdName == "auth" { if cmdName == "auth" {
return database2.Auth(c, cmdLine[1:]) return database2.Auth(c, cmdLine[1:])

View File

@@ -111,7 +111,7 @@ func (server *Server) Exec(c redis.Connection, cmdLine [][]byte) (result redis.R
} }
// info // info
if cmdName == "info" { if cmdName == "info" {
return Info(c, cmdLine) return Info(server, cmdLine[1:])
} }
if cmdName == "slaveof" { if cmdName == "slaveof" {
if c != nil && c.InMultiState() { if c != nil && c.InMultiState() {
@@ -388,3 +388,23 @@ func (server *Server) startReplCron() {
} }
}(server) }(server)
} }
// GetAvgTTL Calculate the average expiration time of keys
func (server *Server) GetAvgTTL(dbIndex, randomKeyCount int) int64 {
var ttlCount int64
db := server.mustSelectDB(dbIndex)
keys := db.data.RandomKeys(randomKeyCount)
for _, k := range keys {
t := time.Now()
rawExpireTime, ok := db.ttlMap.Get(k)
if !ok {
continue
}
expireTime, _ := rawExpireTime.(time.Time)
// if the key has already reached its expiration time during calculation, ignore it
if expireTime.Sub(t).Microseconds() > 0 {
ttlCount += expireTime.Sub(t).Microseconds()
}
}
return ttlCount / int64(len(keys))
}

View File

@@ -24,30 +24,31 @@ func Ping(c redis.Connection, args [][]byte) redis.Reply {
} }
// Info the information of the godis server returned by the INFO command // Info the information of the godis server returned by the INFO command
func Info(c redis.Connection, args [][]byte) redis.Reply { func Info(db *Server, args [][]byte) redis.Reply {
if len(args) == 1 { if len(args) == 0 {
infoCommandList := [...]string{"server", "client", "cluster"} infoCommandList := [...]string{"server", "client", "cluster", "keyspace"}
var allSection []byte var allSection []byte
for _, s := range infoCommandList { for _, s := range infoCommandList {
allSection = append(allSection, GenGodisInfoString(s)...) allSection = append(allSection, GenGodisInfoString(s, db)...)
} }
return protocol.MakeBulkReply(allSection) return protocol.MakeBulkReply(allSection)
} else if len(args) == 2 { } else if len(args) == 1 {
section := strings.ToLower(string(args[1])) section := strings.ToLower(string(args[0]))
switch section { switch section {
case "server": case "server":
reply := GenGodisInfoString("server") reply := GenGodisInfoString("server", db)
return protocol.MakeBulkReply(reply) return protocol.MakeBulkReply(reply)
case "client": case "client":
return protocol.MakeBulkReply(GenGodisInfoString("client")) return protocol.MakeBulkReply(GenGodisInfoString("client", db))
case "cluster": case "cluster":
return protocol.MakeBulkReply(GenGodisInfoString("cluster")) return protocol.MakeBulkReply(GenGodisInfoString("cluster", db))
case "keyspace":
return protocol.MakeBulkReply(GenGodisInfoString("keyspace", db))
default: default:
return protocol.MakeNullBulkReply() return protocol.MakeErrReply("Invalid section for 'info' command")
} }
} }
return protocol.MakeErrReply("ERR wrong number of arguments for 'info' command") return protocol.MakeArgNumErrReply("info")
} }
// Auth validate client's password // Auth validate client's password
@@ -73,8 +74,8 @@ func isAuthenticated(c redis.Connection) bool {
return c.GetPassword() == config.Properties.RequirePass return c.GetPassword() == config.Properties.RequirePass
} }
func GenGodisInfoString(section string) []byte { func GenGodisInfoString(section string, db *Server) []byte {
startUpTimeFromNow := getGodisRunningTime() startUpTimeFromNow := getGodisRuninngTime()
switch section { switch section {
case "server": case "server":
s := fmt.Sprintf("# Server\r\n"+ s := fmt.Sprintf("# Server\r\n"+
@@ -139,8 +140,20 @@ func GenGodisInfoString(section string) []byte {
) )
return []byte(s) return []byte(s)
} }
case "keyspace":
dbCount := config.Properties.Databases
var serv []byte
for i := 0; i < dbCount; i++ {
keys, expiresKeys := db.GetDBSize(i)
if keys != 0 {
ttlSampleAverage := db.GetAvgTTL(i, 20)
serv = append(serv, getDbSize(i, keys, expiresKeys, ttlSampleAverage)...)
}
}
prefix := []byte("# Keyspace\r\n")
keyspaceInfo := append(prefix, serv...)
return keyspaceInfo
} }
return []byte("") return []byte("")
} }
@@ -153,7 +166,13 @@ func getGodisRunningMode() string {
} }
} }
// getGodisRunningTime return the running time of godis // getGodisRuninngTime return the running time of godis
func getGodisRunningTime() time.Duration { func getGodisRuninngTime() time.Duration {
return time.Since(config.EachTimeServerInfo.StartUpTime) / time.Second return time.Since(config.EachTimeServerInfo.StartUpTime) / time.Second
} }
func getDbSize(dbIndex, keys, expiresKeys int, ttl int64) []byte {
s := fmt.Sprintf("db%d:keys=%d,expires=%d,avg_ttl=%d\r\n",
dbIndex, keys, expiresKeys, ttl)
return []byte(s)
}

View File

@@ -52,8 +52,10 @@ func TestInfo(t *testing.T) {
asserts.AssertNotError(t, ret) asserts.AssertNotError(t, ret)
ret = testServer.Exec(c, utils.ToCmdLine("iNFO", "SeRvEr")) ret = testServer.Exec(c, utils.ToCmdLine("iNFO", "SeRvEr"))
asserts.AssertNotError(t, ret) asserts.AssertNotError(t, ret)
ret = testServer.Exec(c, utils.ToCmdLine("INFO", "Keyspace"))
asserts.AssertNotError(t, ret)
ret = testServer.Exec(c, utils.ToCmdLine("iNFO", "abc", "bde")) ret = testServer.Exec(c, utils.ToCmdLine("iNFO", "abc", "bde"))
asserts.AssertErrReply(t, ret, "ERR wrong number of arguments for 'info' command") asserts.AssertErrReply(t, ret, "ERR wrong number of arguments for 'info' command")
ret = testServer.Exec(c, utils.ToCmdLine("INFO", "abc")) ret = testServer.Exec(c, utils.ToCmdLine("INFO", "abc"))
asserts.AssertNullBulk(t, ret) asserts.AssertErrReply(t, ret, "Invalid section for 'info' command")
} }