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:
- Connection: The client must be able to establish a TCP connection to a specified Redis server address (host:port).
PINGCommand: Implement aPing()method that sends thePINGcommand to the server and returns the server'sPONGresponse.GETCommand: Implement aGet(key string)method that sends theGET <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.SETCommand: Implement aSet(key, value string)method that sends theSET <key> <value>command to the server. It should return a success indicator.- RESP Protocol: Adhere to the RESP protocol for sending commands and parsing responses. Specifically, handle bulk strings, simple strings, and nulls.
- Error Handling: The client should gracefully handle network errors, connection issues, and invalid Redis responses.
Expected Behavior:
- When
Ping()is called, the client sendsPING\r\nand expectsPONG\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 anOKresponse 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
SETandGEToperations? (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, andSETcommands. - 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
NewRedisClientfunction 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
netpackage 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
GETis to return a specific error or a second boolean return value.