Skip to content

Commit 733259b

Browse files
committed
fixed EOF error, added INCR, DECR command
1 parent d988b93 commit 733259b

File tree

9 files changed

+219
-95
lines changed

9 files changed

+219
-95
lines changed

.github/workflows/release.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
goreleaser:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
- uses: actions/setup-go@v5
19+
with:
20+
go-version-file: "go.mod"
21+
cache: true
22+
- name: Run GoReleaser
23+
uses: goreleaser/goreleaser-action@v6
24+
with:
25+
args: release --clean
26+
env:
27+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.goreleaser.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
version: 2
2+
3+
before:
4+
hooks:
5+
- go mod tidy
6+
7+
builds:
8+
- env:
9+
- CGO_ENABLED=0
10+
mod_timestamp: '{{ .CommitTimestamp }}'
11+
flags:
12+
- -trimpath
13+
ldflags:
14+
- '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
15+
goos:
16+
- windows
17+
- linux
18+
- darwin
19+
goarch:
20+
- amd64
21+
- arm64
22+
binary: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
23+
24+
checksum:
25+
name_template: '{{ .ProjectName }}_SHA256SUMS'
26+
algorithm: sha256
27+
28+
changelog:
29+
disable: true

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ set name sojeb
3535
- HLEN
3636
- FLUSHALL
3737
- DEL
38-
- INFO
38+
- INFO
39+
- INCR
40+
- DECR

