Hone logo
Hone
Problems

Implementing a Basic Redis Client in Go

This challenge asks you to build a simplified Redis client in Go. Understanding how to interact with distributed data stores like Redis is a fundamental skill for modern backend development. This exercise will help you grasp the concepts of network communication, command serialization, and response parsing.

Problem Description

Your task is to implement a Go client that can connect to a Redis server and execute a limited set of Redis commands: GET, SET, and PING. The client should handle basic connection establishment, command formatting according to the RESP (REdis Serialization Protocol), and parsing of responses.

Key Requirements:

  1. Connection: The client must be able to establish a TCP connection to a specified Redis server address (host:port).
  2. PING Command: Implement a Ping() method that sends the PING command to the server and returns the server's PONG response.
  3. GET Command: Implement a Get(key string) method that sends the GET <key> command to the server and returns the associated value. If the key does not exist, it should return an empty string and indicate that the key was not found.
  4. SET Command: Implement a Set(key, value string) method that sends the SET <key> <value> command to the server. It should return a success indicator.
  5. RESP Protocol: Adhere to the RESP protocol for sending commands and parsing responses. Specifically, handle bulk strings, simple strings, and nulls.
  6. Error Handling: The client should gracefully handle network errors, connection issues, and invalid Redis responses.

Expected Behavior:

  • When Ping() is called, the client sends PING\r\n and expects PONG\r\n.
  • When Get(key) is called for a non-existent key, the client should return an empty string and a specific error or boolean indicating "not found".
  • When Set(key, value) is called, the client should receive an OK response upon successful execution.
  • The client should manage the underlying network connection. A Close() method should be provided to properly shut down the connection.

Edge Cases:

  • What happens if the Redis server is not running?
  • What happens if a key contains special characters or is empty?
  • How to handle large values in SET and GET operations? (For this challenge, assume reasonable sizes, but be aware of the protocol for bulk strings).
  • Malformed Redis responses.

Examples

Example 1: Basic Ping and Set/Get

Let's assume a Redis server is running on localhost:6379.

// Assume a Redis server is running on localhost:6379

// Initialize the client
client, err := NewRedisClient("localhost:6379")
if err != nil {
    // Handle error
}
defer client.Close()

// Ping the server
response, err := client.Ping()
// Expected: response is "PONG", err is nil

// Set a key-value pair
success, err := client.Set("mykey", "myvalue")
// Expected: success is true, err is nil

// Get the value
value, err := client.Get("mykey")
// Expected: value is "myvalue", err is nil

Example 2: Getting a Non-existent Key

// Continue from Example 1

// Get a non-existent key
value, err := client.Get("nonexistentkey")
// Expected: value is "", err indicates "key not found" (e.g., a specific error type or a boolean flag)

Example 3: Handling Empty Values

// Assume a Redis server is running on localhost:6379

client, err := NewRedisClient("localhost:6379")
if err != nil {
    // Handle error
}
defer client.Close()

// Set an empty value
success, err := client.Set("emptykey", "")
// Expected: success is true, err is nil

// Get the empty value
value, err := client.Get("emptykey")
// Expected: value is "", err is nil

Constraints

  • The client should connect using standard TCP on IPv4.
  • The client should implement only the PING, GET, and SET commands.
  • Focus on the core logic of the RESP protocol for these commands. Do not implement advanced features like Pub/Sub, transactions, or Lua scripting.
  • Responses are expected to be relatively small for this exercise. Performance optimization for extremely large data is not the primary goal.
  • The NewRedisClient function should accept the server address as a string.

Notes

  • The Redis RESP protocol specifies how commands and data are formatted. For example, simple strings end with \r\n, bulk strings start with $<length>\r\n, and errors start with -.
  • You will need to carefully parse the incoming data stream from the Redis server to correctly interpret responses.
  • Consider using Go's net package for TCP connections.
  • Think about how to represent different Redis data types (simple strings, bulk strings, errors, nulls) in Go.
  • A good way to handle the "key not found" scenario for GET is to return a specific error or a second boolean return value.
Loading editor...
go