Go Redis Replication #NA2

I’m stuck on Replication stage 18.

I don’t know where it went wrong but it is giving me received “” in replica when we are not supposed to send back anything.

Here are my logs:

[replication-18] Running tests for Replication > Stage #18: WAIT with multiple commands
[replication-18] $ ./spawn_redis_server.sh --port 6379
[replication-18] Proceeding to create 4 replicas.
[replication-18] Creating replica: 1
[replication-18] replica-1: $ redis-cli PING
[replication-18] replica-1: Sent bytes: "*1\r\n$4\r\nPING\r\n"
[replication-18] replica-1: Received bytes: "+PONG\r\n"
[replication-18] replica-1: Received RESP value: "PONG"
[replication-18] Received "PONG"
[replication-18] replica-1: $ redis-cli REPLCONF listening-port 6380
[replication-18] replica-1: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$14\r\nlistening-port\r\n$4\r\n6380\r\n"
[replication-18] replica-1: Received bytes: "+OK\r\n"
[replication-18] replica-1: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-1: $ redis-cli REPLCONF capa psync2
[replication-18] replica-1: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$4\r\ncapa\r\n$6\r\npsync2\r\n"
[replication-18] replica-1: Received bytes: "+OK\r\n"
[replication-18] replica-1: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-1: $ redis-cli PSYNC ? -1
[replication-18] replica-1: Sent bytes: "*3\r\n$5\r\nPSYNC\r\n$1\r\n?\r\n$2\r\n-1\r\n"
[replication-18] replica-1: Received bytes: "++FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0\r\n"
[replication-18] replica-1: Received RESP value: "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Received "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Reading RDB file...
[replication-18] replica-1: Received bytes: "$88\r\nREDIS0011\xfa\tredis-ver\x057.2.0\xfa\nredis-bits\xc0@\xfa\x05ctime\xc2m\b\xbce\xfa\bused-mem°\xc4\x10\x00\xfa\baof-base\xc0\x00\xff\xf0n;\xfe\xc0\xffZ\xa2"
[replication-18] Received RDB file
[replication-18] Creating replica: 2
[replication-18] replica-2: $ redis-cli PING
[replication-18] replica-2: Sent bytes: "*1\r\n$4\r\nPING\r\n"
[replication-18] replica-2: Received bytes: "+PONG\r\n"
[replication-18] replica-2: Received RESP value: "PONG"
[replication-18] Received "PONG"
[replication-18] replica-2: $ redis-cli REPLCONF listening-port 6381
[replication-18] replica-2: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$14\r\nlistening-port\r\n$4\r\n6381\r\n"
[replication-18] replica-2: Received bytes: "+OK\r\n"
[replication-18] replica-2: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-2: $ redis-cli REPLCONF capa psync2
[replication-18] replica-2: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$4\r\ncapa\r\n$6\r\npsync2\r\n"
[replication-18] replica-2: Received bytes: "+OK\r\n"
[replication-18] replica-2: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-2: $ redis-cli PSYNC ? -1
[replication-18] replica-2: Sent bytes: "*3\r\n$5\r\nPSYNC\r\n$1\r\n?\r\n$2\r\n-1\r\n"
[replication-18] replica-2: Received bytes: "++FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0\r\n"
[replication-18] replica-2: Received RESP value: "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Received "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Reading RDB file...
[replication-18] replica-2: Received bytes: "$88\r\nREDIS0011\xfa\tredis-ver\x057.2.0\xfa\nredis-bits\xc0@\xfa\x05ctime\xc2m\b\xbce\xfa\bused-mem°\xc4\x10\x00\xfa\baof-base\xc0\x00\xff\xf0n;\xfe\xc0\xffZ\xa2"
[replication-18] Received RDB file
[replication-18] Creating replica: 3
[replication-18] replica-3: $ redis-cli PING
[replication-18] replica-3: Sent bytes: "*1\r\n$4\r\nPING\r\n"
[replication-18] replica-3: Received bytes: "+PONG\r\n"
[replication-18] replica-3: Received RESP value: "PONG"
[replication-18] Received "PONG"
[replication-18] replica-3: $ redis-cli REPLCONF listening-port 6382
[replication-18] replica-3: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$14\r\nlistening-port\r\n$4\r\n6382\r\n"
[replication-18] replica-3: Received bytes: "+OK\r\n"
[replication-18] replica-3: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-3: $ redis-cli REPLCONF capa psync2
[replication-18] replica-3: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$4\r\ncapa\r\n$6\r\npsync2\r\n"
[replication-18] replica-3: Received bytes: "+OK\r\n"
[replication-18] replica-3: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-3: $ redis-cli PSYNC ? -1
[replication-18] replica-3: Sent bytes: "*3\r\n$5\r\nPSYNC\r\n$1\r\n?\r\n$2\r\n-1\r\n"
[replication-18] replica-3: Received bytes: "++FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0\r\n"
[replication-18] replica-3: Received RESP value: "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Received "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Reading RDB file...
[replication-18] replica-3: Received bytes: "$88\r\nREDIS0011\xfa\tredis-ver\x057.2.0\xfa\nredis-bits\xc0@\xfa\x05ctime\xc2m\b\xbce\xfa\bused-mem°\xc4\x10\x00\xfa\baof-base\xc0\x00\xff\xf0n;\xfe\xc0\xffZ\xa2"
[replication-18] Received RDB file
[replication-18] Creating replica: 4
[replication-18] replica-4: $ redis-cli PING
[replication-18] replica-4: Sent bytes: "*1\r\n$4\r\nPING\r\n"
[replication-18] replica-4: Received bytes: "+PONG\r\n"
[replication-18] replica-4: Received RESP value: "PONG"
[replication-18] Received "PONG"
[replication-18] replica-4: $ redis-cli REPLCONF listening-port 6383
[replication-18] replica-4: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$14\r\nlistening-port\r\n$4\r\n6383\r\n"
[replication-18] replica-4: Received bytes: "+OK\r\n"
[replication-18] replica-4: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-4: $ redis-cli REPLCONF capa psync2
[replication-18] replica-4: Sent bytes: "*3\r\n$8\r\nREPLCONF\r\n$4\r\ncapa\r\n$6\r\npsync2\r\n"
[replication-18] replica-4: Received bytes: "+OK\r\n"
[replication-18] replica-4: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] replica-4: $ redis-cli PSYNC ? -1
[replication-18] replica-4: Sent bytes: "*3\r\n$5\r\nPSYNC\r\n$1\r\n?\r\n$2\r\n-1\r\n"
[replication-18] replica-4: Received bytes: "++FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0\r\n"
[replication-18] replica-4: Received RESP value: "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Received "+FULLRESYNC 8371b4fb1155b71f4a04d3e1bc3e18c4a990aeeb 0"
[replication-18] Reading RDB file...
[replication-18] replica-4: Received bytes: "$88\r\nREDIS0011\xfa\tredis-ver\x057.2.0\xfa\nredis-bits\xc0@\xfa\x05ctime\xc2m\b\xbce\xfa\bused-mem°\xc4\x10\x00\xfa\baof-base\xc0\x00\xff\xf0n;\xfe\xc0\xffZ\xa2"
[replication-18] Received RDB file
[replication-18] client: $ redis-cli SET foo 123
[replication-18] client: Sent bytes: "*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\n123\r\n"
[replication-18] client: Received bytes: "+OK\r\n"
[replication-18] client: Received RESP value: "OK"
[replication-18] Received "OK"
[replication-18] client: $ redis-cli WAIT 1 500
[replication-18] client: Sent bytes: "*3\r\n$4\r\nWAIT\r\n$1\r\n1\r\n$3\r\n500\r\n"
[replication-18] Testing Replica : 1
[replication-18] replica-1: Received bytes: "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\n123\r\n"
[replication-18] replica-1: Received RESP value: ["set", "foo", "123"]
[replication-18] Received ["set", "foo", "123"]
[replication-18] Received: "" (no content received)
[replication-18]            ^ error
[replication-18] Error: Expected start of a new RESP value (either +, -, :, $ or *)
[replication-18] Test failed
[replication-18] Terminating program
[your_program] Error reading: read tcp [::1]:6379->[::1]:53174: read: connection reset by peer
[your_program] Connection closed
[your_program] Error reading: read tcp [::1]:6379->[::1]:53158: read: connection reset by peer
[your_program] Error reading: read tcp [::1]:6379->[::1]:53166: read: connection reset by peer
[your_program] Error reading: read tcp [::1]:6379->[::1]:53172: read: connection reset by peer
[replication-18] Program terminated successfully

