From 93c96b3813a46f060c44d9c23c013c42fc84cc68 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Fri, 5 Jul 2013 16:22:45 -0700 Subject: [PATCH] add list feature --- command.go | 15 +++++++++++ etcd.go | 1 + handlers.go | 26 +++++++++++++++++++ store/store.go | 24 +++++++++++++++++- store/tree.go | 54 ++++++++++++++++++++++++++++++++++------ store/tree_store_test.go | 16 ++++++++++++ 6 files changed, 127 insertions(+), 9 deletions(-) diff --git a/command.go b/command.go index 7ab53d8a5..9188a439b 100644 --- a/command.go +++ b/command.go @@ -61,6 +61,21 @@ func (c *GetCommand) GeneratePath() string { return "get/" + c.Key } +// List command +type ListCommand struct { + Prefix string `json:"prefix"` +} + +// The name of the command in the log +func (c *ListCommand) CommandName() string { + return "list" +} + +// Set the value of key to value +func (c *ListCommand) Apply(server *raft.Server) (interface{}, error) { + return store.List(c.Prefix) +} + // Delete command type DeleteCommand struct { Key string `json:"key"` diff --git a/etcd.go b/etcd.go index a327bcda6..02e52e72b 100644 --- a/etcd.go +++ b/etcd.go @@ -318,6 +318,7 @@ func startClientTransport(port int, st int) { // external commands http.HandleFunc("/v1/keys/", Multiplexer) http.HandleFunc("/v1/watch/", WatchHttpHandler) + http.HandleFunc("/v1/list/", ListHttpHandler) http.HandleFunc("/master", MasterHttpHandler) switch st { diff --git a/handlers.go b/handlers.go index d84457a7b..7581eb8f2 100644 --- a/handlers.go +++ b/handlers.go @@ -231,6 +231,32 @@ func GetHttpHandler(w *http.ResponseWriter, req *http.Request) { } +func ListHttpHandler(w http.ResponseWriter, req *http.Request) { + prefix := req.URL.Path[len("/v1/list/"):] + + debug("[recv] GET http://%v/v1/list/%s", server.Name(), prefix) + + command := &ListCommand{} + command.Prefix = prefix + + if body, err := command.Apply(server); err != nil { + warn("raftd: Unable to write file: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } else { + w.WriteHeader(http.StatusOK) + + body, ok := body.([]byte) + if !ok { + panic("wrong type") + } + + w.Write(body) + return + } + +} + func WatchHttpHandler(w http.ResponseWriter, req *http.Request) { key := req.URL.Path[len("/v1/watch/"):] diff --git a/store/store.go b/store/store.go index 4bd73ac30..9f56827a2 100644 --- a/store/store.go +++ b/store/store.go @@ -70,11 +70,17 @@ type Response struct { Expiration time.Time `json:"expiration"` // countdown until expiration in seconds - TTL int64 `json:"TTL"` + TTL int64 `json:"ttl"` Index uint64 `json:"index"` } +type ListNode struct { + Key string + Value string + Type string +} + // make a new stroe func CreateStore(max int) *Store { s = new(Store) @@ -303,6 +309,22 @@ func Get(key string) Response { } } +// // List all the item in the prefix +func List(prefix string) ([]byte, error) { + nodes, keys, dirs, ok := s.Tree.list(prefix) + + var ln []ListNode + + if ok { + ln = make([]ListNode, len(nodes)) + for i := 0; i < len(nodes); i++ { + ln[i] = ListNode{keys[i], nodes[i].Value, dirs[i]} + } + } + + return json.Marshal(ln) +} + // delete the key func Delete(key string, index uint64) ([]byte, error) { //update index diff --git a/store/tree.go b/store/tree.go index 625445874..34c34ce26 100644 --- a/store/tree.go +++ b/store/tree.go @@ -36,7 +36,7 @@ func (s tnWithKeySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } var emptyNode = Node{".", PERMANENT, nil} // set the key to value, return the old value if the key exists -func (s *tree) set(key string, value Node) bool { +func (t *tree) set(key string, value Node) bool { key = "/" + key key = path.Clean(key) @@ -45,7 +45,7 @@ func (s *tree) set(key string, value Node) bool { //fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes)) - nodeMap := s.Root.NodeMap + nodeMap := t.Root.NodeMap i := 0 newDir := false @@ -94,8 +94,8 @@ func (s *tree) set(key string, value Node) bool { } -// get the node of the key -func (s *tree) get(key string) (Node, bool) { +// use internally to get the internal tree node +func (t *tree)internalGet(key string) (*treeNode, bool) { key = "/" + key key = path.Clean(key) @@ -104,29 +104,67 @@ func (s *tree) get(key string) (Node, bool) { //fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes)) - nodeMap := s.Root.NodeMap + nodeMap := t.Root.NodeMap var i int for i = 0; i < len(nodes) - 1; i++ { node, ok := nodeMap[nodes[i]] if !ok || !node.Dir { - return emptyNode, false + return nil, false } nodeMap = node.NodeMap } treeNode, ok := nodeMap[nodes[i]] + if ok { + return treeNode, ok + } else { + return nil, ok + } +} + +// get the node of the key +func (t *tree) get(key string) (Node, bool) { + treeNode, ok := t.internalGet(key) + if ok { return treeNode.Value, ok } else { return emptyNode, ok } +} +// return the nodes under the directory +func (t *tree) list(prefix string) ([]Node, []string, []string, bool) { + treeNode, ok := t.internalGet(prefix) + + if !ok { + return nil, nil, nil, ok + } else { + length := len(treeNode.NodeMap) + nodes := make([]Node, length) + keys := make([]string, length) + dirs := make([]string, length) + i := 0 + + for key, node := range treeNode.NodeMap { + nodes[i] = node.Value + keys[i] = key + if node.Dir { + dirs[i] = "d" + } else { + dirs[i] = "f" + } + i++ + } + + return nodes, keys, dirs, ok + } } // delete the key, return the old value if the key exists -func (s *tree) delete(key string) bool { +func (t *tree) delete(key string) bool { key = "/" + key key = path.Clean(key) @@ -135,7 +173,7 @@ func (s *tree) delete(key string) bool { //fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes)) - nodeMap := s.Root.NodeMap + nodeMap := t.Root.NodeMap var i int diff --git a/store/tree_store_test.go b/store/tree_store_test.go index d5401127f..8611138e2 100644 --- a/store/tree_store_test.go +++ b/store/tree_store_test.go @@ -61,6 +61,22 @@ func TestStoreGet(t *testing.T) { } + // test list + ts.set("/hello/fooo", CreateTestNode("barbarbar")) + ts.set("/hello/foooo/foo", CreateTestNode("barbarbar")) + + nodes, keys, dirs, ok := ts.list("/hello") + + if !ok { + t.Fatalf("cannot list!") + } else { + length := len(nodes) + + for i := 0; i < length; i++ { + fmt.Println(keys[i] , "=", nodes[i].Value, "[", dirs[i], "]") + } + } + // speed test for i:=0; i < 100; i++ { key := "/"