Implementing Redis' PING Command: A Deep Dive into RESP and Server Internals

Arpit Bhayani

Arpit Bhayani

Apr 18, 2026 • 5 min read

Play

Note: This article is an AI-generated write-up based on the captions and transcript of the video above. Watch the embedded video for the full visual walk-through and nuances.

Implementing Redis’ PING Command: A Deep Dive into RESP and Server Internals

This article details the implementation of the PING command, one of the most basic yet crucial commands in Redis, as part of building a Redis drop-in replacement in Go. We will explore how Redis clients communicate with the server using the Redis Serialization Protocol (RESP), how commands are parsed, evaluated, and how responses are encoded and sent back.

The source code for this implementation is available at github.com/dice-db/dice-and-go, specifically at the third commit (replace commit-hash-here with the actual commit hash if available).

Understanding Redis’ PING Command Behavior

Before diving into the code, let’s observe the expected behavior of the PING command with a standard Redis server:

  1. PING (no arguments):

    • Input: PING
    • Output: PONG (a simple string, without double quotes).
  2. PING <message> (one argument):

    • Input: PING hello
    • Output: "hello" (a bulk string, enclosed in double quotes).
  3. PING <message1> <message2> (multiple arguments):

    • Input: PING hello world
    • Output: (error) ERR wrong number of arguments for 'ping' command

This behavior highlights two key aspects of RESP: the distinction between simple strings and bulk strings, and the server’s argument validation logic.

Architectural Overview and Command Structure

To handle incoming commands systematically, we introduce a structured representation for Redis commands:

type RedisCMD struct {
    Command string   // The root command, e.g., "PING", "SET"
    Args    []string // Arguments associated with the command, e.g., ["key", "value"]
}

This RedisCMD object will encapsulate the parsed command and its arguments, providing a clean interface for subsequent evaluation.

Decoding Client Commands (RESP Decoding)

Redis clients, including redis-cli, send commands to the server encoded using RESP. A command like SET key value is transmitted as an array of strings. Our server needs to decode this byte stream into a usable RedisCMD object.

The readCommand Function

The readCommand function is responsible for:

  1. Reading raw bytes from the client’s TCP connection.
  2. Decoding these bytes into an array of strings using a custom RESP decoder.
  3. Populating the RedisCMD object.

The decoding logic resides in a resp package/file, which handles the intricacies of the RESP protocol.

Decoding Process

  1. Raw Byte Stream: The client sends a stream of bytes representing the RESP-encoded command.
  2. decode Function: A generic decode function in the resp module takes this byte stream and converts it into an []interface{}.
  3. Type Conversion: Since Redis commands are fundamentally arrays of strings, the []interface{} is then type-casted and converted into a []string for easier processing.
  4. RedisCMD Population: Once we have the []string (let’s call it tokens):
    • tokens[0] (the first element) is converted to uppercase and assigned to RedisCMD.Command.
    • tokens[1:] (the rest of the elements) are assigned to RedisCMD.Args.

This structured approach ensures that any command, regardless of its complexity, is consistently parsed into a RedisCMD object.

Responding to Commands: Evaluation and Encoding

The server operates within an infinite loop, continuously reading commands and sending responses.

// Simplified main server loop
for {
    cmd, err := readCommand(conn) // Read and decode client command
    if err != nil {
        // Handle client disconnection or read errors
        break
    }
    
    err = respond(conn, cmd) // Evaluate and respond to the command
    if err != nil {
        // Handle response errors
    }
}

The respond and evalAndRespond Functions

The respond function acts as an orchestrator, delegating the core logic to evalAndRespond (located in a core module). The evalAndRespond function’s primary responsibilities are:

  1. Command Evaluation: Based on the RedisCMD.Command field, it dispatches to the appropriate evaluation function (e.g., evalPing for the PING command).
  2. Error Handling: If an evaluation function encounters an error (e.g., wrong number of arguments), evalAndRespond captures it.
  3. Response Encoding: It ensures that the final response, whether data or an error, is encoded into RESP format before being written back to the client.

Responding to Errors (respondError)

When an error occurs during command evaluation, the respondError function is invoked. Redis errors are encoded in RESP starting with a hyphen (-), followed by the error message, and terminated by \r\n.

Example: -ERR wrong number of arguments for 'ping' command\r\n

Our respondError function uses fmt.Sprintf to format the error string according to RESP error specification, converts it to bytes, and writes it to the TCP socket.

Implementing evalPing

The evalPing function is the core logic for handling the PING command. It takes the args slice from the RedisCMD object and the TCP connection.

func evalPing(args []string) ([]byte, error) {
    var response []byte
    
    // 1. Argument Validation
    if len(args) > 1 {
        return nil, fmt.Errorf("ERR wrong number of arguments for 'ping' command")
    }

    // 2. Determine Response Content
    var rawValue string
    var isSimpleString bool
    if len(args) == 0 {
        rawValue = "PONG"
        isSimpleString = true // PONG is a simple string
    } else { // len(args) == 1
        rawValue = args[0]
        isSimpleString = false // Argument is a bulk string
    }

    // 3. Encode Response using RESP
    response = encode(rawValue, isSimpleString)
    return response, nil
}

Key Logic within evalPing:

  • Argument Count Check: If len(args) is greater than 1, it immediately returns an error, matching Redis’ behavior.
  • No Arguments: If len(args) is 0, the response is the literal string `
Arpit Bhayani

Principal Engineer II at Razorpay - building Agent Studio, Ex-staff engg at GCP Memorystore & Dataproc, Creator of DiceDB, ex-Amazon Fast Data, ex-Director of Engg. SRE and Data Engineering at Unacademy. I spark engineering curiosity through my no-fluff engineering videos on YouTube and my courses