And here’s a snippet of my code:

package main

import (
	"fmt"
	"io"
	"net"

	"github.com/codecrafters-io/redis-starter-go/internal/command"
	"github.com/codecrafters-io/redis-starter-go/internal/redis"
	"github.com/codecrafters-io/redis-starter-go/internal/resp"
	"github.com/codecrafters-io/redis-starter-go/internal/util"
)

func main() {
	// You can use print statements as follows for debugging, they'll be visible when running tests.

	// Uncomment this block to pass the first stage
	// port := flag.String("port", "6379", "Port to bind to")
	redis := redis.NewNode()
	for {
		if redis.IsSlave() {
			go handleMasterConnection(redis)
		}
		conn := redis.Accept()
		go handleConnection(redis, conn)
	}
}

func handleConnection(redis redis.Node, conn net.Conn) {
	// Implement the Redis protocol here
	defer conn.Close()
	for {
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err == io.EOF {
			fmt.Println("Connection closed")
			return
		}
		if err != nil {
			fmt.Println("Error reading:", err.Error())
			return
		}
		inputArr := resp.NewResp(buf[:n])
		for _, input := range inputArr {
			cmd, err := command.NewCommand(input)
			if err != nil {
				fmt.Println(err.Error())
				return
			}
			util.Execute(redis, conn, *cmd)
		}
	}
}

func handleMasterConnection(redis redis.Node) {
	defer redis.GetMasterConn().GetConn().Close()
	defer redis.RemoveSlaveConn(redis.GetMasterConn().GetConn())
	repBuf := make([]byte, 1024)
	for {
		n, err := redis.GetMasterConn().Read(repBuf)
		if err == io.EOF {
			fmt.Println("Connection closed")
			return
		}
		if err != nil {
			fmt.Println("Error reading from master:", err.Error())
			return
		}
		inputArr := resp.NewResp(repBuf[:n])
		for _, input := range inputArr {
			cmd, err := command.NewCommand(input)
			if err != nil {
				fmt.Println(err.Error())
				return
			}
			util.ExecuteReplica(redis, *cmd)
		}
	}
}

We’re going to improve our logs here to convey “what” we’re waiting for - in this case it isn’t a response to SET, I think we’re waiting on the response to WAIT.

Will keep this open until that’s done!

1 Like

Note: I’ve updated the title of this post to include the stage ID (#NA2). You can learn about the stages rename here: Upcoming change: Stages overhaul.

Just recapping what needs to be done here: Whenever we emit a RESP syntax error like “Error: Expected start of a new RESP value”, we should also include info on what was expected (“+OK\r\n” for example).