tree store init commit
This commit is contained in:
2
etcd.go
2
etcd.go
@ -212,7 +212,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open the snapshot
|
// open the snapshot
|
||||||
go server.Snapshot()
|
//go server.Snapshot()
|
||||||
|
|
||||||
if webPort != -1 {
|
if webPort != -1 {
|
||||||
// start web
|
// start web
|
||||||
|
@ -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
|
|
||||||
}
|
|
150
store/tree_store.go
Normal file
150
store/tree_store.go
Normal file
@ -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 ""
|
||||||
|
}
|
||||||
|
|
82
store/tree_store_test.go
Normal file
82
store/tree_store_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user