Browse Source

clean up from yifan

Xiang Li 12 years ago
parent
commit
da83ee223b
19 changed files with 443 additions and 586 deletions
  1. 10 10
      command.go
  2. 15 15
      error/error.go
  3. 3 3
      etcd.go
  4. 3 4
      etcd_handlers.go
  5. 0 146
      file_system/stats.go
  6. 0 221
      file_system/stats_test.go
  7. 1 1
      machines.go
  8. 1 1
      name_url_map.go
  9. 1 1
      raft_server.go
  10. 2 2
      store/event.go
  11. 1 1
      store/event_test.go
  12. 2 2
      store/node.go
  13. 91 0
      store/stats.go
  14. 139 0
      store/stats_test.go
  15. 75 75
      store/store.go
  16. 86 86
      store/store_test.go
  17. 7 12
      store/watcher.go
  18. 3 3
      store/watcher_test.go
  19. 3 3
      util.go

+ 10 - 10
command.go

@@ -9,7 +9,7 @@ import (
 	"time"
 
 	etcdErr "github.com/coreos/etcd/error"
-	"github.com/coreos/etcd/file_system"
+	"github.com/coreos/etcd/store"
 	"github.com/coreos/go-raft"
 )
 
@@ -39,7 +39,7 @@ func (c *CreateCommand) CommandName() string {
 
 // Create node
 func (c *CreateCommand) Apply(server *raft.Server) (interface{}, error) {
-	e, err := etcdFs.Create(c.Key, c.Value, c.ExpireTime, server.CommitIndex(), server.Term())
+	e, err := etcdStore.Create(c.Key, c.Value, c.ExpireTime, server.CommitIndex(), server.Term())
 
 	if err != nil {
 		debug(err)
@@ -63,7 +63,7 @@ func (c *UpdateCommand) CommandName() string {
 
 // Update node
 func (c *UpdateCommand) Apply(server *raft.Server) (interface{}, error) {
-	e, err := etcdFs.Update(c.Key, c.Value, c.ExpireTime, server.CommitIndex(), server.Term())
+	e, err := etcdStore.Update(c.Key, c.Value, c.ExpireTime, server.CommitIndex(), server.Term())
 
 	if err != nil {
 		debug(err)
@@ -89,7 +89,7 @@ func (c *TestAndSetCommand) CommandName() string {
 
 // Set the key-value pair if the current value of the key equals to the given prevValue
 func (c *TestAndSetCommand) Apply(server *raft.Server) (interface{}, error) {
-	e, err := etcdFs.TestAndSet(c.Key, c.PrevValue, c.PrevIndex,
+	e, err := etcdStore.TestAndSet(c.Key, c.PrevValue, c.PrevIndex,
 		c.Value, c.ExpireTime, server.CommitIndex(), server.Term())
 
 	if err != nil {
@@ -114,7 +114,7 @@ func (c *GetCommand) CommandName() string {
 
 // Get the value of key
 func (c *GetCommand) Apply(server *raft.Server) (interface{}, error) {
-	e, err := etcdFs.Get(c.Key, c.Recursive, c.Sorted, server.CommitIndex(), server.Term())
+	e, err := etcdStore.Get(c.Key, c.Recursive, c.Sorted, server.CommitIndex(), server.Term())
 
 	if err != nil {
 		debug(err)
@@ -137,7 +137,7 @@ func (c *DeleteCommand) CommandName() string {
 
 // Delete the key
 func (c *DeleteCommand) Apply(server *raft.Server) (interface{}, error) {
-	e, err := etcdFs.Delete(c.Key, c.Recursive, server.CommitIndex(), server.Term())
+	e, err := etcdStore.Delete(c.Key, c.Recursive, server.CommitIndex(), server.Term())
 
 	if err != nil {
 		debug(err)
@@ -160,7 +160,7 @@ func (c *WatchCommand) CommandName() string {
 }
 
 func (c *WatchCommand) Apply(server *raft.Server) (interface{}, error) {
-	eventChan, err := etcdFs.Watch(c.Key, c.Recursive, c.SinceIndex, server.CommitIndex(), server.Term())
+	eventChan, err := etcdStore.Watch(c.Key, c.Recursive, c.SinceIndex, server.CommitIndex(), server.Term())
 
 	if err != nil {
 		return nil, err
@@ -197,7 +197,7 @@ func (c *JoinCommand) CommandName() string {
 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.
-	e, _ := etcdFs.Get(path.Join("/_etcd/machines", c.Name), false, false, raftServer.CommitIndex(), raftServer.Term())
+	e, _ := etcdStore.Get(path.Join("/_etcd/machines", c.Name), false, false, raftServer.CommitIndex(), raftServer.Term())
 
 	b := make([]byte, 8)
 	binary.PutUvarint(b, raftServer.CommitIndex())
@@ -221,7 +221,7 @@ func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
 	// add machine in etcd storage
 	key := path.Join("_etcd/machines", c.Name)
 	value := fmt.Sprintf("raft=%s&etcd=%s&raftVersion=%s", c.RaftURL, c.EtcdURL, c.RaftVersion)
-	etcdFs.Create(key, value, fileSystem.Permanent, raftServer.CommitIndex(), raftServer.Term())
+	etcdStore.Create(key, value, store.Permanent, raftServer.CommitIndex(), raftServer.Term())
 
 	if c.Name != r.Name() { // do not add self to the peer list
 		r.peersStats[c.Name] = &raftPeerStats{MinLatency: 1 << 63}
@@ -250,7 +250,7 @@ func (c *RemoveCommand) Apply(raftServer *raft.Server) (interface{}, error) {
 	// remove machine in etcd storage
 	key := path.Join("_etcd/machines", c.Name)
 
-	_, err := etcdFs.Delete(key, false, raftServer.CommitIndex(), raftServer.Term())
+	_, err := etcdStore.Delete(key, false, raftServer.CommitIndex(), raftServer.Term())
 	delete(r.peersStats, c.Name)
 
 	if err != nil {

+ 15 - 15
error/error.go

@@ -32,27 +32,27 @@ func init() {
 	errors = make(map[int]string)
 
 	// command related errors
-	errors[100] = "Key Not Found"
-	errors[101] = "Test Failed" //test and set
-	errors[102] = "Not A File"
-	errors[103] = "Reached the max number of machines in the cluster"
-	errors[104] = "Not A Directory"
-	errors[105] = "Already exists" // create
-	errors[106] = "The prefix of given key is a keyword in etcd"
+	errors[EcodeKeyNotFound] = "Key Not Found"
+	errors[EcodeTestFailed] = "Test Failed" //test and set
+	errors[EcodeNotFile] = "Not A File"
+	errors[EcodeNoMoreMachine] = "Reached the max number of machines in the cluster"
+	errors[EcodeNotDir] = "Not A Directory"
+	errors[EcodeNodeExist] = "Already exists" // create
+	errors[EcodeKeyIsPreserved] = "The prefix of given key is a keyword in etcd"
 
 	// Post form related errors
-	errors[200] = "Value 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[203] = "The given index in POST form is not a number"
+	errors[EcodeValueRequired] = "Value is Required in POST form"
+	errors[EcodePrevValueRequired] = "PrevValue is Required in POST form"
+	errors[EcodeTTLNaN] = "The given TTL in POST form is not a number"
+	errors[EcodeIndexNaN] = "The given index in POST form is not a number"
 
 	// raft related errors
-	errors[300] = "Raft Internal Error"
-	errors[301] = "During Leader Election"
+	errors[EcodeRaftInternal] = "Raft Internal Error"
+	errors[EcodeLeaderElect] = "During Leader Election"
 
 	// etcd related errors
-	errors[400] = "watcher is cleared due to etcd recovery"
-	errors[401] = "The event in requested index is outdated and cleared"
+	errors[EcodeWatcherCleared] = "watcher is cleared due to etcd recovery"
+	errors[EcodeEventIndexCleared] = "The event in requested index is outdated and cleared"
 
 }
 

+ 3 - 3
etcd.go

@@ -10,7 +10,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/coreos/etcd/file_system"
+	"github.com/coreos/etcd/store"
 	"github.com/coreos/go-raft"
 )
 
@@ -136,7 +136,7 @@ type TLSConfig struct {
 //
 //------------------------------------------------------------------------------
 
-var etcdFs *fileSystem.FileSystem
+var etcdStore *store.Store
 
 //------------------------------------------------------------------------------
 //
@@ -204,7 +204,7 @@ func main() {
 	info := getInfo(dirPath)
 
 	// Create etcd key-value store
-	etcdFs = fileSystem.New()
+	etcdStore = store.New()
 
 	snapConf = newSnapshotConf()
 

+ 3 - 4
etcd_handlers.go

@@ -7,7 +7,7 @@ import (
 	"strings"
 
 	etcdErr "github.com/coreos/etcd/error"
-	"github.com/coreos/etcd/file_system"
+	"github.com/coreos/etcd/store"
 	"github.com/coreos/go-raft"
 )
 
@@ -120,7 +120,7 @@ func UpdateHttpHandler(w http.ResponseWriter, req *http.Request) error {
 	}
 
 	// TODO: update should give at least one option
-	if value == "" && expireTime.Sub(fileSystem.Permanent) == 0 {
+	if value == "" && expireTime.Sub(store.Permanent) == 0 {
 		return nil
 	}
 
@@ -223,8 +223,7 @@ func VersionHttpHandler(w http.ResponseWriter, req *http.Request) error {
 // Handler to return the basic stats of etcd
 func StatsHttpHandler(w http.ResponseWriter, req *http.Request) error {
 	w.WriteHeader(http.StatusOK)
-	//w.Write(etcdStore.Stats())
-	w.Write(etcdFs.Stats.GetStats())
+	w.Write(etcdStore.JsonStats())
 	w.Write(r.Stats())
 	return nil
 }

+ 0 - 146
file_system/stats.go

@@ -1,146 +0,0 @@
-package fileSystem
-
-import (
-	"encoding/json"
-	"sync"
-)
-
-const (
-	// Operations that will be running serializely
-	StatsSetsHit         = 100
-	StatsSetsMiss        = 101
-	StatsDeletesHit      = 102
-	StatsDeletesMiss     = 103
-	StatsUpdatesHit      = 104
-	StatsUpdatesMiss     = 105
-	StatsTestAndSetsHit  = 106
-	StatsTestAndSetsMiss = 107
-	StatsRecoveryHit     = 108
-	StatsRecoveryMiss    = 109
-
-	// concurrent operations
-	StatsGetsHit  = 200
-	StatsGetsMiss = 201
-
-	StatsWatchHit      = 300
-	StatsWatchMiss     = 301
-	StatsInWatchingNum = 302
-
-	StatsSaveHit  = 400
-	StatsSaveMiss = 401
-)
-
-type EtcdStats struct {
-
-	// Lock for synchronization
-	rwlock sync.RWMutex
-
-	// Number of get requests
-	GetsHit  uint64 `json:"gets_hits"`
-	GetsMiss uint64 `json:"gets_misses"`
-
-	// Number of sets requests
-	SetsHit  uint64 `json:"sets_hits"`
-	SetsMiss uint64 `json:"sets_misses"`
-
-	// Number of delete requests
-	DeletesHit  uint64 `json:"deletes_hits"`
-	DeletesMiss uint64 `json:"deletes_misses"`
-
-	// Number of update requests
-	UpdatesHit  uint64 `json:"updates_hits"`
-	UpdatesMiss uint64 `json:"updates_misses"`
-
-	// Number of testAndSet requests
-	TestAndSetsHit  uint64 `json:"testAndSets_hits"`
-	TestAndSetsMiss uint64 `json:"testAndSets_misses"`
-
-	// Number of Watch requests
-	WatchHit      uint64 `json:"watch_hit"`
-	WatchMiss     uint64 `json:"watch_miss"`
-	InWatchingNum uint64 `json:"in_watching_number"`
-
-	// Number of save requests
-	SaveHit  uint64 `json:"save_hit"`
-	SaveMiss uint64 `json:"save_miss"`
-
-	// Number of recovery requests
-	RecoveryHit  uint64 `json:"recovery_hit"`
-	RecoveryMiss uint64 `json:"recovery_miss"`
-}
-
-func newStats() *EtcdStats {
-	e := new(EtcdStats)
-	return e
-}
-
-// Status() return the statistics info of etcd storage its recent start
-func (e *EtcdStats) GetStats() []byte {
-	b, _ := json.Marshal(e)
-	return b
-}
-
-func (e *EtcdStats) TotalReads() uint64 {
-	return e.GetsHit + e.GetsMiss
-}
-
-func (e *EtcdStats) TotalWrites() uint64 {
-	return e.SetsHit + e.SetsMiss +
-		e.DeletesHit + e.DeletesMiss +
-		e.UpdatesHit + e.UpdatesMiss +
-		e.TestAndSetsHit + e.TestAndSetsMiss
-}
-
-func (e *EtcdStats) IncStats(field int) {
-	if field >= 200 {
-		e.rwlock.Lock()
-
-		switch field {
-		case StatsGetsHit:
-			e.GetsHit++
-		case StatsGetsMiss:
-			e.GetsMiss++
-		case StatsWatchHit:
-			e.WatchHit++
-		case StatsWatchMiss:
-			e.WatchMiss++
-		case StatsInWatchingNum:
-			e.InWatchingNum++
-		case StatsSaveHit:
-			e.SaveHit++
-		case StatsSaveMiss:
-			e.SaveMiss++
-		}
-
-		e.rwlock.Unlock()
-
-	} else {
-		e.rwlock.RLock()
-
-		switch field {
-		case StatsSetsHit:
-			e.SetsHit++
-		case StatsSetsMiss:
-			e.SetsMiss++
-		case StatsDeletesHit:
-			e.DeletesHit++
-		case StatsDeletesMiss:
-			e.DeletesMiss++
-		case StatsUpdatesHit:
-			e.UpdatesHit++
-		case StatsUpdatesMiss:
-			e.UpdatesMiss++
-		case StatsTestAndSetsHit:
-			e.TestAndSetsHit++
-		case StatsTestAndSetsMiss:
-			e.TestAndSetsMiss++
-		case StatsRecoveryHit:
-			e.RecoveryHit++
-		case StatsRecoveryMiss:
-			e.RecoveryMiss++
-		}
-
-		e.rwlock.RUnlock()
-	}
-
-}

+ 0 - 221
file_system/stats_test.go

@@ -1,221 +0,0 @@
-package fileSystem
-
-import (
-	"math/rand"
-	"testing"
-	"time"
-	//"fmt"
-)
-
-func TestBasicStats(t *testing.T) {
-	fs := New()
-	keys := GenKeys(rand.Intn(100), 5)
-
-	i := uint64(0)
-	GetsHit := uint64(0)
-	GetsMiss := uint64(0)
-	SetsHit := uint64(0)
-	SetsMiss := uint64(0)
-	DeletesHit := uint64(0)
-	DeletesMiss := uint64(0)
-	UpdatesHit := uint64(0)
-	UpdatesMiss := uint64(0)
-	TestAndSetsHit := uint64(0)
-	TestAndSetsMiss := uint64(0)
-	WatchHit := uint64(0)
-	WatchMiss := uint64(0)
-	InWatchingNum := uint64(0)
-	SaveHit := uint64(0)
-	SaveMiss := uint64(0)
-	RecoveryHit := uint64(0)
-	RecoveryMiss := uint64(0)
-
-	for _, k := range keys {
-		i++
-		_, err := fs.Create(k, "bar", time.Now().Add(time.Second*time.Duration(rand.Intn(10))), i, 1)
-		if err != nil {
-			SetsMiss++
-		} else {
-			SetsHit++
-		}
-	}
-
-	for _, k := range keys {
-		_, err := fs.Get(k, false, false, i, 1)
-		if err != nil {
-			GetsMiss++
-		} else {
-			GetsHit++
-		}
-	}
-
-	for _, k := range keys {
-		i++
-		_, err := fs.Update(k, "foo", time.Now().Add(time.Second*time.Duration(rand.Intn(5))), i, 1)
-		if err != nil {
-			UpdatesMiss++
-		} else {
-			UpdatesHit++
-		}
-	}
-
-	for _, k := range keys {
-		_, err := fs.Get(k, false, false, i, 1)
-		if err != nil {
-			GetsMiss++
-		} else {
-			GetsHit++
-		}
-	}
-
-	for _, k := range keys {
-		i++
-		_, err := fs.TestAndSet(k, "foo", 0, "bar", Permanent, i, 1)
-		if err != nil {
-			TestAndSetsMiss++
-		} else {
-			TestAndSetsHit++
-		}
-	}
-
-	//fmt.Printf("#TestAndSet [%d]\n", TestAndSetsHit)
-
-	for _, k := range keys {
-		_, err := fs.Watch(k, false, 0, i, 1)
-		if err != nil {
-			WatchMiss++
-		} else {
-			WatchHit++
-			InWatchingNum++
-		}
-	}
-
-	//fmt.Printf("#Watch [%d]\n", WatchHit)
-
-	for _, k := range keys {
-		_, err := fs.Get(k, false, false, i, 1)
-		if err != nil {
-			GetsMiss++
-		} else {
-			GetsHit++
-		}
-	}
-
-	//fmt.Println("fs.index ", fs.Index)
-	for j := 0; j < 5; j++ {
-		b := make([]byte, 10)
-		err := fs.Recovery(b)
-		if err != nil {
-			RecoveryMiss++
-		}
-
-		b, err = fs.Save()
-		if err != nil {
-			SaveMiss++
-		} else {
-			SaveHit++
-		}
-
-		err = fs.Recovery(b)
-		if err != nil {
-			RecoveryMiss++
-		} else {
-			RecoveryHit++
-		}
-	}
-	//fmt.Println("fs.index after ", fs.Index)
-	//fmt.Println("stats.inwatching ", fs.Stats.InWatchingNum)
-
-	for _, k := range keys {
-		i++
-		_, err := fs.Delete(k, false, i, 1)
-		if err != nil {
-			DeletesMiss++
-		} else {
-			InWatchingNum--
-			DeletesHit++
-		}
-	}
-
-	//fmt.Printf("#Delete [%d] stats.deletehit [%d] \n", DeletesHit, fs.Stats.DeletesHit)
-
-	for _, k := range keys {
-		_, err := fs.Get(k, false, false, i, 1)
-		if err != nil {
-			GetsMiss++
-		} else {
-			GetsHit++
-		}
-	}
-
-	if GetsHit != fs.Stats.GetsHit {
-		t.Fatalf("GetsHit [%d] != Stats.GetsHit [%d]", GetsHit, fs.Stats.GetsHit)
-	}
-
-	if GetsMiss != fs.Stats.GetsMiss {
-		t.Fatalf("GetsMiss [%d] != Stats.GetsMiss [%d]", GetsMiss, fs.Stats.GetsMiss)
-	}
-
-	if SetsHit != fs.Stats.SetsHit {
-		t.Fatalf("SetsHit [%d] != Stats.SetsHit [%d]", SetsHit, fs.Stats.SetsHit)
-	}
-
-	if SetsMiss != fs.Stats.SetsMiss {
-		t.Fatalf("SetsMiss [%d] != Stats.SetsMiss [%d]", SetsMiss, fs.Stats.SetsMiss)
-	}
-
-	if DeletesHit != fs.Stats.DeletesHit {
-		t.Fatalf("DeletesHit [%d] != Stats.DeletesHit [%d]", DeletesHit, fs.Stats.DeletesHit)
-	}
-
-	if DeletesMiss != fs.Stats.DeletesMiss {
-		t.Fatalf("DeletesMiss [%d] != Stats.DeletesMiss [%d]", DeletesMiss, fs.Stats.DeletesMiss)
-	}
-
-	if UpdatesHit != fs.Stats.UpdatesHit {
-		t.Fatalf("UpdatesHit [%d] != Stats.UpdatesHit [%d]", UpdatesHit, fs.Stats.UpdatesHit)
-	}
-
-	if UpdatesMiss != fs.Stats.UpdatesMiss {
-		t.Fatalf("UpdatesMiss [%d] != Stats.UpdatesMiss [%d]", UpdatesMiss, fs.Stats.UpdatesMiss)
-	}
-
-	if TestAndSetsHit != fs.Stats.TestAndSetsHit {
-		t.Fatalf("TestAndSetsHit [%d] != Stats.TestAndSetsHit [%d]", TestAndSetsHit, fs.Stats.TestAndSetsHit)
-	}
-
-	if TestAndSetsMiss != fs.Stats.TestAndSetsMiss {
-		t.Fatalf("TestAndSetsMiss [%d] != Stats.TestAndSetsMiss [%d]", TestAndSetsMiss, fs.Stats.TestAndSetsMiss)
-	}
-
-	if SaveHit != fs.Stats.SaveHit {
-		t.Fatalf("SaveHit [%d] != Stats.SaveHit [%d]", SaveHit, fs.Stats.SaveHit)
-	}
-
-	if SaveMiss != fs.Stats.SaveMiss {
-		t.Fatalf("SaveMiss [%d] != Stats.SaveMiss [%d]", SaveMiss, fs.Stats.SaveMiss)
-	}
-
-	if WatchHit != fs.Stats.WatchHit {
-		t.Fatalf("WatchHit [%d] != Stats.WatchHit [%d]", WatchHit, fs.Stats.WatchHit)
-	}
-
-	if WatchMiss != fs.Stats.WatchMiss {
-		t.Fatalf("WatchMiss [%d] != Stats.WatchMiss [%d]", WatchMiss, fs.Stats.WatchMiss)
-	}
-
-	if InWatchingNum != fs.Stats.InWatchingNum {
-		t.Fatalf("InWatchingNum [%d] != Stats.InWatchingNum [%d]", InWatchingNum, fs.Stats.InWatchingNum)
-	}
-
-	if RecoveryHit != fs.Stats.RecoveryHit {
-		t.Fatalf("RecoveryHit [%d] != Stats.RecoveryHit [%d]", RecoveryHit, fs.Stats.RecoveryHit)
-	}
-
-	if RecoveryMiss != fs.Stats.RecoveryMiss {
-		t.Fatalf("RecoveryMiss [%d] != Stats.RecoveryMiss [%d]", RecoveryMiss, fs.Stats.RecoveryMiss)
-	}
-
-	//fmt.Println(GetsHit, GetsMiss, SetsHit, SetsMiss, DeletesHit, DeletesMiss, UpdatesHit, UpdatesMiss, TestAndSetsHit, TestAndSetsMiss, WatchHit, WatchMiss, InWatchingNum, SaveHit, SaveMiss, RecoveryHit, RecoveryMiss)
-
-}

+ 1 - 1
machines.go

@@ -2,7 +2,7 @@ package main
 
 // machineNum returns the number of machines in the cluster
 func machineNum() int {
-	e, err := etcdFs.Get("/_etcd/machines", false, false, r.CommitIndex(), r.Term())
+	e, err := etcdStore.Get("/_etcd/machines", false, false, r.CommitIndex(), r.Term())
 
 	if err != nil {
 		return 0

+ 1 - 1
name_url_map.go

@@ -56,7 +56,7 @@ func readURL(nodeName string, urlName string) (string, bool) {
 	// convert nodeName to url from etcd storage
 	key := path.Join("/_etcd/machines", nodeName)
 
-	e, err := etcdFs.Get(key, false, false, r.CommitIndex(), r.Term())
+	e, err := etcdStore.Get(key, false, false, r.CommitIndex(), r.Term())
 
 	if err != nil {
 		return "", false

+ 1 - 1
raft_server.go

@@ -36,7 +36,7 @@ func newRaftServer(name string, url string, listenHost string, tlsConf *TLSConfi
 	raftTransporter := newTransporter(tlsConf.Scheme, tlsConf.Client, ElectionTimeout)
 
 	// Create raft server
-	server, err := raft.NewServer(name, dirPath, raftTransporter, etcdFs, nil)
+	server, err := raft.NewServer(name, dirPath, raftTransporter, etcdStore, nil)
 
 	check(err)
 

+ 2 - 2
file_system/event.go → store/event.go

@@ -1,4 +1,4 @@
-package fileSystem
+package store
 
 import (
 	"fmt"
@@ -6,7 +6,7 @@ import (
 	"sync"
 	"time"
 
-	etcdErr "github.com/xiangli-cmu/etcd/error"
+	etcdErr "github.com/coreos/etcd/error"
 )
 
 const (

+ 1 - 1
file_system/event_test.go → store/event_test.go

@@ -1,4 +1,4 @@
-package fileSystem
+package store
 
 import (
 	"testing"

+ 2 - 2
file_system/node.go → store/node.go

@@ -1,4 +1,4 @@
-package fileSystem
+package store
 
 import (
 	"path"
@@ -6,7 +6,7 @@ import (
 	"sync"
 	"time"
 
-	etcdErr "github.com/xiangli-cmu/etcd/error"
+	etcdErr "github.com/coreos/etcd/error"
 )
 
 var (

+ 91 - 0
store/stats.go

@@ -0,0 +1,91 @@
+package store
+
+import (
+	"encoding/json"
+	"sync/atomic"
+)
+
+const (
+	SetSuccess        = 100
+	SetFail           = 101
+	DeleteSuccess     = 102
+	DeleteFail        = 103
+	UpdateSuccess     = 104
+	UpdateFail        = 105
+	TestAndSetSuccess = 106
+	TestAndSetFail    = 107
+	GetSuccess        = 110
+	GetFail           = 111
+)
+
+type Stats struct {
+
+	// Number of get requests
+	GetSuccess uint64 `json:"getsSuccess"`
+	GetFail    uint64 `json:"getsFail"`
+
+	// Number of sets requests
+	SetSuccess uint64 `json:"setsSuccess"`
+	SetFail    uint64 `json:"setsFail"`
+
+	// Number of delete requests
+	DeleteSuccess uint64 `json:"deleteSuccess"`
+	DeleteFail    uint64 `json:"deleteFail"`
+
+	// Number of update requests
+	UpdateSuccess uint64 `json:"updateSuccess"`
+	UpdateFail    uint64 `json:"updateFail"`
+
+	// Number of testAndSet requests
+	TestAndSetSuccess uint64 `json:"testAndSetSuccess"`
+	TestAndSetFail    uint64 `json:"testAndSetFail"`
+
+	Watchers uint64 `json:"watchers"`
+}
+
+func newStats() *Stats {
+	s := new(Stats)
+	return s
+}
+
+// Status() return the statistics info of etcd storage its recent start
+func (s *Stats) toJson() []byte {
+	b, _ := json.Marshal(s)
+	return b
+}
+
+func (s *Stats) TotalReads() uint64 {
+	return s.GetSuccess + s.GetFail
+}
+
+func (s *Stats) TotalWrites() uint64 {
+	return s.SetSuccess + s.SetFail +
+		s.DeleteSuccess + s.DeleteFail +
+		s.TestAndSetSuccess + s.TestAndSetFail +
+		s.UpdateSuccess + s.UpdateFail
+}
+
+func (s *Stats) Inc(field int) {
+	switch field {
+	case SetSuccess:
+		atomic.AddUint64(&s.SetSuccess, 1)
+	case SetFail:
+		atomic.AddUint64(&s.SetFail, 1)
+	case DeleteSuccess:
+		atomic.AddUint64(&s.DeleteSuccess, 1)
+	case DeleteFail:
+		atomic.AddUint64(&s.DeleteFail, 1)
+	case GetSuccess:
+		atomic.AddUint64(&s.GetSuccess, 1)
+	case GetFail:
+		atomic.AddUint64(&s.GetFail, 1)
+	case UpdateSuccess:
+		atomic.AddUint64(&s.UpdateSuccess, 1)
+	case UpdateFail:
+		atomic.AddUint64(&s.UpdateFail, 1)
+	case TestAndSetSuccess:
+		atomic.AddUint64(&s.TestAndSetSuccess, 1)
+	case TestAndSetFail:
+		atomic.AddUint64(&s.TestAndSetFail, 1)
+	}
+}

+ 139 - 0
store/stats_test.go

@@ -0,0 +1,139 @@
+package store
+
+import (
+	"math/rand"
+	"testing"
+	"time"
+)
+
+func TestBasicStats(t *testing.T) {
+	s := New()
+	keys := GenKeys(rand.Intn(100), 5)
+
+	var i uint64
+	var GetSuccess, GetFail, SetSuccess, SetFail, DeleteSuccess, DeleteFail uint64
+	var UpdateSuccess, UpdateFail, TestAndSetSuccess, TestAndSetFail, watcher_number uint64
+
+	for _, k := range keys {
+		i++
+		_, err := s.Create(k, "bar", time.Now().Add(time.Second*time.Duration(rand.Intn(10))), i, 1)
+		if err != nil {
+			SetFail++
+		} else {
+			SetSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		_, err := s.Get(k, false, false, i, 1)
+		if err != nil {
+			GetFail++
+		} else {
+			GetSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		i++
+		_, err := s.Update(k, "foo", time.Now().Add(time.Second*time.Duration(rand.Intn(5))), i, 1)
+		if err != nil {
+			UpdateFail++
+		} else {
+			UpdateSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		_, err := s.Get(k, false, false, i, 1)
+		if err != nil {
+			GetFail++
+		} else {
+			GetSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		i++
+		_, err := s.TestAndSet(k, "foo", 0, "bar", Permanent, i, 1)
+		if err != nil {
+			TestAndSetFail++
+		} else {
+			TestAndSetSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		s.Watch(k, false, 0, i, 1)
+		watcher_number++
+	}
+
+	for _, k := range keys {
+		_, err := s.Get(k, false, false, i, 1)
+		if err != nil {
+			GetFail++
+		} else {
+			GetSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		i++
+		_, err := s.Delete(k, false, i, 1)
+		if err != nil {
+			DeleteFail++
+		} else {
+			watcher_number--
+			DeleteSuccess++
+		}
+	}
+
+	for _, k := range keys {
+		_, err := s.Get(k, false, false, i, 1)
+		if err != nil {
+			GetFail++
+		} else {
+			GetSuccess++
+		}
+	}
+
+	if GetSuccess != s.Stats.GetSuccess {
+		t.Fatalf("GetSuccess [%d] != Stats.GetSuccess [%d]", GetSuccess, s.Stats.GetSuccess)
+	}
+
+	if GetFail != s.Stats.GetFail {
+		t.Fatalf("GetFail [%d] != Stats.GetFail [%d]", GetFail, s.Stats.GetFail)
+	}
+
+	if SetSuccess != s.Stats.SetSuccess {
+		t.Fatalf("SetSuccess [%d] != Stats.SetSuccess [%d]", SetSuccess, s.Stats.SetSuccess)
+	}
+
+	if SetFail != s.Stats.SetFail {
+		t.Fatalf("SetFail [%d] != Stats.SetFail [%d]", SetFail, s.Stats.SetFail)
+	}
+
+	if DeleteSuccess != s.Stats.DeleteSuccess {
+		t.Fatalf("DeleteSuccess [%d] != Stats.DeleteSuccess [%d]", DeleteSuccess, s.Stats.DeleteSuccess)
+	}
+
+	if DeleteFail != s.Stats.DeleteFail {
+		t.Fatalf("DeleteFail [%d] != Stats.DeleteFail [%d]", DeleteFail, s.Stats.DeleteFail)
+	}
+
+	if UpdateSuccess != s.Stats.UpdateSuccess {
+		t.Fatalf("UpdateSuccess [%d] != Stats.UpdateSuccess [%d]", UpdateSuccess, s.Stats.UpdateSuccess)
+	}
+
+	if UpdateFail != s.Stats.UpdateFail {
+		t.Fatalf("UpdateFail [%d] != Stats.UpdateFail [%d]", UpdateFail, s.Stats.UpdateFail)
+	}
+
+	if TestAndSetSuccess != s.Stats.TestAndSetSuccess {
+		t.Fatalf("TestAndSetSuccess [%d] != Stats.TestAndSetSuccess [%d]", TestAndSetSuccess, s.Stats.TestAndSetSuccess)
+	}
+
+	if TestAndSetFail != s.Stats.TestAndSetFail {
+		t.Fatalf("TestAndSetFail [%d] != Stats.TestAndSetFail [%d]", TestAndSetFail, s.Stats.TestAndSetFail)
+	}
+
+}

+ 75 - 75
file_system/file_system.go → store/store.go

@@ -1,4 +1,4 @@
-package fileSystem
+package store
 
 import (
 	"encoding/json"
@@ -8,34 +8,34 @@ import (
 	"strings"
 	"time"
 
-	etcdErr "github.com/xiangli-cmu/etcd/error"
+	etcdErr "github.com/coreos/etcd/error"
 )
 
-type FileSystem struct {
+type Store struct {
 	Root       *Node
 	WatcherHub *watcherHub
 	Index      uint64
 	Term       uint64
-	Stats      *EtcdStats
+	Stats      *Stats
 }
 
-func New() *FileSystem {
-	fs := new(FileSystem)
-	fs.Root = newDir("/", 0, 0, nil, "", Permanent)
-	fs.Stats = newStats()
-	fs.WatcherHub = newWatchHub(1000, fs.Stats)
+func New() *Store {
+	s := new(Store)
+	s.Root = newDir("/", 0, 0, nil, "", Permanent)
+	s.Stats = newStats()
+	s.WatcherHub = newWatchHub(1000)
 
-	return fs
+	return s
 
 }
 
-func (fs *FileSystem) Get(nodePath string, recursive, sorted bool, index uint64, term uint64) (*Event, error) {
+func (s *Store) Get(nodePath string, recursive, sorted bool, index uint64, term uint64) (*Event, error) {
 	nodePath = path.Clean(path.Join("/", nodePath))
 
-	n, err := fs.InternalGet(nodePath, index, term)
+	n, err := s.InternalGet(nodePath, index, term)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsGetsMiss)
+		s.Stats.Inc(GetFail)
 		return nil, err
 	}
 
@@ -82,61 +82,62 @@ func (fs *FileSystem) Get(nodePath string, recursive, sorted bool, index uint64,
 		e.TTL = int64(n.ExpireTime.Sub(time.Now())/time.Second) + 1
 	}
 
-	fs.Stats.IncStats(StatsGetsHit)
+	s.Stats.Inc(GetSuccess)
+
 	return e, nil
 }
 
 // Create function creates the Node at nodePath. Create will help to create intermediate directories with no ttl.
 // If the node has already existed, create will fail.
 // If any node on the path is a file, create will fail.
-func (fs *FileSystem) Create(nodePath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
+func (s *Store) Create(nodePath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
 	nodePath = path.Clean(path.Join("/", nodePath))
 
 	// make sure we can create the node
-	_, err := fs.InternalGet(nodePath, index, term)
+	_, err := s.InternalGet(nodePath, index, term)
 
 	if err == nil { // key already exists
-		fs.Stats.IncStats(StatsSetsMiss)
+		s.Stats.Inc(SetFail)
 		return nil, etcdErr.NewError(etcdErr.EcodeNodeExist, nodePath)
 	}
 
 	etcdError, _ := err.(etcdErr.Error)
 
 	if etcdError.ErrorCode == 104 { // we cannot create the key due to meet a file while walking
-		fs.Stats.IncStats(StatsSetsMiss)
+		s.Stats.Inc(SetFail)
 		return nil, err
 	}
 
 	dir, _ := path.Split(nodePath)
 
 	// walk through the nodePath, create dirs and get the last directory node
-	d, err := fs.walk(dir, fs.checkDir)
+	d, err := s.walk(dir, s.checkDir)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsSetsMiss)
+		s.Stats.Inc(SetFail)
 		return nil, err
 	}
 
-	e := newEvent(Create, nodePath, fs.Index, fs.Term)
+	e := newEvent(Create, nodePath, s.Index, s.Term)
 
 	var n *Node
 
 	if len(value) != 0 { // create file
 		e.Value = value
 
-		n = newFile(nodePath, value, fs.Index, fs.Term, d, "", expireTime)
+		n = newFile(nodePath, value, s.Index, s.Term, d, "", expireTime)
 
 	} else { // create directory
 		e.Dir = true
 
-		n = newDir(nodePath, fs.Index, fs.Term, d, "", expireTime)
+		n = newDir(nodePath, s.Index, s.Term, d, "", expireTime)
 
 	}
 
 	err = d.Add(n)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsSetsMiss)
+		s.Stats.Inc(SetFail)
 		return nil, err
 	}
 
@@ -147,28 +148,28 @@ func (fs *FileSystem) Create(nodePath string, value string, expireTime time.Time
 		e.TTL = int64(expireTime.Sub(time.Now())/time.Second) + 1
 	}
 
-	fs.WatcherHub.notify(e)
-	fs.Stats.IncStats(StatsSetsHit)
+	s.WatcherHub.notify(e)
+	s.Stats.Inc(SetSuccess)
 	return e, nil
 }
 
 // Update function updates the value/ttl of the node.
 // If the node is a file, the value and the ttl can be updated.
 // If the node is a directory, only the ttl can be updated.
-func (fs *FileSystem) Update(nodePath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
-	n, err := fs.InternalGet(nodePath, index, term)
+func (s *Store) Update(nodePath string, value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
+	n, err := s.InternalGet(nodePath, index, term)
 
 	if err != nil { // if the node does not exist, return error
-		fs.Stats.IncStats(StatsUpdatesMiss)
+		s.Stats.Inc(UpdateFail)
 		return nil, err
 	}
 
-	e := newEvent(Update, nodePath, fs.Index, fs.Term)
+	e := newEvent(Update, nodePath, s.Index, s.Term)
 
 	if n.IsDir() { // if the node is a directory, we can only update ttl
 
 		if len(value) != 0 {
-			fs.Stats.IncStats(StatsUpdatesMiss)
+			s.Stats.Inc(UpdateFail)
 			return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath)
 		}
 
@@ -194,23 +195,23 @@ func (fs *FileSystem) Update(nodePath string, value string, expireTime time.Time
 		e.TTL = int64(expireTime.Sub(time.Now())/time.Second) + 1
 	}
 
-	fs.WatcherHub.notify(e)
-	fs.Stats.IncStats(StatsUpdatesHit)
+	s.WatcherHub.notify(e)
+	s.Stats.Inc(UpdateSuccess)
 	return e, nil
 }
 
-func (fs *FileSystem) TestAndSet(nodePath string, prevValue string, prevIndex uint64,
+func (s *Store) TestAndSet(nodePath string, prevValue string, prevIndex uint64,
 	value string, expireTime time.Time, index uint64, term uint64) (*Event, error) {
 
-	f, err := fs.InternalGet(nodePath, index, term)
+	f, err := s.InternalGet(nodePath, index, term)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsTestAndSetsMiss)
+		s.Stats.Inc(TestAndSetFail)
 		return nil, err
 	}
 
 	if f.IsDir() { // can only test and set file
-		fs.Stats.IncStats(StatsTestAndSetsMiss)
+		s.Stats.Inc(TestAndSetFail)
 		return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath)
 	}
 
@@ -221,23 +222,23 @@ func (fs *FileSystem) TestAndSet(nodePath string, prevValue string, prevIndex ui
 		e.Value = value
 		f.Write(value, index, term)
 
-		fs.WatcherHub.notify(e)
-		fs.Stats.IncStats(StatsTestAndSetsHit)
+		s.WatcherHub.notify(e)
+		s.Stats.Inc(TestAndSetSuccess)
 		return e, nil
 	}
 
 	cause := fmt.Sprintf("[%v != %v] [%v != %v]", prevValue, f.Value, prevIndex, f.ModifiedIndex)
-	fs.Stats.IncStats(StatsTestAndSetsMiss)
+	s.Stats.Inc(TestAndSetFail)
 	return nil, etcdErr.NewError(etcdErr.EcodeTestFailed, cause)
 }
 
 // Delete function deletes the node at the given path.
 // If the node is a directory, recursive must be true to delete it.
-func (fs *FileSystem) Delete(nodePath string, recursive bool, index uint64, term uint64) (*Event, error) {
-	n, err := fs.InternalGet(nodePath, index, term)
+func (s *Store) Delete(nodePath string, recursive bool, index uint64, term uint64) (*Event, error) {
+	n, err := s.InternalGet(nodePath, index, term)
 
 	if err != nil { // if the node does not exist, return error
-		fs.Stats.IncStats(StatsDeletesMiss)
+		s.Stats.Inc(DeleteFail)
 		return nil, err
 	}
 
@@ -250,37 +251,37 @@ func (fs *FileSystem) Delete(nodePath string, recursive bool, index uint64, term
 	}
 
 	callback := func(path string) { // notify function
-		fs.WatcherHub.notifyWithPath(e, path, true)
+		s.WatcherHub.notifyWithPath(e, path, true)
 	}
 
 	err = n.Remove(recursive, callback)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsDeletesMiss)
+		s.Stats.Inc(DeleteFail)
 		return nil, err
 	}
 
-	fs.WatcherHub.notify(e)
-	fs.Stats.IncStats(StatsDeletesHit)
+	s.WatcherHub.notify(e)
+	s.Stats.Inc(DeleteSuccess)
 
 	return e, nil
 }
 
-func (fs *FileSystem) Watch(prefix string, recursive bool, sinceIndex uint64, index uint64, term uint64) (<-chan *Event, error) {
-	fs.Index, fs.Term = index, term
+func (s *Store) Watch(prefix string, recursive bool, sinceIndex uint64, index uint64, term uint64) (<-chan *Event, error) {
+	s.Index, s.Term = index, term
 
 	if sinceIndex == 0 {
-		return fs.WatcherHub.watch(prefix, recursive, index+1)
+		return s.WatcherHub.watch(prefix, recursive, index+1)
 	}
 
-	return fs.WatcherHub.watch(prefix, recursive, sinceIndex)
+	return s.WatcherHub.watch(prefix, recursive, sinceIndex)
 }
 
 // walk function walks all the nodePath and apply the walkFunc on each directory
-func (fs *FileSystem) walk(nodePath string, walkFunc func(prev *Node, component string) (*Node, error)) (*Node, error) {
+func (s *Store) walk(nodePath string, walkFunc func(prev *Node, component string) (*Node, error)) (*Node, error) {
 	components := strings.Split(nodePath, "/")
 
-	curr := fs.Root
+	curr := s.Root
 
 	var err error
 	for i := 1; i < len(components); i++ {
@@ -299,11 +300,11 @@ func (fs *FileSystem) walk(nodePath string, walkFunc func(prev *Node, component
 }
 
 // InternalGet function get the node of the given nodePath.
-func (fs *FileSystem) InternalGet(nodePath string, index uint64, term uint64) (*Node, error) {
+func (s *Store) InternalGet(nodePath string, index uint64, term uint64) (*Node, error) {
 	nodePath = path.Clean(path.Join("/", nodePath))
 
 	// update file system known index and term
-	fs.Index, fs.Term = index, term
+	s.Index, s.Term = index, term
 
 	walkFunc := func(parent *Node, name string) (*Node, error) {
 
@@ -319,7 +320,7 @@ func (fs *FileSystem) InternalGet(nodePath string, index uint64, term uint64) (*
 		return nil, etcdErr.NewError(etcdErr.EcodeKeyNotFound, path.Join(parent.Path, name))
 	}
 
-	f, err := fs.walk(nodePath, walkFunc)
+	f, err := s.walk(nodePath, walkFunc)
 
 	if err != nil {
 		return nil, err
@@ -332,14 +333,14 @@ func (fs *FileSystem) InternalGet(nodePath string, index uint64, term uint64) (*
 // If it is a directory, this function will return the pointer to that node.
 // If it does not exist, this function will create a new directory and return the pointer to that node.
 // If it is a file, this function will return error.
-func (fs *FileSystem) checkDir(parent *Node, dirName string) (*Node, error) {
+func (s *Store) checkDir(parent *Node, dirName string) (*Node, error) {
 	subDir, ok := parent.Children[dirName]
 
 	if ok {
 		return subDir, nil
 	}
 
-	n := newDir(path.Join(parent.Path, dirName), fs.Index, fs.Term, parent, parent.ACL, Permanent)
+	n := newDir(path.Join(parent.Path, dirName), s.Index, s.Term, parent, parent.ACL, Permanent)
 
 	parent.Children[dirName] = n
 
@@ -350,24 +351,20 @@ func (fs *FileSystem) checkDir(parent *Node, dirName string) (*Node, error) {
 // Save function will not be able to save the state of watchers.
 // Save function will not save the parent field of the node. Or there will
 // be cyclic dependencies issue for the json package.
-func (fs *FileSystem) Save() ([]byte, error) {
-
-	fs.Stats.IncStats(StatsSaveHit)
-	cloneFs := New()
-	cloneFs.Root = fs.Root.Clone()
+func (s *Store) Save() ([]byte, error) {
+	clonedStore := New()
+	clonedStore.Root = s.Root.Clone()
+	clonedStore.WatcherHub = s.WatcherHub
+	clonedStore.Index = s.Index
+	clonedStore.Term = s.Term
+	clonedStore.Stats = s.Stats
 
-	b, err := json.Marshal(fs)
+	b, err := json.Marshal(clonedStore)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsSaveMiss)
-		fs.Stats.rwlock.Lock()
-		fs.Stats.SaveHit-- // restore the savehit
-		fs.Stats.rwlock.Unlock()
-
 		return nil, err
 	}
 
-	fs.Stats.IncStats(StatsSaveHit)
 	return b, nil
 }
 
@@ -375,15 +372,18 @@ func (fs *FileSystem) Save() ([]byte, error) {
 // It needs to recovery the parent field of the nodes.
 // It needs to delete the expired nodes since the saved time and also
 // need to create monitor go routines.
-func (fs *FileSystem) Recovery(state []byte) error {
-	err := json.Unmarshal(state, fs)
+func (s *Store) Recovery(state []byte) error {
+	err := json.Unmarshal(state, s)
 
 	if err != nil {
-		fs.Stats.IncStats(StatsRecoveryMiss)
 		return err
 	}
 
-	fs.Root.recoverAndclean()
-	fs.Stats.IncStats(StatsRecoveryHit)
+	s.Root.recoverAndclean()
 	return nil
 }
+
+func (s *Store) JsonStats() []byte {
+	s.Stats.Watchers = uint64(s.WatcherHub.count)
+	return s.Stats.toJson()
+}

+ 86 - 86
file_system/file_system_test.go → store/store_test.go

@@ -1,4 +1,4 @@
-package fileSystem
+package store
 
 import (
 	"math/rand"
@@ -8,46 +8,46 @@ import (
 )
 
 func TestCreateAndGet(t *testing.T) {
-	fs := New()
+	s := New()
 
-	fs.Create("/foobar", "bar", Permanent, 1, 1)
+	s.Create("/foobar", "bar", Permanent, 1, 1)
 
 	// already exist, create should fail
-	_, err := fs.Create("/foobar", "bar", Permanent, 1, 1)
+	_, err := s.Create("/foobar", "bar", Permanent, 1, 1)
 
 	if err == nil {
 		t.Fatal("Create should fail")
 	}
 
-	fs.Delete("/foobar", true, 1, 1)
+	s.Delete("/foobar", true, 1, 1)
 
 	// this should create successfully
-	createAndGet(fs, "/foobar", t)
-	createAndGet(fs, "/foo/bar", t)
-	createAndGet(fs, "/foo/foo/bar", t)
+	createAndGet(s, "/foobar", t)
+	createAndGet(s, "/foo/bar", t)
+	createAndGet(s, "/foo/foo/bar", t)
 
 	// meet file, create should fail
-	_, err = fs.Create("/foo/bar/bar", "bar", Permanent, 2, 1)
+	_, err = s.Create("/foo/bar/bar", "bar", Permanent, 2, 1)
 
 	if err == nil {
 		t.Fatal("Create should fail")
 	}
 
 	// create a directory
-	_, err = fs.Create("/fooDir", "", Permanent, 3, 1)
+	_, err = s.Create("/fooDir", "", Permanent, 3, 1)
 
 	if err != nil {
 		t.Fatal("Cannot create /fooDir")
 	}
 
-	e, err := fs.Get("/fooDir", false, false, 3, 1)
+	e, err := s.Get("/fooDir", false, false, 3, 1)
 
 	if err != nil || e.Dir != true {
 		t.Fatal("Cannot create /fooDir ")
 	}
 
 	// create a file under directory
-	_, err = fs.Create("/fooDir/bar", "bar", Permanent, 4, 1)
+	_, err = s.Create("/fooDir/bar", "bar", Permanent, 4, 1)
 
 	if err != nil {
 		t.Fatal("Cannot create /fooDir/bar = bar")
@@ -56,21 +56,21 @@ func TestCreateAndGet(t *testing.T) {
 }
 
 func TestUpdateFile(t *testing.T) {
-	fs := New()
+	s := New()
 
-	_, err := fs.Create("/foo/bar", "bar", Permanent, 1, 1)
+	_, err := s.Create("/foo/bar", "bar", Permanent, 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot create %s=bar [%s]", "/foo/bar", err.Error())
 	}
 
-	_, err = fs.Update("/foo/bar", "barbar", Permanent, 2, 1)
+	_, err = s.Update("/foo/bar", "barbar", Permanent, 2, 1)
 
 	if err != nil {
 		t.Fatalf("cannot update %s=barbar [%s]", "/foo/bar", err.Error())
 	}
 
-	e, err := fs.Get("/foo/bar", false, false, 2, 1)
+	e, err := s.Get("/foo/bar", false, false, 2, 1)
 
 	if err != nil {
 		t.Fatalf("cannot get %s [%s]", "/foo/bar", err.Error())
@@ -82,37 +82,37 @@ func TestUpdateFile(t *testing.T) {
 
 	// create a directory, update its ttl, to see if it will be deleted
 
-	_, err = fs.Create("/foo/foo", "", Permanent, 3, 1)
+	_, err = s.Create("/foo/foo", "", Permanent, 3, 1)
 
 	if err != nil {
 		t.Fatalf("cannot create dir [%s] [%s]", "/foo/foo", err.Error())
 	}
 
-	_, err = fs.Create("/foo/foo/foo1", "bar1", Permanent, 4, 1)
+	_, err = s.Create("/foo/foo/foo1", "bar1", Permanent, 4, 1)
 
 	if err != nil {
 		t.Fatal("cannot create [%s]", err.Error())
 	}
 
-	_, err = fs.Create("/foo/foo/foo2", "", Permanent, 5, 1)
+	_, err = s.Create("/foo/foo/foo2", "", Permanent, 5, 1)
 	if err != nil {
 		t.Fatal("cannot create [%s]", err.Error())
 	}
 
-	_, err = fs.Create("/foo/foo/foo2/boo", "boo1", Permanent, 6, 1)
+	_, err = s.Create("/foo/foo/foo2/boo", "boo1", Permanent, 6, 1)
 	if err != nil {
 		t.Fatal("cannot create [%s]", err.Error())
 	}
 
 	expire := time.Now().Add(time.Second * 2)
-	_, err = fs.Update("/foo/foo", "", expire, 7, 1)
+	_, err = s.Update("/foo/foo", "", expire, 7, 1)
 	if err != nil {
 		t.Fatalf("cannot update dir [%s] [%s]", "/foo/foo", err.Error())
 	}
 
 	// sleep 50ms, it should still reach the node
 	time.Sleep(time.Microsecond * 50)
-	e, err = fs.Get("/foo/foo", true, false, 7, 1)
+	e, err = s.Get("/foo/foo", true, false, 7, 1)
 
 	if err != nil || e.Key != "/foo/foo" {
 		t.Fatalf("cannot get dir before expiration [%s]", err.Error())
@@ -132,23 +132,23 @@ func TestUpdateFile(t *testing.T) {
 
 	// wait for expiration
 	time.Sleep(time.Second * 3)
-	e, err = fs.Get("/foo/foo", true, false, 7, 1)
+	e, err = s.Get("/foo/foo", true, false, 7, 1)
 
 	if err == nil {
 		t.Fatal("still can get dir after expiration [%s]")
 	}
 
-	_, err = fs.Get("/foo/foo/foo1", true, false, 7, 1)
+	_, err = s.Get("/foo/foo/foo1", true, false, 7, 1)
 	if err == nil {
 		t.Fatal("still can get sub node after expiration [%s]")
 	}
 
-	_, err = fs.Get("/foo/foo/foo2", true, false, 7, 1)
+	_, err = s.Get("/foo/foo/foo2", true, false, 7, 1)
 	if err == nil {
 		t.Fatal("still can get sub dir after expiration [%s]")
 	}
 
-	_, err = fs.Get("/foo/foo/foo2/boo", true, false, 7, 1)
+	_, err = s.Get("/foo/foo/foo2/boo", true, false, 7, 1)
 	if err == nil {
 		t.Fatalf("still can get sub node of sub dir after expiration [%s]", err.Error())
 	}
@@ -156,17 +156,17 @@ func TestUpdateFile(t *testing.T) {
 }
 
 func TestListDirectory(t *testing.T) {
-	fs := New()
+	s := New()
 
 	// create dir /foo
 	// set key-value /foo/foo=bar
-	fs.Create("/foo/foo", "bar", Permanent, 1, 1)
+	s.Create("/foo/foo", "bar", Permanent, 1, 1)
 
 	// create dir /foo/fooDir
 	// set key-value /foo/fooDir/foo=bar
-	fs.Create("/foo/fooDir/foo", "bar", Permanent, 2, 1)
+	s.Create("/foo/fooDir/foo", "bar", Permanent, 2, 1)
 
-	e, err := fs.Get("/foo", true, false, 2, 1)
+	e, err := s.Get("/foo", true, false, 2, 1)
 
 	if err != nil {
 		t.Fatalf("%v", err)
@@ -191,9 +191,9 @@ func TestListDirectory(t *testing.T) {
 
 	// create dir /foo/_hidden
 	// set key-value /foo/_hidden/foo -> bar
-	fs.Create("/foo/_hidden/foo", "bar", Permanent, 3, 1)
+	s.Create("/foo/_hidden/foo", "bar", Permanent, 3, 1)
 
-	e, _ = fs.Get("/foo", false, false, 2, 1)
+	e, _ = s.Get("/foo", false, false, 2, 1)
 
 	if len(e.KVPairs) != 2 {
 		t.Fatalf("hidden node is not hidden! %s", e.KVPairs[2].Key)
@@ -201,38 +201,38 @@ func TestListDirectory(t *testing.T) {
 }
 
 func TestRemove(t *testing.T) {
-	fs := New()
+	s := New()
 
-	fs.Create("/foo", "bar", Permanent, 1, 1)
-	_, err := fs.Delete("/foo", false, 1, 1)
+	s.Create("/foo", "bar", Permanent, 1, 1)
+	_, err := s.Delete("/foo", false, 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot delete %s [%s]", "/foo", err.Error())
 	}
 
-	_, err = fs.Get("/foo", false, false, 1, 1)
+	_, err = s.Get("/foo", false, false, 1, 1)
 
 	if err == nil || err.Error() != "Key Not Found" {
 		t.Fatalf("can get the node after deletion")
 	}
 
-	fs.Create("/foo/bar", "bar", Permanent, 1, 1)
-	fs.Create("/foo/car", "car", Permanent, 1, 1)
-	fs.Create("/foo/dar/dar", "dar", Permanent, 1, 1)
+	s.Create("/foo/bar", "bar", Permanent, 1, 1)
+	s.Create("/foo/car", "car", Permanent, 1, 1)
+	s.Create("/foo/dar/dar", "dar", Permanent, 1, 1)
 
-	_, err = fs.Delete("/foo", false, 1, 1)
+	_, err = s.Delete("/foo", false, 1, 1)
 
 	if err == nil {
 		t.Fatalf("should not be able to delete a directory without recursive")
 	}
 
-	_, err = fs.Delete("/foo", true, 1, 1)
+	_, err = s.Delete("/foo", true, 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot delete %s [%s]", "/foo", err.Error())
 	}
 
-	_, err = fs.Get("/foo", false, false, 1, 1)
+	_, err = s.Get("/foo", false, false, 1, 1)
 
 	if err == nil || err.Error() != "Key Not Found" {
 		t.Fatalf("can get the node after deletion ")
@@ -241,13 +241,13 @@ func TestRemove(t *testing.T) {
 }
 
 func TestExpire(t *testing.T) {
-	fs := New()
+	s := New()
 
 	expire := time.Now().Add(time.Second)
 
-	fs.Create("/foo", "bar", expire, 1, 1)
+	s.Create("/foo", "bar", expire, 1, 1)
 
-	_, err := fs.InternalGet("/foo", 1, 1)
+	_, err := s.InternalGet("/foo", 1, 1)
 
 	if err != nil {
 		t.Fatalf("can not get the node")
@@ -255,7 +255,7 @@ func TestExpire(t *testing.T) {
 
 	time.Sleep(time.Second * 2)
 
-	_, err = fs.InternalGet("/foo", 1, 1)
+	_, err = s.InternalGet("/foo", 1, 1)
 
 	if err == nil {
 		t.Fatalf("can get the node after expiration time")
@@ -263,10 +263,10 @@ func TestExpire(t *testing.T) {
 
 	// test if we can reach the node before expiration
 	expire = time.Now().Add(time.Second)
-	fs.Create("/foo", "bar", expire, 1, 1)
+	s.Create("/foo", "bar", expire, 1, 1)
 
 	time.Sleep(time.Millisecond * 50)
-	_, err = fs.InternalGet("/foo", 1, 1)
+	_, err = s.InternalGet("/foo", 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot get the node before expiration", err.Error())
@@ -274,8 +274,8 @@ func TestExpire(t *testing.T) {
 
 	expire = time.Now().Add(time.Second)
 
-	fs.Create("/foo", "bar", expire, 1, 1)
-	_, err = fs.Delete("/foo", false, 1, 1)
+	s.Create("/foo", "bar", expire, 1, 1)
+	_, err = s.Delete("/foo", false, 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot delete the node before expiration", err.Error())
@@ -284,17 +284,17 @@ func TestExpire(t *testing.T) {
 }
 
 func TestTestAndSet(t *testing.T) { // TODO prevValue == nil ?
-	fs := New()
-	fs.Create("/foo", "bar", Permanent, 1, 1)
+	s := New()
+	s.Create("/foo", "bar", Permanent, 1, 1)
 
 	// test on wrong previous value
-	_, err := fs.TestAndSet("/foo", "barbar", 0, "car", Permanent, 2, 1)
+	_, err := s.TestAndSet("/foo", "barbar", 0, "car", Permanent, 2, 1)
 	if err == nil {
 		t.Fatal("test and set should fail barbar != bar")
 	}
 
 	// test on value
-	e, err := fs.TestAndSet("/foo", "bar", 0, "car", Permanent, 3, 1)
+	e, err := s.TestAndSet("/foo", "bar", 0, "car", Permanent, 3, 1)
 
 	if err != nil {
 		t.Fatal("test and set should succeed bar == bar")
@@ -305,7 +305,7 @@ func TestTestAndSet(t *testing.T) { // TODO prevValue == nil ?
 	}
 
 	// test on index
-	e, err = fs.TestAndSet("/foo", "", 3, "bar", Permanent, 4, 1)
+	e, err = s.TestAndSet("/foo", "", 3, "bar", Permanent, 4, 1)
 
 	if err != nil {
 		t.Fatal("test and set should succeed index 3 == 3")
@@ -318,61 +318,61 @@ func TestTestAndSet(t *testing.T) { // TODO prevValue == nil ?
 }
 
 func TestWatch(t *testing.T) {
-	fs := New()
+	s := New()
 	// watch at a deeper path
-	c, _ := fs.WatcherHub.watch("/foo/foo/foo", false, 0)
-	fs.Create("/foo/foo/foo", "bar", Permanent, 1, 1)
+	c, _ := s.WatcherHub.watch("/foo/foo/foo", false, 0)
+	s.Create("/foo/foo/foo", "bar", Permanent, 1, 1)
 
 	e := nonblockingRetrive(c)
 	if e.Key != "/foo/foo/foo" {
 		t.Fatal("watch for Create node fails")
 	}
 
-	c, _ = fs.WatcherHub.watch("/foo/foo/foo", false, 0)
-	fs.Update("/foo/foo/foo", "car", Permanent, 2, 1)
+	c, _ = s.WatcherHub.watch("/foo/foo/foo", false, 0)
+	s.Update("/foo/foo/foo", "car", Permanent, 2, 1)
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo/foo/foo" {
 		t.Fatal("watch for Update node fails")
 	}
 
-	c, _ = fs.WatcherHub.watch("/foo/foo/foo", false, 0)
-	fs.TestAndSet("/foo/foo/foo", "car", 0, "bar", Permanent, 3, 1)
+	c, _ = s.WatcherHub.watch("/foo/foo/foo", false, 0)
+	s.TestAndSet("/foo/foo/foo", "car", 0, "bar", Permanent, 3, 1)
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo/foo/foo" {
 		t.Fatal("watch for TestAndSet node fails")
 	}
 
-	c, _ = fs.WatcherHub.watch("/foo/foo/foo", false, 0)
-	fs.Delete("/foo", true, 4, 1) //recursively delete
+	c, _ = s.WatcherHub.watch("/foo/foo/foo", false, 0)
+	s.Delete("/foo", true, 4, 1) //recursively delete
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo" {
 		t.Fatal("watch for Delete node fails")
 	}
 
 	// watch at a prefix
-	c, _ = fs.WatcherHub.watch("/foo", true, 0)
-	fs.Create("/foo/foo/boo", "bar", Permanent, 5, 1)
+	c, _ = s.WatcherHub.watch("/foo", true, 0)
+	s.Create("/foo/foo/boo", "bar", Permanent, 5, 1)
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo/foo/boo" {
 		t.Fatal("watch for Create subdirectory fails")
 	}
 
-	c, _ = fs.WatcherHub.watch("/foo", true, 0)
-	fs.Update("/foo/foo/boo", "foo", Permanent, 6, 1)
+	c, _ = s.WatcherHub.watch("/foo", true, 0)
+	s.Update("/foo/foo/boo", "foo", Permanent, 6, 1)
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo/foo/boo" {
 		t.Fatal("watch for Update subdirectory fails")
 	}
 
-	c, _ = fs.WatcherHub.watch("/foo", true, 0)
-	fs.TestAndSet("/foo/foo/boo", "foo", 0, "bar", Permanent, 7, 1)
+	c, _ = s.WatcherHub.watch("/foo", true, 0)
+	s.TestAndSet("/foo/foo/boo", "foo", 0, "bar", Permanent, 7, 1)
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo/foo/boo" {
 		t.Fatal("watch for TestAndSet subdirectory fails")
 	}
 
-	c, _ = fs.WatcherHub.watch("/foo", true, 0)
-	fs.Delete("/foo/foo/boo", false, 8, 1)
+	c, _ = s.WatcherHub.watch("/foo", true, 0)
+	s.Delete("/foo/foo/boo", false, 8, 1)
 	e = nonblockingRetrive(c)
 	if e.Key != "/foo/foo/boo" {
 		t.Fatal("watch for Delete subdirectory fails")
@@ -381,14 +381,14 @@ func TestWatch(t *testing.T) {
 }
 
 func TestSort(t *testing.T) {
-	fs := New()
+	s := New()
 
 	// simulating random creation
 	keys := GenKeys(80, 4)
 
 	i := uint64(1)
 	for _, k := range keys {
-		_, err := fs.Create(k, "bar", Permanent, i, 1)
+		_, err := s.Create(k, "bar", Permanent, i, 1)
 		if err != nil {
 			panic(err)
 		} else {
@@ -396,7 +396,7 @@ func TestSort(t *testing.T) {
 		}
 	}
 
-	e, err := fs.Get("/foo", true, true, i, 1)
+	e, err := s.Get("/foo", true, true, i, 1)
 	if err != nil {
 		t.Fatalf("get dir nodes failed [%s]", err.Error())
 	}
@@ -419,14 +419,14 @@ func TestSort(t *testing.T) {
 }
 
 func TestSaveAndRecover(t *testing.T) {
-	fs := New()
+	s := New()
 
 	// simulating random creation
 	keys := GenKeys(8, 4)
 
 	i := uint64(1)
 	for _, k := range keys {
-		_, err := fs.Create(k, "bar", Permanent, i, 1)
+		_, err := s.Create(k, "bar", Permanent, i, 1)
 		if err != nil {
 			panic(err)
 		} else {
@@ -438,8 +438,8 @@ func TestSaveAndRecover(t *testing.T) {
 	// test if we can reach the node before expiration
 
 	expire := time.Now().Add(time.Second)
-	fs.Create("/foo/foo", "bar", expire, 1, 1)
-	b, err := fs.Save()
+	s.Create("/foo/foo", "bar", expire, 1, 1)
+	b, err := s.Save()
 
 	cloneFs := New()
 	time.Sleep(time.Second)
@@ -453,18 +453,18 @@ func TestSaveAndRecover(t *testing.T) {
 		}
 	}
 
-	if fs.WatcherHub.EventHistory.StartIndex != cloneFs.WatcherHub.EventHistory.StartIndex {
+	if s.WatcherHub.EventHistory.StartIndex != cloneFs.WatcherHub.EventHistory.StartIndex {
 		t.Fatal("Error recovered event history start index")
 	}
 
-	for i = 0; int(i) < fs.WatcherHub.EventHistory.Queue.Size; i++ {
-		if fs.WatcherHub.EventHistory.Queue.Events[i].Key !=
+	for i = 0; int(i) < s.WatcherHub.EventHistory.Queue.Size; i++ {
+		if s.WatcherHub.EventHistory.Queue.Events[i].Key !=
 			cloneFs.WatcherHub.EventHistory.Queue.Events[i].Key {
 			t.Fatal("Error recovered event history")
 		}
 	}
 
-	_, err = fs.Get("/foo/foo", false, false, 1, 1)
+	_, err = s.Get("/foo/foo", false, false, 1, 1)
 
 	if err == nil || err.Error() != "Key Not Found" {
 		t.Fatalf("can get the node after deletion ")
@@ -488,14 +488,14 @@ func GenKeys(num int, depth int) []string {
 	return keys
 }
 
-func createAndGet(fs *FileSystem, path string, t *testing.T) {
-	_, err := fs.Create(path, "bar", Permanent, 1, 1)
+func createAndGet(s *Store, path string, t *testing.T) {
+	_, err := s.Create(path, "bar", Permanent, 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot create %s=bar [%s]", path, err.Error())
 	}
 
-	e, err := fs.Get(path, false, false, 1, 1)
+	e, err := s.Get(path, false, false, 1, 1)
 
 	if err != nil {
 		t.Fatalf("cannot get %s [%s]", path, err.Error())

+ 7 - 12
file_system/watcher.go → store/watcher.go

@@ -1,16 +1,16 @@
-package fileSystem
+package store
 
 import (
 	"container/list"
 	"path"
 	"strings"
+	"sync/atomic"
 )
 
 type watcherHub struct {
 	watchers     map[string]*list.List
-	count        uint64 // current number of watchers
+	count        int64 // current number of watchers.
 	EventHistory *EventHistory
-	Stats        *EtcdStats
 }
 
 type watcher struct {
@@ -19,11 +19,10 @@ type watcher struct {
 	sinceIndex uint64
 }
 
-func newWatchHub(capacity int, stats *EtcdStats) *watcherHub {
+func newWatchHub(capacity int) *watcherHub {
 	return &watcherHub{
 		watchers:     make(map[string]*list.List),
 		EventHistory: newEventHistory(capacity),
-		Stats:        stats,
 	}
 }
 
@@ -37,13 +36,9 @@ func (wh *watcherHub) watch(prefix string, recursive bool, index uint64) (<-chan
 	e, err := wh.EventHistory.scan(prefix, index)
 
 	if err != nil {
-		wh.Stats.IncStats(StatsWatchMiss)
 		return nil, err
 	}
 
-	wh.Stats.IncStats(StatsWatchHit)
-	wh.Stats.IncStats(StatsInWatchingNum)
-
 	if e != nil {
 		eventChan <- e
 		return eventChan, nil
@@ -66,6 +61,8 @@ func (wh *watcherHub) watch(prefix string, recursive bool, index uint64) (<-chan
 		wh.watchers[prefix] = l
 	}
 
+	atomic.AddInt64(&wh.count, 1)
+
 	return eventChan, nil
 }
 
@@ -94,10 +91,8 @@ func (wh *watcherHub) notifyWithPath(e *Event, path string, force bool) {
 
 			if (w.recursive || force || e.Key == path) && e.Index >= w.sinceIndex {
 				w.eventChan <- e
-				wh.Stats.rwlock.Lock() // lock the InWatchingNum
-				wh.Stats.InWatchingNum--
-				wh.Stats.rwlock.Unlock()
 				l.Remove(curr)
+				atomic.AddInt64(&wh.count, -1)
 			} else {
 				notifiedAll = false
 			}

+ 3 - 3
file_system/watcher_test.go → store/watcher_test.go

@@ -1,12 +1,12 @@
-package fileSystem
+package store
 
 import (
 	"testing"
 )
 
 func TestWatcher(t *testing.T) {
-	fs := New()
-	wh := fs.WatcherHub
+	s := New()
+	wh := s.WatcherHub
 	c, err := wh.watch("/foo", true, 0)
 
 	if err != nil {

+ 3 - 3
util.go

@@ -15,7 +15,7 @@ import (
 	"time"
 
 	etcdErr "github.com/coreos/etcd/error"
-	"github.com/coreos/etcd/file_system"
+	"github.com/coreos/etcd/store"
 	"github.com/coreos/etcd/web"
 	"github.com/coreos/go-raft"
 )
@@ -30,12 +30,12 @@ func durationToExpireTime(strDuration string) (time.Time, error) {
 		duration, err := strconv.Atoi(strDuration)
 
 		if err != nil {
-			return fileSystem.Permanent, err
+			return store.Permanent, err
 		}
 		return time.Now().Add(time.Second * (time.Duration)(duration)), nil
 
 	} else {
-		return fileSystem.Permanent, nil
+		return store.Permanent, nil
 	}
 }