Browse Source

Merge branch 'master' into moreStats

Xiang Li 12 years ago
parent
commit
197689fcb5
5 changed files with 142 additions and 95 deletions
  1. 5 4
      README.md
  2. 11 11
      build
  3. 2 0
      etcd_handlers.go
  4. 88 80
      store/store.go
  5. 36 0
      store/store_test.go

+ 5 - 4
README.md

@@ -30,6 +30,7 @@ You can build etcd from source:
 
 
 ```sh
 ```sh
 git clone https://github.com/coreos/etcd
 git clone https://github.com/coreos/etcd
+cd etcd
 ./build
 ./build
 ```
 ```
 
 
@@ -141,7 +142,7 @@ Now you can try to get the key by sending:
 curl -L http://127.0.0.1:4001/v1/keys/foo
 curl -L http://127.0.0.1:4001/v1/keys/foo
 ```
 ```
 
 
-If the TTL has expired, the key will be deleted, and you will be returned a 404.
+If the TTL has expired, the key will be deleted, and you will be returned a 100.
 
 
 ```json
 ```json
 {"errorCode":100,"message":"Key Not Found","cause":"/foo"}
 {"errorCode":100,"message":"Key Not Found","cause":"/foo"}
@@ -356,7 +357,7 @@ Let the join two more nodes to this cluster using the -C argument:
 Get the machines in the cluster:
 Get the machines in the cluster:
 
 
 ```sh
 ```sh
-curl -L http://127.0.0.1:4001/machines
+curl -L http://127.0.0.1:4001/v1/machines
 ```
 ```
 
 
 We should see there are three nodes in the cluster
 We should see there are three nodes in the cluster
@@ -380,7 +381,7 @@ The key of the machine is based on the ```commit index``` when it was added. The
 Also try to get the current leader in the cluster
 Also try to get the current leader in the cluster
 
 
 ```
 ```
-curl -L http://127.0.0.1:4001/leader
+curl -L http://127.0.0.1:4001/v1/leader
 ```
 ```
 The first server we set up should be the leader, if it has not dead during these commands.
 The first server we set up should be the leader, if it has not dead during these commands.
 
 
@@ -409,7 +410,7 @@ curl -L http://127.0.0.1:4002/v1/keys/foo
 A new leader should have been elected.
 A new leader should have been elected.
 
 
 ```
 ```
-curl -L http://127.0.0.1:4001/leader
+curl -L http://127.0.0.1:4001/v1/leader
 ```
 ```
 
 
 ```
 ```

+ 11 - 11
build

@@ -1,25 +1,25 @@
 #!/bin/bash
 #!/bin/bash
 
 
 ETCD_PACKAGE=github.com/coreos/etcd
 ETCD_PACKAGE=github.com/coreos/etcd
-export GOPATH=${PWD}
-SRC_DIR=$GOPATH/src
-ETCD_DIR=$SRC_DIR/$ETCD_PACKAGE
+export GOPATH="${PWD}"
+SRC_DIR="$GOPATH/src"
+ETCD_DIR="$SRC_DIR/$ETCD_PACKAGE"
 
 
-ETCD_BASE=$(dirname ${ETCD_DIR})
-if [ ! -d ${ETCD_BASE} ]; then
-	mkdir -p ${ETCD_BASE}
+ETCD_BASE=$(dirname "${ETCD_DIR}")
+if [ ! -d "${ETCD_BASE}" ]; then
+	mkdir -p "${ETCD_BASE}"
 fi
 fi
 
 
