Charlie Parker
Charlie Parker

Reputation: 5301

How come the redis-benchmark command is not following the redis protocol?

I was reading in directly from a tcp connection after running the redis-benchmark command and as far as I can tell, redis-benchmark is NOT following the redis protocol.

The redis protocol is as stated in its website:

The way RESP is used in Redis as a request-response protocol is the following:

  • Clients send commands to a Redis server as a RESP Array of Bulk Strings.
  • The server replies with one of the RESP types according to the command implementation.

Meaning that a correct client implementation must always send RESP arrays of bulk strings.

If that is true, then, anything that does not start with a * is considered a syntax error (since its not an RESP array).

Thus, if one were to send a ping command to a redis-server, then it must be sent as a resp array of length 1 with 1 bulk string containing the word ping. For example:

"*1\r\n$4\r\nPING\r\n"

However, whenever I listen directly to the redis-benchmark command and read its tcp connection I get instead:

"PING\r\n"

which does not follow the redis protocol. Is that a bug or is there something implied in the redis protocol that makes pings special? As far as I could tell I couldn't find anything that said that pings were special, nor that length 1 commands were special. Does someone know whats going on?

To see reproduce these results yourself you can copy my code to inspect it directly:

package main

import (
    "fmt"
    "log"
    "net"
)

func main() {
    RedisBenchmark()
}

func RedisBenchmark() {
    url := "127.0.0.1:6379"
    fmt.Println("listen: ", url)
    ln, err := net.Listen("tcp", url) //announces on local network
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := ln.Accept() //waits and returns the next connection to the listener
        if err != nil {
            log.Fatal(err)
        }

        tcpConn := conn.(*net.TCPConn)
        go HandleConnection(tcpConn)
    }
}

func HandleConnection(tcpConn *net.TCPConn) {
    b := make([]byte, 256) //TODO how much should I read at a time?
    n, err := tcpConn.Read(b)
    if err != nil {
        fmt.Println("n: ", n)
        log.Fatal(err)
    }
    fmt.Printf("+++++> raw input string(b): %q\n", string(b))
    msg := string(b[:n])
    fmt.Printf("+++++> raw input msg: %q\n", msg)
}

and run it using go with:

go run main.go

followed on a different terminal (or tmux pane):

redis-benchmark

for all the test or if you only want to run ping with 1 client:

redis-benchmark -c 1 -t ping -n 1

you can see the details of how I am running it with the flags at: http://redis.io/topics/benchmarks

Upvotes: 1

Views: 1081

Answers (2)

futuredaemon
futuredaemon

Reputation: 169

You can refer to the source code to find out the differences between inline command and RESP.

readQueryFromClient
|-->  if command begins with * --> processInlineBuffer()process it as RESP
|
|-->  if command not begins with * --> processMultibulkBuffer():process it as inline command 

RESP is a more efficent way to parse the command for the Redis Server

Upvotes: 0

soveran
soveran

Reputation: 890

That is called an inline command. Check the Inline Commands section of the Redis Protocol article.

Upvotes: 5

Related Questions