Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

// Duplicate variables - commenting out to avoid redeclaration
// See config.go for the actual declarations
//var Host string="0.0.0.0"
//var Port int=7379
var Host string="0.0.0.0"

Check failure on line 5 in config/main.go

View workflow job for this annotation

GitHub Actions / build

Host redeclared in this block
var Port int=7379

Check failure on line 6 in config/main.go

View workflow job for this annotation

GitHub Actions / build

Port redeclared in this block

//var KeysLimit int=100 //like ye hum threshold set krdiye hai like our database still support atmax this many keys

//will evict EvictionRation of keys whenever evictionruns
//it would dictate whenever my eviction is triggered, how many keys i will be evicting.
// var EvictionRatio float64=0.40 //Generally aise real scenario me kabhi itna to nahi hi krte hai but still
var EvictionRatio float64=0.40 //Generally aise real scenario me kabhi itna to nahi hi krte hai but still

Check failure on line 12 in config/main.go

View workflow job for this annotation

GitHub Actions / build

EvictionRatio redeclared in this block

// var EvictionStrategy string="allkeys-random"
var EvictionStrategy string="allkeys-random"

Check failure on line 14 in config/main.go

View workflow job for this annotation

GitHub Actions / build

EvictionStrategy redeclared in this block
var AOFFile string="./velox.aof"

45 changes: 41 additions & 4 deletions core/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
"io"
"strconv"
"time"
"strings"
)


var RESP_NIL []byte=[]byte("$-1\r\n")
var RESP_OK []byte=[]byte("+OK\r\n")
var RESP_ZERO []byte=[]byte(":0\r\n")
Expand Down Expand Up @@ -267,6 +269,41 @@ func evalINFO(args []string)[]byte{
return buf.Bytes() // Fixed: added missing return
}

func evalCLIENT(args []string) []byte {
if len(args) == 0 {
return Encode("CLIENT subcommands: LIST, SETNAME, GETNAME", true)
}
sub := strings.ToUpper(args[0])
switch sub {
case "LIST":
return Encode("id=1 addr=127.0.0.1:12345 fd=5 name=velox-cli age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping", false)
case "SETNAME":
if len(args) < 2 {
return Encode(errors.New("ERR wrong number of arguments for 'CLIENT SETNAME'"), false)
}
return RESP_OK
case "GETNAME":
return Encode("velox-client", false)
default:
return Encode(errors.New("ERR unknown subcommand for CLIENT"), false)
}
}

func evalLATENCY(args []string) []byte {
if len(args) == 0 {
return Encode("LATENCY subcommands: LATEST, RESET", true)
}
sub := strings.ToUpper(args[0])
switch sub {
case "LATEST":
return Encode([]string{"[0, 0, 0, 0, 0]"}, false)
case "RESET":
return RESP_OK
default:
return Encode(errors.New("ERR unknown subcommand for LATENCY"), false)
}
}

