diff options
Diffstat (limited to 'handlers.go')
| -rw-r--r-- | handlers.go | 809 |
1 files changed, 377 insertions, 432 deletions
diff --git a/handlers.go b/handlers.go index 105bca0..b5987e8 100644 --- a/handlers.go +++ b/handlers.go @@ -3,6 +3,8 @@ package main import ( "errors" "net" + "path/filepath" + "sort" "strconv" "time" ) @@ -221,538 +223,481 @@ func handleRename(conn net.Conn, args []string){ conn.Write(serializeRESP(result)) } -/* func handleRpush(conn net.Conn, args []string) { - key := args[1] - newItems := args[2:] - if len(args) < 3 { - err := errors.New("wrong number of arguments for 'RPUSH'") - conn.Write(serializeRESP(err)) + err := errors.New("wrong number of arguments for 'RPUSH'") + conn.Write(serializeRESP(err)) return - } - - db.mu.Lock() - defer db.mu.Unlock() - - var list []string - existingItem, exists := db.data[key] + } + key := args[1] + newItems := args[2:] - if exists { - var ok bool - 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)) - return - } + result := db.Execute(key, func(s *Shard) interface{} { + var list []string + existingItem, exists := s.data[key] + if exists { + var ok bool + list, ok = existingItem.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } } else { list = []string{} } - list = append(list, newItems...) - db.data[key] = Item{Value: list} - conn.Write(serializeRESP(len(list))) + list = append(list, newItems...) + s.data[key] = Item{Value: list} + return len(list) + }) + conn.Write(serializeRESP(result)) } func handleLpush(conn net.Conn, args []string) { - key := args[1] - newItems := args[2:] - if len(args) < 3 { - err := errors.New("wrong number of arguments for 'LPUSH'") - conn.Write(serializeRESP(err)) + err := errors.New("wrong number of arguments for 'LPUSH'") + conn.Write(serializeRESP(err)) return - } - - db.mu.Lock() - defer db.mu.Unlock() - - var list []string - existingItem, exists := db.data[key] + } + key := args[1] + newItems := args[2:] - if exists { - var ok bool - 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)) - return - } + result := db.Execute(key, func(s *Shard) interface{} { + var list []string + existingItem, exists := s.data[key] + if exists { + var ok bool + list, ok = existingItem.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } } else { list = []string{} } - list = append(newItems, list...) - db.data[key] = Item{Value: list} - conn.Write(serializeRESP(len(list))) + list = append(newItems, list...) + s.data[key] = Item{Value: list} + return len(list) + }) + conn.Write(serializeRESP(result)) } func handleLrange(conn net.Conn, args []string) { - db.mu.Lock() - defer db.mu.Unlock() - if len(args) != 4 { - err := errors.New("wrong number of arguments for 'LRANGE'") - conn.Write(serializeRESP(err)) + err := errors.New("wrong number of arguments for 'LRANGE'") + conn.Write(serializeRESP(err)) return - } + } start, err1 := strconv.Atoi(args[2]) stop, err2 := strconv.Atoi(args[3]) if err1 != nil || err2 != nil { err := errors.New("value is not an integer or out of range") conn.Write(serializeRESP(err)) - return - } - - key := args[1] - - item, exists := db.data[key] - if !exists { - conn.Write(serializeRESP([]string{})) - - return - } - - if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { - delete(db.data, key) - conn.Write(serializeRESP(nil)) return } + key := args[1] - 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)) - return - } - - size := len(typedList) - - if start < 0 { - start += size - if start < 0 { - start = 0 - } - } - - if stop < 0 { - stop += size - if stop < 0 { - stop = 0 - } - } - - if stop >= size { - stop = size - 1 - } - - if start > stop || start >= size { - conn.Write(serializeRESP([]string{})) - return - } - - resultList := typedList[start : stop+1] - conn.Write(serializeRESP(resultList)) + result := db.ExecuteRead(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return []string{} + } + if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { + delete(s.data, key) + return nil + } + typedList, ok := item.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + size := len(typedList) + if start < 0 { + start += size + if start < 0 { + start = 0 + } + } + if stop < 0 { + stop += size + if stop < 0 { + stop = 0 + } + } + if stop >= size { + stop = size - 1 + } + if start > stop || start >= size { + return []string{} + } + return typedList[start : stop+1] + }) + conn.Write(serializeRESP(result)) } -func handleLpop(conn net.Conn, args []string){ - db.mu.Lock() - defer db.mu.Unlock() - +func handleLpop(conn net.Conn, args []string) { if len(args) != 2 { - err := errors.New("wrong number of arguments for 'LPOP'") - conn.Write(serializeRESP(err)) - return - } - - key := args[1] - item, exists := db.data[key] - if !exists { - 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") + err := errors.New("wrong number of arguments for 'LPOP'") conn.Write(serializeRESP(err)) - return - } - - if len(typedList) == 0 { - conn.Write(serializeRESP(nil)) - return - } - - newItem := typedList[0] - newList := typedList[1:] - if len(newList) <= 0 { - delete(db.data, key) - }else{ - db.data[key] = Item{Value: newList} + return } - conn.Write(serializeRESP(newItem)) + key := args[1] + result := db.Execute(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return nil + } + typedList, ok := item.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + if len(typedList) == 0 { + return nil + } + newItem := typedList[0] + newList := typedList[1:] + if len(newList) <= 0 { + delete(s.data, key) + } else { + s.data[key] = Item{Value: newList} + } + return newItem + }) + conn.Write(serializeRESP(result)) } -func handleRpop(conn net.Conn, args []string){ - db.mu.Lock() - defer db.mu.Unlock() - +func handleRpop(conn net.Conn, args []string) { if len(args) != 2 { - err := errors.New("wrong number of arguments for 'RPOOP'") - conn.Write(serializeRESP(err)) - return - } - - key := args[1] - item, exists := db.data[key] - if !exists { - 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") + err := errors.New("wrong number of arguments for 'RPOP'") conn.Write(serializeRESP(err)) - return - } - - if len(typedList) == 0 { - conn.Write(serializeRESP(nil)) - return - } - - newItem := typedList[len(typedList)-1] - newList := typedList[:len(typedList)-1] - if len(newList) <= 0 { - delete(db.data, key) - }else{ - db.data[key] = Item{Value: newList} + return } - conn.Write(serializeRESP(newItem)) + key := args[1] + result := db.Execute(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return nil + } + typedList, ok := item.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + if len(typedList) == 0 { + return nil + } + newItem := typedList[len(typedList)-1] + newList := typedList[:len(typedList)-1] + if len(newList) <= 0 { + delete(s.data, key) + } else { + s.data[key] = Item{Value: newList} + } + return newItem + }) + conn.Write(serializeRESP(result)) } -func handleExpire(conn net.Conn, args []string){ - +func handleExpire(conn net.Conn, args []string) { if len(args) != 3 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'EXPIRE'"))) - return - } - + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'EXPIRE'"))) + return + } key := args[1] - seconds, err := strconv.Atoi(args[2]) + seconds, err := strconv.Atoi(args[2]) if err != nil { - conn.Write(serializeRESP(errors.New("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 - } + conn.Write(serializeRESP(errors.New("value is not an integer"))) + return + } - expiry := time.Now().Add(time.Duration(seconds) * time.Second) - item.ExpiresAt = &expiry - db.data[key] = item - conn.Write(serializeRESP(1)) + result := db.Execute(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return 0 + } + expiry := time.Now().Add(time.Duration(seconds) * time.Second) + item.ExpiresAt = &expiry + s.data[key] = item + return 1 + }) + conn.Write(serializeRESP(result)) } -func handleHset(conn net.Conn, args []string){ - +func handleHset(conn net.Conn, args []string) { if len(args) < 4 || (len(args)-2)%2 != 0 { - err := errors.New("wrong number of arguments for 'HSET'") - conn.Write(serializeRESP(err)) - return - } + err := errors.New("wrong number of arguments for 'HSET'") + conn.Write(serializeRESP(err)) + return + } key := args[1] - db.mu.Lock() - defer db.mu.Unlock() - item, exists := db.data[key] - - var hash map[string]string - - if exists { - var ok bool - hash, ok = item.Value.(map[string]string) - if !ok { - conn.Write(serializeRESP(errors.New("WRONGTYPE Operation against a key holding the wrong kind of value"))) - return - } - } else { - hash = make(map[string]string) - } - count := 0 - for i := 2; i < len(args); i += 2 { - hash[args[i]] = args[i+1] - count += 1 - } - db.data[key] = Item{Value: hash} - conn.Write(serializeRESP(count)) + result := db.Execute(key, func(s *Shard) interface{} { + item, exists := s.data[key] + var hash map[string]string + if exists { + var ok bool + hash, ok = item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + } else { + hash = make(map[string]string) + } + count := 0 + for i := 2; i < len(args); i += 2 { + hash[args[i]] = args[i+1] + count++ + } + s.data[key] = Item{Value: hash} + return count + }) + conn.Write(serializeRESP(result)) } func handleHget(conn net.Conn, args []string) { - if len(args) != 3 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HGET'"))) - return - } - - key := args[1] - field := args[2] - - db.mu.Lock() - defer db.mu.Unlock() - - item, exists := db.data[key] - if !exists { - conn.Write(serializeRESP(nil)) - return - } - - hash, ok := item.Value.(map[string]string) - if !ok { - conn.Write(serializeRESP(errors.New("WRONGTYPE Operation against a key holding the wrong kind of value"))) - return - } - - val, found := hash[field] - if !found { - conn.Write(serializeRESP(nil)) - return - } + if len(args) != 3 { + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HGET'"))) + return + } + key := args[1] + field := args[2] - conn.Write(serializeRESP(val)) + result := db.ExecuteRead(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + val, found := hash[field] + if !found { + return nil + } + return val + }) + conn.Write(serializeRESP(result)) } -func handleHgetall(conn net.Conn, args []string){ +func handleHgetall(conn net.Conn, args []string) { if len(args) != 2 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HGETALL'"))) - return - } + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HGETALL'"))) + return + } key := args[1] - db.mu.Lock() - defer db.mu.Unlock() - - item, exists := db.data[key] - if !exists { - conn.Write(serializeRESP(nil)) - return - } - - hash, ok := item.Value.(map[string]string) - if !ok { - conn.Write(serializeRESP(errors.New("WRONGTYPE Operation against a key holding the wrong kind of value"))) - return - } - conn.Write(serializeRESP(hash)) + result := db.ExecuteRead(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + return hash + }) + conn.Write(serializeRESP(result)) } -func handleHkeys(conn net.Conn, args []string){ +func handleHkeys(conn net.Conn, args []string) { if len(args) != 2 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HKEYS'"))) - return - } + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HKEYS'"))) + return + } key := args[1] - db.mu.Lock() - defer db.mu.Unlock() - - item, exists := db.data[key] - if !exists { - conn.Write(serializeRESP(nil)) - return - } - - hash, ok := item.Value.(map[string]string) - if !ok { - conn.Write(serializeRESP(errors.New("WRONGTYPE Operation against a key holding the wrong kind of value"))) - return - } - result := make([]string, 0, len(hash)) - for key := range hash { - result = append(result, key) - } + result := db.ExecuteRead(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + keys := make([]string, 0, len(hash)) + for k := range hash { + keys = append(keys, k) + } + return keys + }) conn.Write(serializeRESP(result)) } -func handleHvalues(conn net.Conn, args []string){ +func handleHvalues(conn net.Conn, args []string) { if len(args) != 2 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HVALUES'"))) - return - } + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'HVALUES'"))) + return + } key := args[1] - db.mu.Lock() - defer db.mu.Unlock() - - item, exists := db.data[key] - if !exists { - conn.Write(serializeRESP(nil)) - return - } - - hash, ok := item.Value.(map[string]string) - if !ok { - conn.Write(serializeRESP(errors.New("WRONGTYPE Operation against a key holding the wrong kind of value"))) - return - } - result := make([]string, 0, len(hash)) - for _, value := range hash { - result = append(result, value) - } + result := db.ExecuteRead(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + values := make([]string, 0, len(hash)) + for _, v := range hash { + values = append(values, v) + } + return values + }) conn.Write(serializeRESP(result)) } -func handleType(conn net.Conn, args []string){ +func handleType(conn net.Conn, args []string) { if len(args) != 2 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'TYPE'"))) - return - } + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'TYPE'"))) + return + } key := args[1] - db.mu.Lock() - defer db.mu.Unlock() - - item, exists := db.data[key] - if !exists { - conn.Write(serializeRESP(SimpleString("none"))) - return - } - - switch item.Value.(type) { - case string: - conn.Write(serializeRESP(SimpleString("string"))) - case []string: - conn.Write(serializeRESP(SimpleString("list"))) - case map[string]string: - conn.Write(serializeRESP(SimpleString("hash"))) - default: - conn.Write(serializeRESP(SimpleString("unknown"))) - } + result := db.ExecuteRead(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return SimpleString("none") + } + switch item.Value.(type) { + case string: + return SimpleString("string") + case []string: + return SimpleString("list") + case map[string]string: + return SimpleString("hash") + default: + return SimpleString("unknown") + } + }) + conn.Write(serializeRESP(result)) } -func handleDbsize(conn net.Conn, args []string){ +func handleDbsize(conn net.Conn, args []string) { if len(args) != 1 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'DBSIZE'"))) - return - } - db.mu.Lock() - defer db.mu.Unlock() - conn.Write(serializeRESP(len(db.data))) + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'DBSIZE'"))) + return + } + result := db.ExecuteReadAll(func(shards []*Shard) interface{} { + count := 0 + for _, shard := range shards { + count += len(shard.data) + } + return count + }) + conn.Write(serializeRESP(result)) } + func handleKeys(conn net.Conn, args []string) { if len(args) < 2 || len(args) > 3 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'DBSIZE'"))) - return - } + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'KEYS'"))) + return + } + pattern := args[1] limit := -1 - pattern := args[1] - var matches []string if len(args) == 3 { - val, err := strconv.Atoi(args[2]) - if err != nil { - conn.Write(serializeRESP(errors.New("value is not an integer"))) - return - } - limit = val - } - db.mu.Lock() - for key := range db.data { - matched, err := filepath.Match(pattern, key) - if err != nil { - conn.Write(serializeRESP(errors.New("illegal glob pattern"))) - db.mu.Unlock() - return - } - - if matched { - matches = append(matches, key) - } - } - - sort.Strings(matches) - if limit != -1 && len(matches) > limit { - matches = matches[:limit] - } - db.mu.Unlock() + val, err := strconv.Atoi(args[2]) + if err != nil { + conn.Write(serializeRESP(errors.New("value is not an integer"))) + return + } + limit = val + } - conn.Write(serializeRESP(matches)) + result := db.ExecuteReadAll(func(shards []*Shard) interface{} { + var matches []string + for _, shard := range shards { + for key := range shard.data { + matched, err := filepath.Match(pattern, key) + if err != nil { + return errors.New("illegal glob pattern") + } + if matched { + matches = append(matches, key) + } + } + } + sort.Strings(matches) + if limit != -1 && len(matches) > limit { + matches = matches[:limit] + } + return matches + }) + conn.Write(serializeRESP(result)) } -func handleLrem(conn net.Conn, args []string){ +func handleLrem(conn net.Conn, args []string) { if len(args) != 4 { - conn.Write(serializeRESP(errors.New("wrong number of arguments for 'LREM'"))) - return - } - db.mu.Lock() - defer db.mu.Unlock() - + conn.Write(serializeRESP(errors.New("wrong number of arguments for 'LREM'"))) + return + } key := args[1] - count,err := strconv.Atoi(args[2]) + count, err := strconv.Atoi(args[2]) toBeRemoved := args[3] - if err != nil { conn.Write(serializeRESP(errors.New("value is not an integer"))) - return + return } - item, exists:= db.data[key] - if !exists { - } - list, ok := item.Value.([]string) - if !ok { - conn.Write(serializeRESP(errors.New("WRONGTYPE Operation against a key holding the wrong kind of value"))) - return - } - counter := 0 - absCount := count - if count < 0 { - absCount = -count - } - switch{ - case count == 0: - newList := []string{} - for i, value := range list { - if value == toBeRemoved{ - newList = append(list[:i], list[i+1:]...) - counter += 1 + result := db.Execute(key, func(s *Shard) interface{} { + item, exists := s.data[key] + if !exists { + return 0 + } + list, ok := item.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + + counter := 0 + absCount := count + if count < 0 { + absCount = -count + } + + var newList []string + if count == 0 { + newList = []string{} + for _, value := range list { + if value == toBeRemoved { + counter++ + } else { + newList = append(newList, value) } } - list = newList - case count > 0: - newList := []string{} - for i, value := range list { - if counter == absCount{ - break - } - if value == toBeRemoved{ - newList = append(list[:i], list[i+1:]...) - counter += 1 + } else if count > 0 { + newList = []string{} + for _, value := range list { + if value == toBeRemoved && counter < absCount { + counter++ + } else { + newList = append(newList, value) } } - list = newList - case count < 0: - newList := []string{} + } else { // count < 0 + reversedNewList := []string{} for i := len(list) - 1; i >= 0; i-- { - value := list[i] - if counter == absCount{ - break - } - if value == toBeRemoved{ - newList = append(list[:i], list[i+1:]...) - counter += 1 + value := list[i] + if value == toBeRemoved && counter < absCount { + counter++ + } else { + reversedNewList = append(reversedNewList, value) } } - list = newList - } - item.Value = list - db.data[key] = item - conn.Write(serializeRESP(counter)) -} -*/
\ No newline at end of file + newList = make([]string, len(reversedNewList)) + for i, v := range reversedNewList { + newList[len(reversedNewList)-1-i] = v + } + } + + if len(newList) == 0 { + delete(s.data, key) + } else { + item.Value = newList + s.data[key] = item + } + return counter + }) + conn.Write(serializeRESP(result)) +}
\ No newline at end of file |
