Merge branch 'master' into moreStats

This commit is contained in:
Xiang Li
2013-08-23 13:45:51 -04:00
5 changed files with 159 additions and 112 deletions

View File

@@ -30,6 +30,7 @@ You can build etcd from source:
```sh
git clone https://github.com/coreos/etcd
cd etcd
./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
```
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
{"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:
```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
@@ -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
```
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.
@@ -409,7 +410,7 @@ curl -L http://127.0.0.1:4002/v1/keys/foo
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
```
```

22
build
View File

@@ -1,25 +1,25 @@
#!/bin/bash
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
if [ ! -h ${ETCD_DIR} ]; then
ln -s ../../../ ${ETCD_DIR}
if [ ! -h "${ETCD_DIR}" ]; then
ln -s ../../../ "${ETCD_DIR}"
fi
for i in third_party/*; do
if [ $i = "third_party/src" ]; then
if [ "$i" = "third_party/src" ]; then
continue
fi
cp -R $i src/
cp -R "$i" src/
done
./scripts/release-version > release_version.go
go build ${ETCD_PACKAGE}
go build "${ETCD_PACKAGE}"

View File

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

View File

@@ -326,6 +326,62 @@ func (s *Store) Get(key string) ([]byte, error) {
return json.Marshal(resps)
}
func (s *Store) rawGetNode(key string, node *Node) ([]*Response, error) {
resps := make([]*Response, 1)
isExpire := !node.ExpireTime.Equal(PERMANENT)
resps[0] = &Response{
Action: "GET",
Index: s.Index,
Key: key,
Value: node.Value,
}
// Update ttl
if isExpire {
TTL := int64(node.ExpireTime.Sub(time.Now()) / time.Second)
resps[0].Expiration = &node.ExpireTime
resps[0].TTL = TTL
}
return resps, nil
}
func (s *Store) rawGetNodeList(key string, keys []string, nodes []*Node) ([]*Response, error) {
resps := make([]*Response, len(nodes))
// TODO: check if nodes and keys are the same length
for i := 0; i < len(nodes); i++ {
var TTL int64
var isExpire bool = false
isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
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
}
// Update ttl
if isExpire {
TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second)
resps[i].Expiration = &nodes[i].ExpireTime
resps[i].TTL = TTL
}
}
return resps, nil
}
func (s *Store) RawGet(key string) ([]*Response, error) {
// Update stats
s.BasicStats.Gets++
@@ -333,68 +389,18 @@ func (s *Store) RawGet(key string) ([]*Response, error) {
key = path.Clean("/" + key)
nodes, keys, ok := s.Tree.list(key)
if ok {
node, ok := nodes.(*Node)
if ok {
resps := make([]*Response, 1)
isExpire := !node.ExpireTime.Equal(PERMANENT)
resps[0] = &Response{
Action: "GET",
Index: s.Index,
Key: key,
Value: node.Value,
}
// Update ttl
if isExpire {
TTL := int64(node.ExpireTime.Sub(time.Now()) / time.Second)
resps[0].Expiration = &node.ExpireTime
resps[0].TTL = TTL
}
return resps, nil
}
nodes, _ := nodes.([]*Node)
resps := make([]*Response, len(nodes))
for i := 0; i < len(nodes); i++ {
var TTL int64
var isExpire bool = false
isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
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
}
// Update ttl
if isExpire {
TTL = int64(nodes[i].ExpireTime.Sub(time.Now()) / time.Second)
resps[i].Expiration = &nodes[i].ExpireTime
resps[i].TTL = TTL
}
}
return resps, nil
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) {
@@ -416,43 +422,41 @@ func (s *Store) internalDelete(key string, index uint64) ([]byte, error) {
node, ok := s.Tree.get(key)
if ok {
resp := Response{
Action: "DELETE",
Key: key,
PrevValue: node.Value,
Index: index,
}
if node.ExpireTime.Equal(PERMANENT) {
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)
// notify the messager
if s.messager != nil && err == nil {
s.messager <- string(msg)
}
s.addToResponseMap(index, &resp)
return msg, err
} else {
if !ok {
return nil, etcdErr.NewError(100, "delete: "+key)
}
resp := Response{
Action: "DELETE",
Key: key,
PrevValue: node.Value,
Index: index,
}
if node.ExpireTime.Equal(PERMANENT) {
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)
// notify the messager
if s.messager != nil && err == nil {
s.messager <- string(msg)
}
s.addToResponseMap(index, &resp)
return msg, err
}
// 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)
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 test success, do set
// If test succeed, do set
return s.internalSet(key, value, expireTime, index)
} else {

View File

@@ -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) {
s := CreateStore(100)