mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge branch 'master' into moreStats
This commit is contained in:
@@ -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
22
build
@@ -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}"
|
||||
|
||||
@@ -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:
|
||||
|
||||
202
store/store.go
202
store/store.go
@@ -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 {
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user