aboutsummaryrefslogtreecommitdiff
path: root/handlers.go
diff options
context:
space:
mode:
Diffstat (limited to 'handlers.go')
-rw-r--r--handlers.go809
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