From c5901f4b88ac6fa6cf8d917b01fb238a118ced0c Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Tue, 2 Jul 2013 18:09:54 -0700 Subject: [PATCH] =?UTF-8?q?=E3=80=80tree=20store=20init=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- etcd.go | 2 +- store/{store_test.go => store_test.bak} | 0 store/tree_store.bak | 85 ----------- store/tree_store.go | 150 ++++++++++++++++++++ store/tree_store_test.go | 82 +++++++++++ store/{watcher_test.go => watcher_test.bak} | 0 6 files changed, 233 insertions(+), 86 deletions(-) rename store/{store_test.go => store_test.bak} (100%) delete mode 100644 store/tree_store.bak create mode 100644 store/tree_store.go create mode 100644 store/tree_store_test.go rename store/{watcher_test.go => watcher_test.bak} (100%) diff --git a/etcd.go b/etcd.go index 721193143..a327bcda6 100644 --- a/etcd.go +++ b/etcd.go @@ -212,7 +212,7 @@ func main() { } // open the snapshot - go server.Snapshot() + //go server.Snapshot() if webPort != -1 { // start web diff --git a/store/store_test.go b/store/store_test.bak similarity index 100% rename from store/store_test.go rename to store/store_test.bak diff --git a/store/tree_store.bak b/store/tree_store.bak deleted file mode 100644 index 60616b3f5..000000000 --- a/store/tree_store.bak +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "path" - "strings" - ) - -type store struct { - nodes map[string]node -} - -type node struct { - value string - dir bool // just for clearity - nodes map[string]node -} - -// set the key to value, return the old value if the key exists -func (s *store) set(key string, value string) string, error { - - key = path.Clean(key) - - nodeNames := strings.Split(key, "/") - - levelNodes := s.nodes - for i = 0; i < len(nodes) - 1; ++i { - node, ok := levelNodes[nodeNames[i]] - // add new dir - if !ok { - node := Node{nodeNames[i], true, make(map[string]node)} - levelNodes[nodeNames[i]] := node - } else if ok && !node.dir { - return nil, errors.New("The key is a directory") - } - else { - levelNodes = levelNodes.nodes - } - } - // add the last node and value - node, ok := levelNodes[nodeNames[i]] - - if !ok { - node := Node{nodeNames[i], false, nil} - levelNodes[nodeNames] = node - return nil, nil - } else { - oldValue := node.value - node.value = value - return oldValue ,nil - } - -} - -// get the node of the key -func (s *store) get(key string) node { - key = path.Clean(key) - - nodeNames := strings.Split(key, "/") - - levelNodes := s.nodes - - for i = 0; i < len(nodes) - 1; ++i { - node, ok := levelNodes[nodeNames[i]] - if !ok || !node.dir { - return nil - } - levelNodes = levelNodes.nodes - } - - node, ok := levelNodes[nodeNames[i]] - if ok { - return node - } - return nil - -} - -// delete the key, return the old value if the key exists -func (s *store) delete(key string) string { - return nil -} - -func (n *node) Value() string{ - return n.value -} diff --git a/store/tree_store.go b/store/tree_store.go new file mode 100644 index 000000000..b84ec3ad1 --- /dev/null +++ b/store/tree_store.go @@ -0,0 +1,150 @@ +package store + +import ( + "path" + "strings" + "errors" + //"fmt" + ) + +type treeStore struct { + Root *treeNode +} + +type treeNode struct { + Value string + + Dir bool //for clearity + + NodeMap map[string]*treeNode + + // if the node is a permanent one the ExprieTime will be Unix(0,0) + // Otherwise after the expireTime, the node will be deleted + ExpireTime time.Time `json:"expireTime"` + + // a channel to update the expireTime of the node + update chan time.Time `json:"-"` +} + +// set the key to value, return the old value if the key exists +func (s *treeStore) set(key string, value string, expireTime time.Time, index uint64) (string, error) { + key = "/" + key + key = path.Clean(key) + + nodes := strings.Split(key, "/") + nodes = nodes[1:] + + //fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes)) + + nodeMap := s.Root.NodeMap + + i := 0 + newDir := false + + for i = 0; i < len(nodes) - 1; i++ { + + if newDir { + node := &treeNode{".", true, make(map[string]*treeNode)} + nodeMap[nodes[i]] = node + nodeMap = node.NodeMap + continue + } + + node, ok := nodeMap[nodes[i]] + // add new dir + if !ok { + //fmt.Println("TreeStore: Add a dir ", nodes[i]) + newDir = true + node := &treeNode{".", true, make(map[string]*treeNode)} + nodeMap[nodes[i]] = node + nodeMap = node.NodeMap + + } else if ok && !node.Dir { + + return "", errors.New("Try to add a key under a file") + } else { + + //fmt.Println("TreeStore: found dir ", nodes[i]) + nodeMap = node.NodeMap + } + + } + + // add the last node and value + node, ok := nodeMap[nodes[i]] + + if !ok { + node := &treeNode{value, false, nil} + nodeMap[nodes[i]] = node + //fmt.Println("TreeStore: Add a new Node ", key, "=", value) + return "", nil + } else { + oldValue := node.Value + node.Value = value + //fmt.Println("TreeStore: Update a Node ", key, "=", value, "[", oldValue, "]") + return oldValue ,nil + } + +} + +// get the node of the key +func (s *treeStore) get(key string) *treeNode { + key = "/" + key + key = path.Clean(key) + + nodes := strings.Split(key, "/") + nodes = nodes[1:] + + //fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes)) + + nodeMap := s.Root.NodeMap + + var i int + + for i = 0; i < len(nodes) - 1; i++ { + node, ok := nodeMap[nodes[i]] + if !ok || !node.Dir { + return nil + } + nodeMap = node.NodeMap + } + + node, ok := nodeMap[nodes[i]] + if ok { + return node + } + return nil + +} + +// delete the key, return the old value if the key exists +func (s *treeStore) delete(key string) string { + key = "/" + key + key = path.Clean(key) + + nodes := strings.Split(key, "/") + nodes = nodes[1:] + + //fmt.Println("TreeStore: Nodes ", nodes, " length: ", len(nodes)) + + nodeMap := s.Root.NodeMap + + var i int + + for i = 0; i < len(nodes) - 1; i++ { + node, ok := nodeMap[nodes[i]] + if !ok || !node.Dir { + return "" + } + nodeMap = node.NodeMap + } + + node, ok := nodeMap[nodes[i]] + if ok && !node.Dir{ + oldValue := node.Value + delete(nodeMap, nodes[i]) + return oldValue + } + return "" +} + diff --git a/store/tree_store_test.go b/store/tree_store_test.go new file mode 100644 index 000000000..562e5bfeb --- /dev/null +++ b/store/tree_store_test.go @@ -0,0 +1,82 @@ +package store + +import ( + "testing" + "math/rand" + "strconv" +) + +func TestStoreGet(t *testing.T) { + + ts := &treeStore{ + &treeNode{ + "/", + true, + make(map[string]*treeNode), + }, + } + + // create key + ts.set("/foo", "bar") + // change value + ts.set("/foo", "barbar") + // create key + ts.set("/hello/foo", "barbarbar") + treeNode := ts.get("/foo") + + if treeNode == nil { + t.Fatalf("Expect to get node, but not") + } + if treeNode.Value != "barbar" { + t.Fatalf("Expect value barbar, but got %s", treeNode.Value) + } + + // create key + treeNode = ts.get("/hello/foo") + if treeNode == nil { + t.Fatalf("Expect to get node, but not") + } + if treeNode.Value != "barbarbar" { + t.Fatalf("Expect value barbarbar, but got %s", treeNode.Value) + } + + // create a key under other key + _, err := ts.set("/foo/foo", "bar") + if err == nil { + t.Fatalf("shoud not add key under a exisiting key") + } + + // delete a key + oldValue := ts.delete("/foo") + if oldValue != "barbar" { + t.Fatalf("Expect Oldvalue bar, but got %s", oldValue) + } + + // delete a directory + oldValue = ts.delete("/hello") + if oldValue != "" { + t.Fatalf("Expect cannot delet /hello, but deleted! %s", oldValue) + } + + + // speed test + for i:=0; i < 10000; i++ { + key := "/" + depth := rand.Intn(10) + for j := 0; j < depth; j++ { + key += "/" + strconv.Itoa(rand.Int()) + } + value := strconv.Itoa(rand.Int()) + ts.set(key, value) + treeNode := ts.get(key) + + if treeNode == nil { + t.Fatalf("Expect to get node, but not") + } + if treeNode.Value != value { + t.Fatalf("Expect value %s, but got %s", value, treeNode.Value) + } + + } + +} \ No newline at end of file diff --git a/store/watcher_test.go b/store/watcher_test.bak similarity index 100% rename from store/watcher_test.go rename to store/watcher_test.bak