diff options
| -rw-r--r-- | handlers.go | 97 | ||||
| -rw-r--r-- | main.go | 17 | ||||
| -rw-r--r-- | redisDB.go | 8 |
3 files changed, 91 insertions, 31 deletions
diff --git a/handlers.go b/handlers.go index 885e348..244d332 100644 --- a/handlers.go +++ b/handlers.go @@ -4,6 +4,7 @@ import ( "errors" "net" "strconv" + "time" ) func handleGet(conn net.Conn, args []string) { @@ -18,13 +19,19 @@ func handleGet(conn net.Conn, args []string) { key := args[1] - value, exists := db.data[key] + item, exists := db.data[key] if !exists { conn.Write(serializeRESP(nil)) return } - strValue, ok := value.(string) + if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { + delete(db.data, key) + conn.Write(serializeRESP(nil)) + return + } + + strValue, ok := item.Value.(string) if !ok { err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") conn.Write(serializeRESP(err)) @@ -65,9 +72,9 @@ func handleSet(conn net.Conn, args []string) { } key := args[1] - value := args[2] + item := Item{Value: args[2]} - db.data[key] = value + db.data[key] = item conn.Write(serializeRESP(SimpleString("OK"))) } @@ -104,9 +111,9 @@ func handleIncr(conn net.Conn, args []string) { key := args[1] - value, exists := db.data[key] + item, exists := db.data[key] if exists { - strValue, ok := value.(string) + strValue, ok := item.Value.(string) if !ok { err := errors.New("value is not an integer or out of range") conn.Write(serializeRESP(err)) @@ -121,10 +128,10 @@ func handleIncr(conn net.Conn, args []string) { } newValueStr := strconv.Itoa(currentInt + 1) - db.data[key] = newValueStr + db.data[key] = Item{Value: newValueStr} conn.Write(serializeRESP(currentInt + 1)) } else { - db.data[key] = "1" + db.data[key] = Item{Value: "1"} conn.Write(serializeRESP(1)) } } @@ -141,9 +148,9 @@ func handleDecr(conn net.Conn, args []string) { key := args[1] - value, exists := db.data[key] + item, exists := db.data[key] if exists { - strValue, ok := value.(string) + strValue, ok := item.Value.(string) if !ok { err := errors.New("value is not an integer or out of range") conn.Write(serializeRESP(err)) @@ -158,10 +165,10 @@ func handleDecr(conn net.Conn, args []string) { } newValueStr := strconv.Itoa(currentInt - 1) - db.data[key] = newValueStr + db.data[key] = Item{Value:newValueStr} conn.Write(serializeRESP(currentInt - 1)) } else { - db.data[key] = "-1" + db.data[key] = Item{Value: "-1"} conn.Write(serializeRESP(-1)) } } @@ -185,7 +192,7 @@ func handleFlushall(conn net.Conn, args []string) { return } - db.data = make(map[string]any) + db.data = make(map[string]Item) conn.Write(serializeRESP(SimpleString("OK"))) } @@ -197,11 +204,11 @@ func handleRpush(conn net.Conn, args []string) { defer db.mu.Unlock() var list []string - existingValue, exists := db.data[key] + existingItem, exists := db.data[key] if exists { var ok bool - list, ok = existingValue.([]string) + list, ok = existingItem.Value.([]string) if !ok { err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") conn.Write(serializeRESP(err)) @@ -211,7 +218,7 @@ func handleRpush(conn net.Conn, args []string) { list = []string{} } list = append(list, newItems...) - db.data[key] = list + db.data[key] = Item{Value: list} conn.Write(serializeRESP(len(list))) } @@ -234,14 +241,20 @@ func handleLrange(conn net.Conn, args []string) { key := args[1] - value, exists := db.data[key] + item, exists := db.data[key] if !exists { conn.Write(serializeRESP([]string{})) return } - typedList, ok := value.([]string) + if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { + delete(db.data, key) + conn.Write(serializeRESP(nil)) + return + } + + typedList, ok := item.Value.([]string) if !ok { err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") conn.Write(serializeRESP(err)) @@ -288,13 +301,13 @@ func handleLpop(conn net.Conn, args []string){ } key := args[1] - value, exists := db.data[key] + item, exists := db.data[key] if !exists { conn.Write(serializeRESP(nil)) return } - typedList, ok := value.([]string) + typedList, ok := item.Value.([]string) if !ok { err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") conn.Write(serializeRESP(err)) @@ -306,14 +319,14 @@ func handleLpop(conn net.Conn, args []string){ return } - item := typedList[0] + newItem := typedList[0] newList := typedList[1:] if len(newList) <= 0 { delete(db.data, key) }else{ - db.data[key] = newList + db.data[key] = Item{Value: newList} } - conn.Write(serializeRESP(item)) + conn.Write(serializeRESP(newItem)) } @@ -328,13 +341,13 @@ func handleRpop(conn net.Conn, args []string){ } key := args[1] - value, exists := db.data[key] + item, exists := db.data[key] if !exists { conn.Write(serializeRESP(nil)) return } - typedList, ok := value.([]string) + typedList, ok := item.Value.([]string) if !ok { err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") conn.Write(serializeRESP(err)) @@ -346,13 +359,41 @@ func handleRpop(conn net.Conn, args []string){ return } - item := typedList[len(typedList)-1] + newItem := typedList[len(typedList)-1] newList := typedList[:len(typedList)-1] if len(newList) <= 0 { delete(db.data, key) }else{ - db.data[key] = newList + db.data[key] = Item{Value: newList} } - conn.Write(serializeRESP(item)) + conn.Write(serializeRESP(newItem)) } + +func handleExpire(conn net.Conn, args []string){ + + if len(args) != 3 { + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'EXPIRE'"))) + return + } + + key := args[1] + seconds, err := strconv.Atoi(args[2]) + if err != nil { + conn.Write(serializeRESP(errors.New("ERR value is not an integer"))) + return + } + db.mu.Lock() + defer db.mu.Unlock() + + item, exists := db.data[key] + if !exists { + conn.Write(serializeRESP(0)) // Key doesn't exist + return + } + + expiry := time.Now().Add(time.Duration(seconds) * time.Second) + item.ExpiresAt = &expiry + db.data[key] = item + conn.Write(serializeRESP(1)) +}
\ No newline at end of file @@ -7,6 +7,7 @@ import ( "net" "os" "strings" + "time" ) @@ -27,10 +28,24 @@ var commandRegistry = map[string]handlerFunc{ "LRANGE": handleLrange, "LPOP": handleLpop, "RPOP": handleRpop, + "EXPIRE": handleExpire, } func main() { - db = RedisDB{data: make(map[string]any)} + db = RedisDB{data: make(map[string]Item)} + + go func() { + ticker := time.NewTicker(1 * time.Second) + for range ticker.C { + db.mu.Lock() + for k, v := range db.data { + if v.ExpiresAt != nil && time.Now().After(*v.ExpiresAt) { + delete(db.data, k) + } + } + db.mu.Unlock() + } + }() listener, err := net.Listen("tcp", ":6379") if err != nil { @@ -3,11 +3,15 @@ package main import ( "fmt" "sync" + "time" ) - +type Item struct { + Value any + ExpiresAt *time.Time +} type RedisDB struct { mu sync.Mutex - data map[string]any + data map[string]Item } type SimpleString string |