build.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ platforms=("windows/amd64" "windows/arm64" "linux/amd64" "linux/arm64" "darwin/a
55
for platform in "${platforms[@]}"; do
66
OS=${platform%/*}
77
ARCH=${platform#*/}
8-
OUTPUT="smem-${OS}-${ARCH}"
8+
OUTPUT="build/tunnel-${OS}-${ARCH}"
99

1010
# Append .exe for Windows
1111
if [ "$OS" == "windows" ]; then
1212
OUTPUT+=".exe"
1313
fi
1414

1515
echo "Building for $OS $ARCH..."
16-
GOOS=$OS GOARCH=$ARCH go build -o $OUTPUT
16+
GOOS=$OS GOARCH=$ARCH go build -o "$OUTPUT" .
1717
done
1818

19-
echo "Build completed!"
19+
echo "Build completed!"

aof.go renamed to internal/aof.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package internal
22

33
import (
44
"bufio"

handler.go renamed to internal/handler.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
package main
1+
package internal
22

33
import (
44
"fmt"
5+
"strconv"
56
"sync"
67
)
78

@@ -17,6 +18,8 @@ var Handlers = map[string]func([]Value) Value{
1718
"FLUSHALL": flushall,
1819
"DEL": del,
1920
"INFO": info,
21+
"INCR": incr,
22+
"DECR": decr,
2023
}
2124

2225
func ping(args []Value) Value {
@@ -222,3 +225,57 @@ func info(args []Value) Value {
222225

223226
return Value{typ: "bulk", bulk: infoStr}
224227
}
228+
229+
// incr command which increments the value of a key in the SET
230+
func incr(args []Value) Value {
231+
if len(args) != 1 {
232+
return Value{typ: "error", str: "ERR wrong number of arguments for 'incr' command"}
233+
}
234+
235+
key := args[0].bulk
236+
237+
SETsMu.Lock()
238+
value, ok := SETs[key]
239+
SETsMu.Unlock()
240+
241+
if !ok {
242+
SETs[key] = "1"
243+
return Value{typ: "integer", num: 1}
244+
}
245+
246+
num, err := strconv.Atoi(value)
247+
if err != nil {
248+
return Value{typ: "error", str: "ERR value is not an integer"}
249+
}
250+
251+
num++
252+
SETs[key] = strconv.Itoa(num)
253+
return Value{typ: "integer", num: num}
254+
}
255+
256+
// decr command which decrements the value of a key in the SET
257+
func decr(args []Value) Value {
258+
if len(args) != 1 {
259+
return Value{typ: "error", str: "ERR wrong number of arguments for 'decr' command"}
260+
}
261+
262+
key := args[0].bulk
263+
264+
SETsMu.Lock()
265+
value, ok := SETs[key]
266+
SETsMu.Unlock()
267+
268+
if !ok {
269+
SETs[key] = "0"
270+
return Value{typ: "integer", num: 0}
271+
}
272+
273+
num, err := strconv.Atoi(value)
274+
if err != nil {
275+
return Value{typ: "error", str: "ERR value is not an integer"}
276+
}
277+
278+
num--
279+
SETs[key] = strconv.Itoa(num)
280+
return Value{typ: "integer", num: num}
281+
}

resp.go renamed to internal/resp.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package internal
22

33
import (
44
"bufio"
@@ -195,7 +195,6 @@ func (v Value) marshalInteger() []byte {
195195
}
196196

197197
// Writer
198-
199198
type Writer struct {
200199
writer io.Writer
201200
}

internal/server.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package internal
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"net"
7+
"strings"
8+
)
9+
10+
var port = "6379"
11+
12+
func StartServer() {
13+
flag.StringVar(&port, "port", "6379", "port to listen on")
14+
flag.Parse()
15+
16+
fmt.Println("Listening on port :" + port)
17+
18+
// Create a new server
19+
listener, err := net.Listen("tcp", ":"+port)
20+
if err != nil {
21+
fmt.Println("Error listening:", err)
22+
return
23+
}
24+
defer listener.Close()
25+
26+
aof, err := NewAof("database.aof")
27+
if err != nil {
28+
fmt.Println("Error opening AOF:", err)
29+
return
30+
}
31+
defer aof.Close()
32+
33+
// Load data from AOF
34+
aof.Read(func(value Value) {
35+
command := strings.ToUpper(value.array[0].bulk)
36+
args := value.array[1:]
37+
38+
handler, ok := Handlers[command]
39+
if !ok {
40+
fmt.Println("Invalid command from AOF:", command)
41+
return
42+
}
43+
44+
handler(args)
45+
})
46+
47+
for {
48+
conn, err := listener.Accept()
49+
if err != nil {
50+
fmt.Println("Error accepting connection:", err)
51+
continue
52+
}
53+
54+
go handleConnection(conn, aof)
55+
}
56+
}
57+
58+
func handleConnection(conn net.Conn, aof *Aof) {
59+
defer conn.Close()
60+
61+
resp := NewResp(conn)
62+
writer := NewWriter(conn)
63+
64+
for {
65+
value, err := resp.Read()
66+
if err != nil {
67+
if err.Error() != "EOF" {
68+
fmt.Println("Error reading:", err)
69+
}
70+
return
71+
}
72+
73+
if value.typ != "array" || len(value.array) == 0 {
74+
writer.Write(Value{typ: "string", str: "Invalid request"})
75+
continue
76+
}
77+
78+
command := strings.ToUpper(value.array[0].bulk)
79+
args := value.array[1:]
80+
81+
handler, ok := Handlers[command]
82+
if !ok {
83+
writer.Write(Value{typ: "string", str: "Unknown command"})
84+
continue
85+
}
86+
87+
if command == "SET" || command == "HSET" || command == "DEL" || command == "FLUSHALL" ||
88+
command == "HDEL" || command == "INCR" || command == "DECR" {
89+
aof.Write(value)
90+
}
91+
92+
result := handler(args)
93+
writer.Write(result)
94+
}
95+
}

main.go

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,7 @@
11
package main
22

3-
import (
4-
"flag"
5-
"fmt"
6-
"net"
7-
"strings"
8-
)
9-
10-
var port = "6379"
3+
import "go-in-memory-database/internal"
114

125
func main() {
13-
flag.StringVar(&port, "port", "6379", "port to listen on")
14-
flag.Parse()
15-
16-
fmt.Println("Listening on port :" + port)
17-
18-
// Create a new server
19-
l, err := net.Listen("tcp", ":"+port)
20-
if err != nil {
21-
fmt.Println(err)
22-
return
23-
}
24-
25-
aof, err := NewAof("database.aof")
26-
if err != nil {
27-
fmt.Println(err)
28-
return
29-
}
30-
defer aof.Close()
31-
32-
aof.Read(func(value Value) {
33-
command := strings.ToUpper(value.array[0].bulk)
34-
args := value.array[1:]
35-
36-
handler, ok := Handlers[command]
37-
if !ok {
38-
fmt.Println("Invalid command: ", command)
39-
return
40-
}
41-
42-
handler(args)
43-
})
44-
45-
// Listen for connections
46-
conn, err := l.Accept()
47-
if err != nil {
48-
fmt.Println(err)
49-
return
50-
}
51-
52-
defer conn.Close()
53-
54-
for {
55-
resp := NewResp(conn)
56-
value, err := resp.Read()
57-
if err != nil {
58-
fmt.Println(err)
59-
return
60-
}
61-
62-
if value.typ != "array" {
63-
fmt.Println("Invalid request, expected array")
64-
continue
65-
}
66-
67-
if len(value.array) == 0 {
68-
fmt.Println("Invalid request, expected array length > 0")
69-
continue
70-
}
71-
72-
command := strings.ToUpper(value.array[0].bulk)
73-
args := value.array[1:]
74-
75-
writer := NewWriter(conn)
76-
77-
handler, ok := Handlers[command]
78-
if !ok {
79-
fmt.Println("Invalid command: ", command)
80-
writer.Write(Value{typ: "string", str: ""})
81-
continue
82-
}
83-
84-
// write to aof file if the command does any changes to the database
85-
if command == "SET" || command == "HSET" || command == "DEL" || command == "FLUSHALL" || command == "HDEL" {
86-
aof.Write(value)
87-
}
88-
89-
result := handler(args)
90-
writer.Write(result)
91-
}
6+
internal.StartServer()
927
}

0 commit comments

Comments
 (0)