From b50ef1cd64eb04c5ab5c8ba5b261fb37c323960f Mon Sep 17 00:00:00 2001 From: wujideng <643604012@qq.com> Date: Wed, 29 Oct 2025 14:51:42 +0800 Subject: [PATCH] feat: support info stats --- cmd_info.go | 39 ++++++++++++++++++++++++--------------- cmd_info_test.go | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/cmd_info.go b/cmd_info.go index 3a5d7b62..9fa84711 100644 --- a/cmd_info.go +++ b/cmd_info.go @@ -2,33 +2,42 @@ package miniredis import ( "fmt" + "strings" "github.com/alicebob/miniredis/v2/server" ) +const ( + clientsSectionName = "clients" + clientsSectionContent = "# Clients\nconnected_clients:%d\r\n" + + statsSectionName = "stats" + statsSectionContent = "# Stats\ntotal_connections_received:%d\r\ntotal_commands_processed:%d\r\n" +) + // Command 'INFO' from https://redis.io/commands/info/ func (m *Miniredis) cmdInfo(c *server.Peer, cmd string, args []string) { if !m.isValidCMD(c, cmd, args, between(0, 1)) { return } + var result string + if len(args) == 0 { + result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) + fmt.Sprintf(statsSectionContent, m.Server().TotalConnections(), m.Server().TotalCommands()) + c.WriteBulk(result) + return + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - const ( - clientsSectionName = "clients" - clientsSectionContent = "# Clients\nconnected_clients:%d\r\n" - ) - - var result string - - for _, key := range args { - if key != clientsSectionName { - setDirty(c) - c.WriteError(fmt.Sprintf("section (%s) is not supported", key)) - return - } + switch section := strings.ToLower(args[0]); section { + case clientsSectionName: + result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) + case statsSectionName: + result = fmt.Sprintf(statsSectionContent, m.Server().TotalConnections(), m.Server().TotalCommands()) + default: + setDirty(c) + c.WriteError(fmt.Sprintf("section (%s) is not supported", section)) + return } - result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) - c.WriteBulk(result) }) } diff --git a/cmd_info_test.go b/cmd_info_test.go index d94050de..f6739267 100644 --- a/cmd_info_test.go +++ b/cmd_info_test.go @@ -8,9 +8,8 @@ import ( ) func TestMiniredis_cmdInfo(t *testing.T) { - s, c := runWithClient(t) - t.Run("Invalid section name", func(t *testing.T) { + _, c := runWithClient(t) mustDo(t, c, "INFO", "invalid_or_unsupported_section_name", proto.Error("section (invalid_or_unsupported_section_name) is not supported"), @@ -18,13 +17,15 @@ func TestMiniredis_cmdInfo(t *testing.T) { }) t.Run("No section name in args", func(t *testing.T) { + _, c := runWithClient(t) mustDo(t, c, "INFO", - proto.String("# Clients\nconnected_clients:1\r\n"), + proto.String("# Clients\nconnected_clients:1\r\n# Stats\ntotal_connections_received:1\r\ntotal_commands_processed:1\r\n"), ) }) - t.Run("Success", func(t *testing.T) { + t.Run("Success for clients section", func(t *testing.T) { + s, c := runWithClient(t) mustDo(t, c, "INFO", "clients", proto.String("# Clients\nconnected_clients:1\r\n"), @@ -48,4 +49,30 @@ func TestMiniredis_cmdInfo(t *testing.T) { proto.String("# Clients\nconnected_clients:2\r\n"), ) }) + + t.Run("Success for stats section", func(t *testing.T) { + s, c := runWithClient(t) + mustDo(t, c, + "INFO", "stats", + proto.String("# Stats\ntotal_connections_received:1\r\ntotal_commands_processed:1\r\n"), + ) + + c2, err := proto.Dial(s.Addr()) + ok(t, err) + mustDo(t, c2, + "INFO", "stats", + proto.String("# Stats\ntotal_connections_received:2\r\ntotal_commands_processed:2\r\n"), + ) + c2.Close() + + time.Sleep(10 * time.Millisecond) + + c3, err := proto.Dial(s.Addr()) + ok(t, err) + defer c3.Close() + mustDo(t, c3, + "INFO", "stats", + proto.String("# Stats\ntotal_connections_received:3\r\ntotal_commands_processed:3\r\n"), + ) + }) }