// func EvalAndRespond(cmd *Rediscmd,c net.Conn)error{
func EvalAndRespond(cmds []*RedisCmd, c io.ReadWriter) error{
//It's job is like depending on what job is sent to us
Expand Down Expand Up @@ -295,10 +332,10 @@ func EvalAndRespond(cmds []*RedisCmd, c io.ReadWriter) error{
buf.Write(evalINCR(cmd.Args))
case "INFO":
buf.Write(evalINFO(cmd.Args))
// case "CLIENT": // Commented out - evalCLIENT not implemented
// buf.Write(evalCLIENT(cmd.Args))
// case "LATENCY": // Commented out - evalLATENCY not implemented
// buf.Write(evalLATENCY(cmd.Args))
case "CLIENT":
buf.Write(evalCLIENT(cmd.Args))
case "LATENCY":
buf.Write(evalLATENCY(cmd.Args))
default:
buf.Write(evalPING(cmd.Args))
}
Expand Down
6 changes: 3 additions & 3 deletions core/eviction.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func populateEvictionPool(){

//TODO: no need to populate everytime, should populate
//only when the number of keys to evict is less than what we have in the pool
func evictAllKeysLRU(){ // Commented out - incomplete implementation
func evictAllKeysLRU(){
populateEvictionPool()
evictCount:=int16(config.EvictionRatio*float64(config.KeysLimit))
for i:=0;i<int(evictCount) && len(ePool.pool)>0 ;i++{
Expand Down Expand Up @@ -96,7 +96,7 @@ func evict(){
evictFirst() // Fixed function name case
case "allkeys-random": // Fixed spelling
evictAllKeysRandom() // Fixed function name case
// case "allkeys-lru": // Commented out - incomplete
// evictAllKeysLRU()
case "allkeys-lru": // Commented out - incomplete
evictAllKeysLRU()
}
}
4 changes: 3 additions & 1 deletion core/resp.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ func Encode(value interface{}, isSimple bool) []byte{
return []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(v), v))
case int, int8, int16, int32, int64:
return []byte(fmt.Sprintf(":%d\r\n", v))
case uint, uint8, uint16, uint32, uint64:
return []byte(fmt.Sprintf(":%d\r\n", v)) // Support unsigned ints for DEL and similar commands
case []string:
var b []byte
buf := bytes.NewBuffer(b)
Expand All @@ -237,4 +239,4 @@ func Encode(value interface{}, isSimple bool) []byte{
return RESP_NIL
// return []byte{}
}
}
}
35 changes: 18 additions & 17 deletions core/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,21 @@ func NewObj(value interface{},expDurationMs int64,oType uint8,oEnc uint8) *Obj{
//creating a new object, setting things up and returning another object
//since we want to store abolution expires instead of doing it multiple time that's why we have created this fucntion
// var expiresAt int64=-1
if expDurationMs>0{
/*

/*
when we say setExpiry?

it means we have to create an enrty of particular object that is expired dictionary
*/
setExpiry(obj,expDurationMs)
}

return &Obj{
Value: value,
TypeEncoding: oType|oEnc,
// ExpiresAt: expiresAt,
LastAccessedAt: getCurrentClock(),
}
obj:=&Obj{
Value:value,
TypeEncoding:oType|oEnc,
LastAccessedAt:getCurrentClock(),
}
if expDurationMs>0{
setExpiry(obj,expDurationMs)
}
return obj
}

func Put(k string, obj *Obj){
Expand Down Expand Up @@ -92,12 +92,13 @@ func Get(k string) *Obj{
}

func Del(k string ) bool{
if _,ok:=store[k];ok{
delete(store,k)
delete(expire,_)
KeyspaceStat[0]["keys"]--
return true
}
obj,ok:=store[k]
if ok {
delete(store, k)
delete(expires, obj)
KeyspaceStat[0]["keys"]--
return true
}
return false
}
/*
Expand Down
128 changes: 128 additions & 0 deletions integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"bufio"
"net"
"os/exec"
"strings"
"testing"
"time"
)

// Helper to start the server and return a cleanup function
func startServer(t *testing.T) func() {
cmd := exec.Command("go", "run", "main.go")
if err := cmd.Start(); err != nil {
t.Fatalf("Failed to start main.go: %v", err)
}
// Wait for server to start
ready := false
for i := 0; i < 20; i++ {
conn, err := net.DialTimeout("tcp", "127.0.0.1:7379", 200*time.Millisecond)
if err == nil {
conn.Close()
ready = true
break
}
time.Sleep(100 * time.Millisecond)
}
if !ready {
cmd.Process.Kill()
t.Fatalf("Server did not start in time")
}
return func() { cmd.Process.Kill() }
}

func TestServerPingPong(t *testing.T) {
cleanup := startServer(t)
defer cleanup()
conn, err := net.Dial("tcp", "127.0.0.1:7379")
if err != nil {
t.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
// Send PING
_, err = conn.Write([]byte("*1\r\n$4\r\nPING\r\n"))
if err != nil {
t.Fatalf("Failed to write to server: %v", err)
}
resp, _ := bufio.NewReader(conn).ReadString('\n')
if !strings.Contains(resp, "+PONG") {
t.Errorf("Expected +PONG, got: %q", resp)
}
}

func TestSetGetDel(t *testing.T) {
cleanup := startServer(t)
defer cleanup()
conn, err := net.Dial("tcp", "127.0.0.1:7379")
if err != nil {
t.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
reader := bufio.NewReader(conn)
// SET
_, err = conn.Write([]byte("*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"))
if err != nil {
t.Fatalf("Failed to write SET: %v", err)
}
resp, _ := reader.ReadString('\n')
if !strings.Contains(resp, "+OK") {
t.Errorf("Expected +OK for SET, got: %q", resp)
}
// GET
_, err = conn.Write([]byte("*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n"))
if err != nil {
t.Fatalf("Failed to write GET: %v", err)
}
resp, _ = reader.ReadString('\n')
if !strings.Contains(resp, "$3") && !strings.Contains(resp, "bar") {
t.Errorf("Expected $3...bar for GET, got: %q", resp)
}
// DEL
_, err = conn.Write([]byte("*2\r\n$3\r\nDEL\r\n$3\r\nfoo\r\n"))
if err != nil {
t.Fatalf("Failed to write DEL: %v", err)
}
resp, _ = reader.ReadString('\n')
if !strings.Contains(resp, ":1") {
t.Errorf("Expected :1 for DEL, got: %q", resp)
}
}

func TestExpireAndTTL(t *testing.T) {
cleanup := startServer(t)
defer cleanup()
conn, err := net.Dial("tcp", "127.0.0.1:7379")
if err != nil {
t.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
reader := bufio.NewReader(conn)
// SET with EX 1 (1 second)
_, err = conn.Write([]byte("*5\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$2\r\nEX\r\n$1\r\n1\r\n"))
if err != nil {
t.Fatalf("Failed to write SET with EX: %v", err)
}
reader.ReadString('\n') // +OK
// TTL should be 1 or 0
_, err = conn.Write([]byte("*2\r\n$3\r\nTTL\r\n$3\r\nfoo\r\n"))
if err != nil {
t.Fatalf("Failed to write TTL: %v", err)
}
resp, _ := reader.ReadString('\n')
if !strings.HasPrefix(resp, ":") {
t.Errorf("Expected integer for TTL, got: %q", resp)
}
// Wait for key to expire
time.Sleep(1200 * time.Millisecond)
// GET should return nil
_, err = conn.Write([]byte("*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n"))
if err != nil {
t.Fatalf("Failed to write GET after expire: %v", err)
}
resp, _ = reader.ReadString('\n')
if !strings.Contains(resp, "$-1") {
t.Errorf("Expected $-1 for expired key, got: %q", resp)
}
}
16 changes: 16 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"os/exec"
"testing"
)

func TestMainRuns(t *testing.T) {
cmd := exec.Command("go", "run", "main.go")
if err := cmd.Start(); err != nil {
t.Fatalf("Failed to start main.go: %v", err)
}
// Optionally, wait a short time and kill the process
// to avoid hanging the test suite
cmd.Process.Kill()
}
Loading