-if [ ! -h ${ETCD_DIR} ]; then
-	ln -s ../../../ ${ETCD_DIR}
+if [ ! -h "${ETCD_DIR}" ]; then
+	ln -s ../../../ "${ETCD_DIR}"
 fi
 fi
 
 
 for i in third_party/*; do
 for i in third_party/*; do
-	if [ $i = "third_party/src" ]; then
+	if [ "$i" = "third_party/src" ]; then
 		continue
 		continue
 	fi
 	fi
-	cp -R $i src/
+	cp -R "$i" src/
 done
 done
 
 
 ./scripts/release-version > release_version.go
 ./scripts/release-version > release_version.go
-go build ${ETCD_PACKAGE}
+go build "${ETCD_PACKAGE}"

+ 2 - 0
etcd_handlers.go

@@ -49,6 +49,8 @@ func Multiplexer(w http.ResponseWriter, req *http.Request) error {
 		return GetHttpHandler(w, req)
 		return GetHttpHandler(w, req)
 	case "POST":
 	case "POST":
 		return SetHttpHandler(w, req)
 		return SetHttpHandler(w, req)
+	case "PUT":
+		return SetHttpHandler(w, req)
 	case "DELETE":
 	case "DELETE":
 		return DeleteHttpHandler(w, req)
 		return DeleteHttpHandler(w, req)
 	default:
 	default:

+ 88 - 80
store/store.go

@@ -326,75 +326,81 @@ func (s *Store) Get(key string) ([]byte, error) {
 	return json.Marshal(resps)
 	return json.Marshal(resps)
 }
 }
 
 
-func (s *Store) RawGet(key string) ([]*Response, error) {
-	// Update stats
-	s.BasicStats.Gets++
+func (s *Store) rawGetNode(key string, node *Node) ([]*Response, error) {
+	resps := make([]*Response, 1)
 
 
-	key = path.Clean("/" + key)
+	isExpire := !node.ExpireTime.Equal(PERMANENT)
 
 
-	nodes, keys, ok := s.Tree.list(key)
-
-	if ok {
+	resps[0] = &Response{
+		Action: "GET",
+		Index:  s.Index,
+		Key:    key,
+		Value:  node.Value,
+	}
 
 
-		node, ok := nodes.(*Node)
+	// Update ttl
+	if isExpire {
+		TTL := int64(node.ExpireTime.Sub(time.Now()) / time.Second)
+		resps[0].Expiration = &node.ExpireTime
+		resps[0].TTL = TTL
+	}
 
 
-		if ok {
-			resps := make([]*Response, 1)
+	return resps, nil
+}
 
 
-			isExpire := !node.ExpireTime.Equal(PERMANENT)
+func (s *Store) rawGetNodeList(key string, keys []string, nodes []*Node) ([]*Response, error) {
+	resps := make([]*Response, len(nodes))
 
 
-			resps[0] = &Response{
-				Action: "GET",
-				Index:  s.Index,
-				Key:    key,
-				Value:  node.Value,
-			}
+	// TODO: check if nodes and keys are the same length
+	for i := 0; i < len(nodes); i++ {
+		var TTL int64
+		var isExpire bool = false
 
 
-			// Update ttl
-			if isExpire {
-				TTL := int64(node.ExpireTime.Sub(time.Now()) / time.Second)
-				resps[0].Expiration = &node.ExpireTime
-				resps[0].TTL = TTL
-			}
+		isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
 
 
-			return resps, nil
+		resps[i] = &Response{
+			Action: "GET",
+			Index:  s.Index,
+			Key:    path.Join(key, keys[i]),
 		}
 		}
 
 
-		nodes, _ := nodes.([]*Node)
-
-		resps := make([]*Response, len(nodes))
-		for i := 0; i < len(nodes); i++ {
-
-			var TTL int64
-			var isExpire bool = false
+		if len(nodes[i].Value) != 0 {
+			resps[i].Value = nodes[i].Value
+		} else {
+			resps[i].Dir = true
+		}
 
 
-			isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
+		// Update ttl
+		if isExpire {
+			TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second)
+			resps[i].Expiration = &nodes[i].ExpireTime
+			resps[i].TTL = TTL
+		}
 
 
-			resps[i] = &Response{
-				Action: "GET",
-				Index:  s.Index,
-				Key:    path.Join(key, keys[i]),
-			}
+	}
 
 
-			if len(nodes[i].Value) != 0 {
-				resps[i].Value = nodes[i].Value
-			} else {
-				resps[i].Dir = true
-			}
+	return resps, nil
+}
 
 
-			// Update ttl
-			if isExpire {
-				TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second)
-				resps[i].Expiration = &nodes[i].ExpireTime
-				resps[i].TTL = TTL
-			}
+func (s *Store) RawGet(key string) ([]*Response, error) {
+	// Update stats
+	s.BasicStats.Gets++
 
 
-		}
+	key = path.Clean("/" + key)
 
 
-		return resps, nil
+	nodes, keys, ok := s.Tree.list(key)
+	if !ok {
+		return nil, etcdErr.NewError(100, "get: "+key)
 	}
 	}
 
 
-	return nil, etcdErr.NewError(100, "get: "+key)
+	switch node := nodes.(type) {
+	case *Node:
+		return s.rawGetNode(key, node)
+	case []*Node:
+		return s.rawGetNodeList(key, keys, node)
+	default:
+		panic("invalid cast ")
+	}
 }
 }
 
 
 func (s *Store) Delete(key string, index uint64) ([]byte, error) {
 func (s *Store) Delete(key string, index uint64) ([]byte, error) {
@@ -416,43 +422,41 @@ func (s *Store) internalDelete(key string, index uint64) ([]byte, error) {
 
 
 	node, ok := s.Tree.get(key)
 	node, ok := s.Tree.get(key)
 
 
-	if ok {
-
-		resp := Response{
-			Action:    "DELETE",
-			Key:       key,
-			PrevValue: node.Value,
-			Index:     index,
-		}
+	if !ok {
+		return nil, etcdErr.NewError(100, "delete: "+key)
+	}
 
 
-		if node.ExpireTime.Equal(PERMANENT) {
+	resp := Response{
+		Action:    "DELETE",
+		Key:       key,
+		PrevValue: node.Value,
+		Index:     index,
+	}
 
 
-			s.Tree.delete(key)
+	if node.ExpireTime.Equal(PERMANENT) {
 
 
-		} else {
-			resp.Expiration = &node.ExpireTime
-			// Kill the expire go routine
-			node.update <- PERMANENT
-			s.Tree.delete(key)
+		s.Tree.delete(key)
 
 
-		}
+	} else {
+		resp.Expiration = &node.ExpireTime
+		// Kill the expire go routine
+		node.update <- PERMANENT
+		s.Tree.delete(key)
 
 
-		msg, err := json.Marshal(resp)
+	}
 
 
-		s.watcher.notify(resp)
+	msg, err := json.Marshal(resp)
 
 
-		// notify the messager
-		if s.messager != nil && err == nil {
-			s.messager <- string(msg)
-		}
+	s.watcher.notify(resp)
 
 
-		s.addToResponseMap(index, &resp)
+	// notify the messager
+	if s.messager != nil && err == nil {
+		s.messager <- string(msg)
+	}
 
 
-		return msg, err
+	s.addToResponseMap(index, &resp)
 
 
-	} else {
-		return nil, etcdErr.NewError(100, "delete: "+key)
-	}
+	return msg, err
 }
 }
 
 
 // Set the value of the key to the value if the given prevValue is equal to the value of the key
 // Set the value of the key to the value if the given prevValue is equal to the value of the key
@@ -466,12 +470,16 @@ func (s *Store) TestAndSet(key string, prevValue string, value string, expireTim
 	resp := s.internalGet(key)
 	resp := s.internalGet(key)
 
 
 	if resp == nil {
 	if resp == nil {
-		return nil, etcdErr.NewError(100, "testandset: "+key)
+		if prevValue != "" {
+			errmsg := fmt.Sprintf("TestAndSet: key not found and previousValue is not empty %s:%s ", key, prevValue)
+			return nil, etcdErr.NewError(100, errmsg)
+		}
+		return s.internalSet(key, value, expireTime, index)
 	}
 	}
 
 
 	if resp.Value == prevValue {
 	if resp.Value == prevValue {
 
 
-		// If test success, do set
+		// If test succeed, do set
 		return s.internalSet(key, value, expireTime, index)
 		return s.internalSet(key, value, expireTime, index)
 	} else {
 	} else {
 
 

+ 36 - 0
store/store_test.go

@@ -31,6 +31,42 @@ func TestStoreGetDelete(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestTestAndSet(t *testing.T) {
+	s := CreateStore(100)
+	s.Set("foo", "bar", time.Unix(0, 0), 1)
+
+	_, err := s.TestAndSet("foo", "barbar", "barbar", time.Unix(0, 0), 2)
+
+	if err == nil {
+		t.Fatalf("test bar == barbar should fail")
+	}
+
+	_, err = s.TestAndSet("foo", "bar", "barbar", time.Unix(0, 0), 3)
+
+	if err != nil {
+		t.Fatalf("test bar == bar should succeed")
+	}
+
+	_, err = s.TestAndSet("foo", "", "barbar", time.Unix(0, 0), 4)
+
+	if err == nil {
+		t.Fatalf("test empty == bar should fail")
+	}
+
+	_, err = s.TestAndSet("fooo", "bar", "barbar", time.Unix(0, 0), 5)
+
+	if err == nil {
+		t.Fatalf("test bar == non-existing key should fail")
+	}
+
+	_, err = s.TestAndSet("fooo", "", "bar", time.Unix(0, 0), 6)
+
+	if err != nil {
+		t.Fatalf("test empty == non-existing key should succeed")
+	}
+
+}
+
 func TestSaveAndRecovery(t *testing.T) {
 func TestSaveAndRecovery(t *testing.T) {
 
 
 	s := CreateStore(100)
 	s := CreateStore(100)