mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #47 from xiangli-cmu/master
support version and basic stats
This commit is contained in:
commit
0310268d27
@ -131,7 +131,7 @@ func dispatch(c Command, w *http.ResponseWriter, req *http.Request, client bool)
|
|||||||
|
|
||||||
if body == nil {
|
if body == nil {
|
||||||
(*w).WriteHeader(http.StatusNotFound)
|
(*w).WriteHeader(http.StatusNotFound)
|
||||||
(*w).Write(newJsonError(100, err.Error()))
|
(*w).Write(newJsonError(300, "Empty result from raft"))
|
||||||
} else {
|
} else {
|
||||||
body, ok := body.([]byte)
|
body, ok := body.([]byte)
|
||||||
// this should not happen
|
// this should not happen
|
||||||
@ -225,6 +225,18 @@ func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler to return the current version of etcd
|
||||||
|
func VersionHttpHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(releaseVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler to return the basic stats of etcd
|
||||||
|
func StatsHttpHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(etcdStore.Stats())
|
||||||
|
}
|
||||||
|
|
||||||
// Get Handler
|
// Get Handler
|
||||||
func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
|
func GetHttpHandler(w *http.ResponseWriter, req *http.Request) {
|
||||||
key := req.URL.Path[len("/v1/keys/"):]
|
key := req.URL.Path[len("/v1/keys/"):]
|
||||||
|
13
command.go
13
command.go
@ -120,6 +120,13 @@ func (c *JoinCommand) CommandName() string {
|
|||||||
// Join a server to the cluster
|
// Join a server to the cluster
|
||||||
func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
|
func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
|
||||||
|
|
||||||
|
// check if the join command is from a previous machine, who lost all its previous log.
|
||||||
|
response, _ := etcdStore.RawGet(path.Join("_etcd/machines", c.Name))
|
||||||
|
|
||||||
|
if response != nil {
|
||||||
|
return []byte("join success"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// check machine number in the cluster
|
// check machine number in the cluster
|
||||||
num := machineNum()
|
num := machineNum()
|
||||||
if num == maxClusterSize {
|
if num == maxClusterSize {
|
||||||
@ -129,12 +136,8 @@ func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
|
|||||||
// add peer in raft
|
// add peer in raft
|
||||||
err := raftServer.AddPeer(c.Name)
|
err := raftServer.AddPeer(c.Name)
|
||||||
|
|
||||||
// add machine in etcd
|
|
||||||
addMachine(c.Name, c.Hostname, c.RaftPort, c.ClientPort)
|
|
||||||
|
|
||||||
// add machine in etcd storage
|
// add machine in etcd storage
|
||||||
nodeName := fmt.Sprintf("%s%d", "node", raftServer.CommitIndex())
|
key := path.Join("_etcd/machines", c.Name)
|
||||||
key := path.Join("_etcd/machines", nodeName)
|
|
||||||
value := fmt.Sprintf("%s,%d,%d", c.Hostname, c.RaftPort, c.ClientPort)
|
value := fmt.Sprintf("%s,%d,%d", c.Hostname, c.RaftPort, c.ClientPort)
|
||||||
etcdStore.Set(key, value, time.Unix(0, 0), raftServer.CommitIndex())
|
etcdStore.Set(key, value, time.Unix(0, 0), raftServer.CommitIndex())
|
||||||
|
|
||||||
|
1
error.go
1
error.go
@ -20,6 +20,7 @@ func init() {
|
|||||||
errors[201] = "PrevValue is Required in POST form"
|
errors[201] = "PrevValue is Required in POST form"
|
||||||
errors[202] = "The given TTL in POST form is not a number"
|
errors[202] = "The given TTL in POST form is not a number"
|
||||||
errors[203] = "The given index in POST form is not a number"
|
errors[203] = "The given index in POST form is not a number"
|
||||||
|
|
||||||
// raft related errors
|
// raft related errors
|
||||||
errors[300] = "Raft Internal Error"
|
errors[300] = "Raft Internal Error"
|
||||||
errors[301] = "During Leader Election"
|
errors[301] = "During Leader Election"
|
||||||
|
2
etcd.go
2
etcd.go
@ -434,6 +434,8 @@ func startClientTransport(port int, st int) {
|
|||||||
http.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
|
http.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
|
||||||
http.HandleFunc("/leader", LeaderHttpHandler)
|
http.HandleFunc("/leader", LeaderHttpHandler)
|
||||||
http.HandleFunc("/machines", MachinesHttpHandler)
|
http.HandleFunc("/machines", MachinesHttpHandler)
|
||||||
|
http.HandleFunc("/", VersionHttpHandler)
|
||||||
|
http.HandleFunc("/stats", StatsHttpHandler)
|
||||||
|
|
||||||
switch st {
|
switch st {
|
||||||
|
|
||||||
|
32
machines.go
32
machines.go
@ -2,34 +2,26 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type machine struct {
|
|
||||||
hostname string
|
|
||||||
raftPort int
|
|
||||||
clientPort int
|
|
||||||
}
|
|
||||||
|
|
||||||
var machinesMap = map[string]machine{}
|
|
||||||
|
|
||||||
func addMachine(name string, hostname string, raftPort int, clientPort int) {
|
|
||||||
|
|
||||||
machinesMap[name] = machine{hostname, raftPort, clientPort}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func getClientAddr(name string) (string, bool) {
|
func getClientAddr(name string) (string, bool) {
|
||||||
machine, ok := machinesMap[name]
|
response, _ := etcdStore.RawGet(path.Join("_etcd/machines", name))
|
||||||
if !ok {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := fmt.Sprintf("%s:%v", machine.hostname, machine.clientPort)
|
values := strings.Split(response[0].Value, ",")
|
||||||
|
|
||||||
|
hostname := values[0]
|
||||||
|
clientPort := values[2]
|
||||||
|
|
||||||
|
addr := fmt.Sprintf("%s:%s", hostname, clientPort)
|
||||||
|
|
||||||
return addr, true
|
return addr, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// machineNum returns the number of machines in the cluster
|
// machineNum returns the number of machines in the cluster
|
||||||
func machineNum() int {
|
func machineNum() int {
|
||||||
return len(machinesMap)
|
response, _ := etcdStore.RawGet("_etcd/machines")
|
||||||
|
|
||||||
|
return len(response)
|
||||||
}
|
}
|
||||||
|
25
store/stats.go
Normal file
25
store/stats.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EtcdStats struct {
|
||||||
|
// Number of get requests
|
||||||
|
Gets uint64 `json:"gets"`
|
||||||
|
|
||||||
|
// Number of sets requests
|
||||||
|
Sets uint64 `json:"sets"`
|
||||||
|
|
||||||
|
// Number of delete requests
|
||||||
|
Deletes uint64 `json:"deletes"`
|
||||||
|
|
||||||
|
// Number of testAndSet requests
|
||||||
|
TestAndSets uint64 `json:"testAndSets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats returns the basic statistics information of etcd storage
|
||||||
|
func (s *Store) Stats() []byte {
|
||||||
|
b, _ := json.Marshal(s.BasicStats)
|
||||||
|
return b
|
||||||
|
}
|
@ -42,6 +42,9 @@ type Store struct {
|
|||||||
|
|
||||||
// Current index of the raft machine
|
// Current index of the raft machine
|
||||||
Index uint64
|
Index uint64
|
||||||
|
|
||||||
|
// Basic statistics information of etcd storage
|
||||||
|
BasicStats EtcdStats
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Node represents a Value in the Key-Value pair in the store
|
// A Node represents a Value in the Key-Value pair in the store
|
||||||
@ -139,6 +142,9 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
|
|||||||
//Update index
|
//Update index
|
||||||
s.Index = index
|
s.Index = index
|
||||||
|
|
||||||
|
//Update stats
|
||||||
|
s.BasicStats.Sets++
|
||||||
|
|
||||||
key = path.Clean("/" + key)
|
key = path.Clean("/" + key)
|
||||||
|
|
||||||
isExpire := !expireTime.Equal(PERMANENT)
|
isExpire := !expireTime.Equal(PERMANENT)
|
||||||
@ -284,13 +290,29 @@ func (s *Store) internalGet(key string) *Response {
|
|||||||
// If key is a file return the file
|
// If key is a file return the file
|
||||||
// If key is a directory reuturn an array of files
|
// If key is a directory reuturn an array of files
|
||||||
func (s *Store) Get(key string) ([]byte, error) {
|
func (s *Store) Get(key string) ([]byte, error) {
|
||||||
|
resps, err := s.RawGet(key)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resps) == 1 {
|
||||||
|
return json.Marshal(resps[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(resps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) RawGet(key string) ([]*Response, error) {
|
||||||
|
// Update stats
|
||||||
|
s.BasicStats.Gets++
|
||||||
|
|
||||||
key = path.Clean("/" + key)
|
key = path.Clean("/" + key)
|
||||||
|
|
||||||
nodes, keys, dirs, ok := s.Tree.list(key)
|
nodes, keys, dirs, ok := s.Tree.list(key)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
resps := make([]Response, len(nodes))
|
resps := make([]*Response, len(nodes))
|
||||||
for i := 0; i < len(nodes); i++ {
|
for i := 0; i < len(nodes); i++ {
|
||||||
|
|
||||||
var TTL int64
|
var TTL int64
|
||||||
@ -298,7 +320,7 @@ func (s *Store) Get(key string) ([]byte, error) {
|
|||||||
|
|
||||||
isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
|
isExpire = !nodes[i].ExpireTime.Equal(PERMANENT)
|
||||||
|
|
||||||
resps[i] = Response{
|
resps[i] = &Response{
|
||||||
Action: "GET",
|
Action: "GET",
|
||||||
Index: s.Index,
|
Index: s.Index,
|
||||||
Key: path.Join(key, keys[i]),
|
Key: path.Join(key, keys[i]),
|
||||||
@ -318,10 +340,8 @@ func (s *Store) Get(key string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if len(resps) == 1 {
|
|
||||||
return json.Marshal(resps[0])
|
return resps, nil
|
||||||
}
|
|
||||||
return json.Marshal(resps)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := NotFoundError(key)
|
err := NotFoundError(key)
|
||||||
@ -331,9 +351,12 @@ func (s *Store) Get(key string) ([]byte, error) {
|
|||||||
// Delete the key
|
// Delete the key
|
||||||
func (s *Store) Delete(key string, index uint64) ([]byte, error) {
|
func (s *Store) Delete(key string, index uint64) ([]byte, error) {
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
s.BasicStats.Deletes++
|
||||||
|
|
||||||
key = path.Clean("/" + key)
|
key = path.Clean("/" + key)
|
||||||
|
|
||||||
//Update index
|
// Update index
|
||||||
s.Index = index
|
s.Index = index
|
||||||
|
|
||||||
node, ok := s.Tree.get(key)
|
node, ok := s.Tree.get(key)
|
||||||
@ -381,6 +404,9 @@ func (s *Store) Delete(key string, index uint64) ([]byte, error) {
|
|||||||
|
|
||||||
// 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
|
||||||
func (s *Store) TestAndSet(key string, prevValue string, value string, expireTime time.Time, index uint64) ([]byte, error) {
|
func (s *Store) TestAndSet(key string, prevValue string, value string, expireTime time.Time, index uint64) ([]byte, error) {
|
||||||
|
// Update stats
|
||||||
|
s.BasicStats.TestAndSets++
|
||||||
|
|
||||||
resp := s.internalGet(key)
|
resp := s.internalGet(key)
|
||||||
|
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v1"
|
var version = "v1"
|
||||||
|
|
||||||
|
var releaseVersion = "etcd pre-0.1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user