From 58710c23b2389f42e46bd84f5012e922b7bbc7a3 Mon Sep 17 00:00:00 2001 From: Timmelmann <41704604+Timmelmann@users.noreply.github.com> Date: Sat, 8 Aug 2026 14:26:06 +0200 Subject: [PATCH] moved the kafka-go project to this repository --- README.md | 34 +++++++++++++++++++++++++ app/protocol/request/body.go | 35 ++++++++++++++++++++++++++ app/protocol/request/header.go | 19 ++++++++++++++ app/protocol/response/message.go | 7 ++++++ app/protocol/response/v0_header.go | 19 ++++++++++++++ app/protocol/response/v0_response.go | 28 +++++++++++++++++++++ app/server.go | 37 ++++++++++++++++++++++++++++ codecrafters.yml | 11 +++++++++ go.mod | 11 +++++++++ go.sum | 0 your_program.sh | 24 ++++++++++++++++++ 11 files changed, 225 insertions(+) create mode 100644 README.md create mode 100644 app/protocol/request/body.go create mode 100644 app/protocol/request/header.go create mode 100644 app/protocol/response/message.go create mode 100644 app/protocol/response/v0_header.go create mode 100644 app/protocol/response/v0_response.go create mode 100644 app/server.go create mode 100644 codecrafters.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100755 your_program.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9a521b --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +[![progress-banner](https://backend.codecrafters.io/progress/kafka/4eddd9dd-43a3-4d8f-bae7-2f6d7d8ec6bc)](https://app.codecrafters.io/users/Timmelmann?r=2qF) + +This is a starting point for Go solutions to the +["Build Your Own Kafka" Challenge](https://codecrafters.io/challenges/kafka). + +In this challenge, you'll build a toy Kafka clone that's capable of accepting +and responding to APIVersions & Fetch API requests. You'll also learn about +encoding and decoding messages using the Kafka wire protocol. You'll also learn +about handling the network protocol, event loops, TCP sockets and more. + +**Note**: If you're viewing this repo on GitHub, head over to +[codecrafters.io](https://codecrafters.io) to try the challenge. + +# Passing the first stage + +The entry point for your Kafka implementation is in `app/server.go`. Study and +uncomment the relevant code, and push your changes to pass the first stage: + +```sh +git commit -am "pass 1st stage" # any msg +git push origin master +``` + +That's all! + +# Stage 2 & beyond + +Note: This section is for stages 2 and beyond. + +1. Ensure you have `go (1.19)` installed locally +1. Run `./your_program.sh` to run your Kafka broker, which is implemented in + `app/server.go`. +1. Commit your changes and run `git push origin master` to submit your solution + to CodeCrafters. Test output will be streamed to your terminal. diff --git a/app/protocol/request/body.go b/app/protocol/request/body.go new file mode 100644 index 0000000..0636104 --- /dev/null +++ b/app/protocol/request/body.go @@ -0,0 +1,35 @@ +package protocol + +import "encoding/binary" + +type Request struct { + bytes []byte + byteParseOffset uint + + Length int32 + Header *RequestHeader +} + +func NewRequest(bytes []byte) *Request { + req := Request{ + bytes: bytes, + byteParseOffset: 0, + } + + req.Length = req.ReadInt32() + req.Header = ParseRequestHeader(&req) + + return &req +} + +func (r *Request) ReadInt16() int16{ + val := binary.BigEndian.Uint16(r.bytes[r.byteParseOffset:]) + r.byteParseOffset += 2 + return int16(val) +} + +func (r *Request) ReadInt32() int32 { + val := binary.BigEndian.Uint32(r.bytes[r.byteParseOffset:]) + r.byteParseOffset += 4 + return int32(val) +} diff --git a/app/protocol/request/header.go b/app/protocol/request/header.go new file mode 100644 index 0000000..cf0f47b --- /dev/null +++ b/app/protocol/request/header.go @@ -0,0 +1,19 @@ +package protocol + +type RequestHeader struct { + APIKey int16 + APIVersion int16 + Correlation_id int32 +} + +func ParseRequestHeader(r *Request) *RequestHeader { + apiKey := r.ReadInt16() + apiVersion := r.ReadInt16() + correlation_id := r.ReadInt32() + + return &RequestHeader{ + APIKey: apiKey, + APIVersion: apiVersion, + Correlation_id: correlation_id, + } +} \ No newline at end of file diff --git a/app/protocol/response/message.go b/app/protocol/response/message.go new file mode 100644 index 0000000..587636a --- /dev/null +++ b/app/protocol/response/message.go @@ -0,0 +1,7 @@ +package response + +type Message struct { + body []byte + header V0Header + messageSize int32 +} diff --git a/app/protocol/response/v0_header.go b/app/protocol/response/v0_header.go new file mode 100644 index 0000000..4b66bb6 --- /dev/null +++ b/app/protocol/response/v0_header.go @@ -0,0 +1,19 @@ +package response + +import "encoding/binary" + +type V0Header struct { + Correlation_id uint32 +} + +func NewHeader(correlation_id uint32) *V0Header { + return &V0Header{ + Correlation_id: correlation_id, + } +} + +func (h *V0Header) ToBytes() []byte { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(h.Correlation_id)) + return buf +} diff --git a/app/protocol/response/v0_response.go b/app/protocol/response/v0_response.go new file mode 100644 index 0000000..17fbbbf --- /dev/null +++ b/app/protocol/response/v0_response.go @@ -0,0 +1,28 @@ +package response + +import ( + "encoding/binary" +) + +type V0Response struct { + header V0Header + body []byte +} + +func NewV0Reponse(correlation_id uint32) *V0Response { + return &V0Response{ + header: V0Header{ + Correlation_id: correlation_id}, + body: []byte{}, + } +} + +func (r *V0Response) ToBytes() []byte { + header := r.header.ToBytes() + length := len(header) + buf := make([]byte, length+4) + binary.BigEndian.PutUint32(buf[0:4], uint32(length)) + copy(buf[4:], header) + + return buf +} diff --git a/app/server.go b/app/server.go new file mode 100644 index 0000000..4a15068 --- /dev/null +++ b/app/server.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "net" + "os" + + protocol "github.com/codecrafters-io/redis-starter-go/app/protocol/response" +) + +// Ensures gofmt doesn't remove the "net" and "os" imports in stage 1 (feel free to remove this!) +var _ = net.Listen +var _ = os.Exit + +func main() { + + l, err := net.Listen("tcp", "0.0.0.0:9092") + if err != nil { + fmt.Println("Failed to bind to port 9092: ", err.Error()) + os.Exit(1) + } + conn, err := l.Accept() + if err != nil { + fmt.Println("Could not accept connection: ", err.Error()) + os.Exit(1) + } + defer conn.Close() + + if _, err = conn.Read(make([]byte, 1024)); err != nil { + fmt.Println("Failed to read request: ", err.Error()) + } + + response := protocol.NewV0Reponse(7) + if _, err = conn.Write(response.ToBytes()); err != nil { + fmt.Println("Failed to write response: ", err.Error()) + } +} diff --git a/codecrafters.yml b/codecrafters.yml new file mode 100644 index 0000000..77886cf --- /dev/null +++ b/codecrafters.yml @@ -0,0 +1,11 @@ +# Set this to true if you want debug logs. +# +# These can be VERY verbose, so we suggest turning them off +# unless you really need them. +debug: false + +# Use this to change the Go version used to run your code +# on Codecrafters. +# +# Available versions: go-1.22 +language_pack: go-1.22 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a8e2d2a --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +// DON'T EDIT THIS! +// +// Codecrafters relies on this file being intact to run tests successfully. Any changes +// here will not reflect when CodeCrafters tests your code, and might even cause build +// failures. +// +// DON'T EDIT THIS! + +module github.com/codecrafters-io/redis-starter-go + +go 1.22 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/your_program.sh b/your_program.sh new file mode 100755 index 0000000..8b5fdeb --- /dev/null +++ b/your_program.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Use this script to run your program LOCALLY. +# +# Note: Changing this script WILL NOT affect how CodeCrafters runs your program. +# +# Learn more: https://codecrafters.io/program-interface + +set -e # Exit early if any commands fail + +# Copied from .codecrafters/compile.sh +# +# - Edit this to change how your program compiles locally +# - Edit .codecrafters/compile.sh to change how your program compiles remotely +( + cd "$(dirname "$0")" # Ensure compile steps are run within the repository directory + go build -o /tmp/codecrafters-build-redis-go app/*.go +) + +# Copied from .codecrafters/run.sh +# +# - Edit this to change how your program runs locally +# - Edit .codecrafters/run.sh to change how your program runs remotely +exec /tmp/codecrafters-build-redis-go "$@"