Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expiration #59

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ This is a simple in-memory key-value store written in Go. All the commands are r
- [LSET](https://redis.io/docs/latest/commands/lset/)
- [DBSIZE](https://redis.io/docs/latest/commands/dbsize/)
- [MGET](https://redis.io/docs/latest/commands/mget/)
- [TTL](https://redis.io/docs/latest/commands/ttl/)
- [PTTL](https://redis.io/docs/latest/commands/pttl/)
- [EXPIRE](https://redis.io/docs/latest/commands/expire/)

# Benchmark
```
Expand Down
89 changes: 79 additions & 10 deletions handlers/general.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package handlers

import (
"container/list"
"fmt"
"strconv"
"time"

"github.com/idugan100/GoKV/resp"
)
Expand All @@ -15,26 +18,92 @@ func lolwut(args []resp.Serializable) resp.Serializable {
}

func flushall(args []resp.Serializable) resp.Serializable {
setMU.Lock()
setData = map[string]string{}
setMU.Unlock()
hsetMU.Lock()
stringMU.Lock()
stringData = map[string]stringDataItem{}
stringMU.Unlock()
hstringMU.Lock()
hsetData = map[string]map[string]string{}
hsetMU.Unlock()
hstringMU.Unlock()
listMU.Lock()
listData = map[string]*list.List{}
listMU.Unlock()
return resp.Serializable{Typ: "string", Str: "OK"}
}

func dbsize(args []resp.Serializable) resp.Serializable {
setMU.RLock()
defer setMU.RUnlock()
stringMU.RLock()
defer stringMU.RUnlock()
listMU.RLock()
defer listMU.RUnlock()
hsetMU.RLock()
defer hsetMU.RUnlock()
total_keys := len(setData) + len(listData) + len(hsetData)
hstringMU.RLock()
defer hstringMU.RUnlock()
total_keys := len(stringData) + len(listData) + len(hsetData)

return resp.Serializable{Typ: "integer", Num: total_keys}
}

func ttl(args []resp.Serializable) resp.Serializable {
if len(args) != 1 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "TTL"}.Error()}
}
fmt.Println("here 1")
_, ok := getString(args[0].Bulk)
fmt.Println("here 2")

if !ok {
return resp.Serializable{Typ: "integer", Num: -2}
}
fmt.Println("here 3")

stringMU.RLock()
defer stringMU.RUnlock()
fmt.Println("here 4")

val, ok := stringData[args[0].Bulk]
fmt.Println("here 5")

if !ok || !val.will_expire {
return resp.Serializable{Typ: "integer", Num: -1}
}
fmt.Println("here 6")

return resp.Serializable{Typ: "integer", Num: int(time.Until(val.expiration).Seconds())}
}

func pttl(args []resp.Serializable) resp.Serializable {
if len(args) != 1 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "PTTL"}.Error()}
}
_, ok := getString(args[0].Bulk)
if !ok {
return resp.Serializable{Typ: "integer", Num: -2}
}
stringMU.RLock()
defer stringMU.RUnlock()

val, ok := stringData[args[0].Bulk]
if !ok || !val.will_expire {
return resp.Serializable{Typ: "integer", Num: -1}
}

return resp.Serializable{Typ: "integer", Num: int(time.Until(val.expiration).Milliseconds())}
}

func expire(args []resp.Serializable) resp.Serializable {
if len(args) != 2 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "EXPIRE"}.Error()}
}
str, ok := getString(args[0].Bulk)
if !ok {
return resp.Serializable{Typ: "integer", Num: 0}
}
expr_seconds, err := strconv.Atoi(args[1].Bulk)
if err != nil {
return resp.Serializable{Typ: "error", Str: InvalidDataTypeError{Command: "EXPIRE"}.Error()}
}
stringMU.Lock()
stringData[args[0].Bulk] = stringDataItem{str: str, will_expire: true, expiration: time.Now().Add(time.Second * time.Duration(expr_seconds))}
stringMU.Unlock()
return resp.Serializable{Typ: "integer", Num: 1}

}
44 changes: 40 additions & 4 deletions handlers/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,47 @@ import (
"container/list"
"fmt"
"sync"
"time"

"github.com/idugan100/GoKV/resp"
)

var setData = map[string]string{}
var setMU = sync.RWMutex{}
type stringDataItem struct {
str string
expiration time.Time
will_expire bool
}

var stringData = map[string]stringDataItem{}
var stringMU = sync.RWMutex{}

func getString(key string) (string, bool) {
// get item
stringMU.RLock()
val, ok := stringData[key]
stringMU.RUnlock()

// if item is not found
if !ok {
return "", false
}

is_expired := val.will_expire && (time.Now().Compare(val.expiration) != -1)

// if item is expired
if is_expired {
stringMU.Lock()
delete(stringData, key)
stringMU.Unlock()
return "", false
}

// if item is found and not expired
return val.str, true
}

var hsetData = map[string]map[string]string{}
var hsetMU = sync.RWMutex{}
var hstringMU = sync.RWMutex{}
var listData = map[string]*list.List{}
var listMU = sync.RWMutex{}
var Handlers = map[string]func([]resp.Serializable) resp.Serializable{
Expand Down Expand Up @@ -52,6 +85,9 @@ var Handlers = map[string]func([]resp.Serializable) resp.Serializable{
"LREM": lrem,
"DBSIZE": dbsize,
"MGET": mget,
"TTL": ttl,
"PTTL": pttl,
"EXPIRE": expire,
}

type InvalidArgsNumberError struct {
Expand All @@ -71,7 +107,7 @@ func (i InvalidDataTypeError) Error() string {
}

func ClearData() {
setData = map[string]string{}
stringData = map[string]stringDataItem{}
hsetData = map[string]map[string]string{}
listData = map[string]*list.List{}

Expand Down
32 changes: 16 additions & 16 deletions handlers/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ func hset(args []resp.Serializable) resp.Serializable {
if len(args)%2 != 1 || len(args) < 3 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HSET"}.Error()}
}
hsetMU.Lock()
hstringMU.Lock()
hsetData[args[0].Bulk] = map[string]string{}
for i := 1; i < len(args); i += 2 {
hsetData[args[0].Bulk][args[i].Bulk] = args[i+1].Bulk
}
hsetMU.Unlock()
hstringMU.Unlock()

return resp.Serializable{Typ: "integer", Num: (len(args) - 1) / 2}
}
Expand All @@ -22,8 +22,8 @@ func hget(args []resp.Serializable) resp.Serializable {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HGET"}.Error()}
}

hsetMU.RLock()
defer hsetMU.RUnlock()
hstringMU.RLock()
defer hstringMU.RUnlock()

_, ok := hsetData[args[0].Bulk]

Expand All @@ -49,10 +49,10 @@ func hexists(args []resp.Serializable) resp.Serializable {
if len(args) != 2 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HEXISTS"}.Error()}
}
hsetMU.RLock()
hstringMU.RLock()
_, okKey := hsetData[args[0].Bulk]
_, okValue := hsetData[args[0].Bulk][args[1].Bulk]
hsetMU.RUnlock()
hstringMU.RUnlock()

if !okKey || !okValue {
return resp.Serializable{Typ: "integer", Num: 0}
Expand All @@ -65,9 +65,9 @@ func hstrlen(args []resp.Serializable) resp.Serializable {
if len(args) != 2 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HSTRLEN"}.Error()}
}
hsetMU.RLock()
hstringMU.RLock()
val, ok := hsetData[args[0].Bulk][args[1].Bulk]
hsetMU.RUnlock()
hstringMU.RUnlock()
if !ok {
return resp.Serializable{Typ: "integer", Num: 0}
}
Expand All @@ -79,9 +79,9 @@ func hlen(args []resp.Serializable) resp.Serializable {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HLEN"}.Error()}
}

hsetMU.RLock()
hstringMU.RLock()
val, ok := hsetData[args[0].Bulk]
hsetMU.RUnlock()
hstringMU.RUnlock()

if !ok {
return resp.Serializable{Typ: "integer", Num: 0}
Expand All @@ -94,9 +94,9 @@ func hgetall(args []resp.Serializable) resp.Serializable {
if len(args) != 1 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HGETALL"}.Error()}
}
hsetMU.RLock()
hstringMU.RLock()
val, ok := hsetData[args[0].Bulk]
hsetMU.RUnlock()
hstringMU.RUnlock()
if !ok {
return resp.Serializable{Typ: "array"}
}
Expand All @@ -112,8 +112,8 @@ func hsetnx(args []resp.Serializable) resp.Serializable {
if len(args) != 3 {
return resp.Serializable{Typ: "error", Str: InvalidArgsNumberError{Command: "HSETNX"}.Error()}
}
hsetMU.Lock()
defer hsetMU.Unlock()
hstringMU.Lock()
defer hstringMU.Unlock()

_, ok := hsetData[args[0].Bulk][args[1].Bulk]

Expand All @@ -133,8 +133,8 @@ func hdel(args []resp.Serializable) resp.Serializable {
numFields := len(args) - 1
numDeleted := 0

hsetMU.Lock()
defer hsetMU.Unlock()
hstringMU.Lock()
defer hstringMU.Unlock()
for i := range numFields {
if _, ok := hsetData[args[0].Bulk][args[i+1].Bulk]; ok {
delete(hsetData[args[0].Bulk], args[i+1].Bulk)
Expand Down
Loading
Loading