Browse Source

refactor event

Xiang Li 12 years ago
parent
commit
b7d07ea5c8

+ 3 - 3
server/registry.go

@@ -70,7 +70,7 @@ func (r *Registry) Count() int {
 	if err != nil {
 	if err != nil {
 		return 0
 		return 0
 	}
 	}
-	return len(e.KVPairs)
+	return len(e.Node.Nodes)
 }
 }
 
 
 // Retrieves the client URL for a given node by name.
 // Retrieves the client URL for a given node by name.
@@ -135,7 +135,7 @@ func (r *Registry) urls(leaderName, selfName string, url func(name string) (stri
 	// Retrieve a list of all nodes.
 	// Retrieve a list of all nodes.
 	if e, _ := r.store.Get(RegistryKey, false, false); e != nil {
 	if e, _ := r.store.Get(RegistryKey, false, false); e != nil {
 		// Lookup the URL for each one.
 		// Lookup the URL for each one.
-		for _, pair := range e.KVPairs {
+		for _, pair := range e.Node.Nodes {
 			_, name := filepath.Split(pair.Key)
 			_, name := filepath.Split(pair.Key)
 			if url, _ := url(name); len(url) > 0 && name != leaderName {
 			if url, _ := url(name); len(url) > 0 && name != leaderName {
 				urls = append(urls, url)
 				urls = append(urls, url)
@@ -166,7 +166,7 @@ func (r *Registry) load(name string) {
 	}
 	}
 
 
 	// Parse as a query string.
 	// Parse as a query string.
-	m, err := url.ParseQuery(e.Value)
+	m, err := url.ParseQuery(e.Node.Value)
 	if err != nil {
 	if err != nil {
 		panic(fmt.Sprintf("Failed to parse peers entry: %s", name))
 		panic(fmt.Sprintf("Failed to parse peers entry: %s", name))
 	}
 	}

+ 1 - 1
server/v2/tests/delete_handler_test.go

@@ -24,6 +24,6 @@ func TestV2DeleteKey(t *testing.T) {
 		resp, err = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), url.Values{})
 		resp, err = tests.DeleteForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), url.Values{})
 		body := tests.ReadBody(resp)
 		body := tests.ReadBody(resp)
 		assert.Nil(t, err, "")
 		assert.Nil(t, err, "")
-		assert.Equal(t, string(body), `{"action":"delete","key":"/foo/bar","prevValue":"XXX","modifiedIndex":2}`, "")
+		assert.Equal(t, string(body), `{"action":"delete","node":{"key":"/foo/bar","modifiedIndex":2}}`, "")
 	})
 	})
 }
 }

+ 34 - 26
server/v2/tests/get_handler_test.go

@@ -25,9 +25,11 @@ func TestV2GetKey(t *testing.T) {
 		resp, _ = tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"))
 		resp, _ = tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"))
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
 		assert.Equal(t, body["action"], "get", "")
 		assert.Equal(t, body["action"], "get", "")
-		assert.Equal(t, body["key"], "/foo/bar", "")
-		assert.Equal(t, body["value"], "XXX", "")
-		assert.Equal(t, body["modifiedIndex"], 1, "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo/bar", "")
+		assert.Equal(t, node["value"], "XXX", "")
+		assert.Equal(t, node["modifiedIndex"], 1, "")
 	})
 	})
 }
 }
 
 
@@ -52,23 +54,25 @@ func TestV2GetKeyRecursively(t *testing.T) {
 		resp, _ = tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo?recursive=true"))
 		resp, _ = tests.Get(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo?recursive=true"))
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
 		assert.Equal(t, body["action"], "get", "")
 		assert.Equal(t, body["action"], "get", "")
-		assert.Equal(t, body["key"], "/foo", "")
-		assert.Equal(t, body["dir"], true, "")
-		assert.Equal(t, body["modifiedIndex"], 1, "")
-		assert.Equal(t, len(body["kvs"].([]interface{})), 2, "")
-
-		kv0 := body["kvs"].([]interface{})[0].(map[string]interface{})
-		assert.Equal(t, kv0["key"], "/foo/x", "")
-		assert.Equal(t, kv0["value"], "XXX", "")
-		assert.Equal(t, kv0["ttl"], 10, "")
-
-		kv1 := body["kvs"].([]interface{})[1].(map[string]interface{})
-		assert.Equal(t, kv1["key"], "/foo/y", "")
-		assert.Equal(t, kv1["dir"], true, "")
-
-		kvs2 := kv1["kvs"].([]interface{})[0].(map[string]interface{})
-		assert.Equal(t, kvs2["key"], "/foo/y/z", "")
-		assert.Equal(t, kvs2["value"], "YYY", "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo", "")
+		assert.Equal(t, node["dir"], true, "")
+		assert.Equal(t, node["modifiedIndex"], 1, "")
+		assert.Equal(t, len(node["nodes"].([]interface{})), 2, "")
+
+		node0 := node["nodes"].([]interface{})[0].(map[string]interface{})
+		assert.Equal(t, node0["key"], "/foo/x", "")
+		assert.Equal(t, node0["value"], "XXX", "")
+		assert.Equal(t, node0["ttl"], 10, "")
+
+		node1 := node["nodes"].([]interface{})[1].(map[string]interface{})
+		assert.Equal(t, node1["key"], "/foo/y", "")
+		assert.Equal(t, node1["dir"], true, "")
+
+		node2 := node1["nodes"].([]interface{})[0].(map[string]interface{})
+		assert.Equal(t, node2["key"], "/foo/y/z", "")
+		assert.Equal(t, node2["value"], "YYY", "")
 	})
 	})
 }
 }
 
 
@@ -109,9 +113,11 @@ func TestV2WatchKey(t *testing.T) {
 
 
 		assert.NotNil(t, body, "")
 		assert.NotNil(t, body, "")
 		assert.Equal(t, body["action"], "set", "")
 		assert.Equal(t, body["action"], "set", "")
-		assert.Equal(t, body["key"], "/foo/bar", "")
-		assert.Equal(t, body["value"], "XXX", "")
-		assert.Equal(t, body["modifiedIndex"], 1, "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo/bar", "")
+		assert.Equal(t, node["value"], "XXX", "")
+		assert.Equal(t, node["modifiedIndex"], 1, "")
 	})
 	})
 }
 }
 
 
@@ -162,8 +168,10 @@ func TestV2WatchKeyWithIndex(t *testing.T) {
 
 
 		assert.NotNil(t, body, "")
 		assert.NotNil(t, body, "")
 		assert.Equal(t, body["action"], "set", "")
 		assert.Equal(t, body["action"], "set", "")
-		assert.Equal(t, body["key"], "/foo/bar", "")
-		assert.Equal(t, body["value"], "YYY", "")
-		assert.Equal(t, body["modifiedIndex"], 2, "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo/bar", "")
+		assert.Equal(t, node["value"], "YYY", "")
+		assert.Equal(t, node["modifiedIndex"], 2, "")
 	})
 	})
 }
 }

+ 9 - 5
server/v2/tests/post_handler_test.go

@@ -21,18 +21,22 @@ func TestV2CreateUnique(t *testing.T) {
 		resp, _ := tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
 		resp, _ := tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
 		assert.Equal(t, body["action"], "create", "")
 		assert.Equal(t, body["action"], "create", "")
-		assert.Equal(t, body["key"], "/foo/bar/1", "")
-		assert.Equal(t, body["dir"], true, "")
-		assert.Equal(t, body["modifiedIndex"], 1, "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo/bar/1", "")
+		assert.Equal(t, node["dir"], true, "")
+		assert.Equal(t, node["modifiedIndex"], 1, "")
 
 
 		// Second POST should add next index to list.
 		// Second POST should add next index to list.
 		resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
 		resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), nil)
 		body = tests.ReadBodyJSON(resp)
 		body = tests.ReadBodyJSON(resp)
-		assert.Equal(t, body["key"], "/foo/bar/2", "")
+		node = body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo/bar/2", "")
 
 
 		// POST to a different key should add index to that list.
 		// POST to a different key should add index to that list.
 		resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/baz"), nil)
 		resp, _ = tests.PostForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/baz"), nil)
 		body = tests.ReadBodyJSON(resp)
 		body = tests.ReadBodyJSON(resp)
-		assert.Equal(t, body["key"], "/foo/baz/3", "")
+		node = body["node"].(map[string]interface{})
+		assert.Equal(t, node["key"], "/foo/baz/3", "")
 	})
 	})
 }
 }

+ 19 - 11
server/v2/tests/put_handler_test.go

@@ -22,7 +22,7 @@ func TestV2SetKey(t *testing.T) {
 		resp, err := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		resp, err := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		body := tests.ReadBody(resp)
 		body := tests.ReadBody(resp)
 		assert.Nil(t, err, "")
 		assert.Nil(t, err, "")
-		assert.Equal(t, string(body), `{"action":"set","key":"/foo/bar","value":"XXX","modifiedIndex":1}`, "")
+		assert.Equal(t, string(body), `{"action":"set","node":{"key":"/foo/bar","value":"XXX","modifiedIndex":1}}`, "")
 	})
 	})
 }
 }
 
 
@@ -38,10 +38,11 @@ func TestV2SetKeyWithTTL(t *testing.T) {
 		v.Set("ttl", "20")
 		v.Set("ttl", "20")
 		resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
-		assert.Equal(t, body["ttl"], 20, "")
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["ttl"], 20, "")
 
 
 		// Make sure the expiration date is correct.
 		// Make sure the expiration date is correct.
-		expiration, _ := time.Parse(time.RFC3339Nano, body["expiration"].(string))
+		expiration, _ := time.Parse(time.RFC3339Nano, node["expiration"].(string))
 		assert.Equal(t, expiration.Sub(t0)/time.Second, 20, "")
 		assert.Equal(t, expiration.Sub(t0)/time.Second, 20, "")
 	})
 	})
 }
 }
@@ -74,7 +75,8 @@ func TestV2CreateKeySuccess(t *testing.T) {
 		v.Set("prevExist", "false")
 		v.Set("prevExist", "false")
 		resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		resp, _ := tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
-		assert.Equal(t, body["value"], "XXX", "")
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["value"], "XXX", "")
 	})
 	})
 }
 }
 
 
@@ -116,7 +118,9 @@ func TestV2UpdateKeySuccess(t *testing.T) {
 		resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
 		assert.Equal(t, body["action"], "update", "")
 		assert.Equal(t, body["action"], "update", "")
-		assert.Equal(t, body["prevValue"], "XXX", "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["prevValue"], "XXX", "")
 	})
 	})
 }
 }
 
 
@@ -173,9 +177,11 @@ func TestV2SetKeyCASOnIndexSuccess(t *testing.T) {
 		resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
 		assert.Equal(t, body["action"], "compareAndSwap", "")
 		assert.Equal(t, body["action"], "compareAndSwap", "")
-		assert.Equal(t, body["prevValue"], "XXX", "")
-		assert.Equal(t, body["value"], "YYY", "")
-		assert.Equal(t, body["modifiedIndex"], 2, "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["prevValue"], "XXX", "")
+		assert.Equal(t, node["value"], "YYY", "")
+		assert.Equal(t, node["modifiedIndex"], 2, "")
 	})
 	})
 }
 }
 
 
@@ -234,9 +240,11 @@ func TestV2SetKeyCASOnValueSuccess(t *testing.T) {
 		resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		resp, _ = tests.PutForm(fmt.Sprintf("http://%s%s", s.URL(), "/v2/keys/foo/bar"), v)
 		body := tests.ReadBodyJSON(resp)
 		body := tests.ReadBodyJSON(resp)
 		assert.Equal(t, body["action"], "compareAndSwap", "")
 		assert.Equal(t, body["action"], "compareAndSwap", "")
-		assert.Equal(t, body["prevValue"], "XXX", "")
-		assert.Equal(t, body["value"], "YYY", "")
-		assert.Equal(t, body["modifiedIndex"], 2, "")
+
+		node := body["node"].(map[string]interface{})
+		assert.Equal(t, node["prevValue"], "XXX", "")
+		assert.Equal(t, node["value"], "YYY", "")
+		assert.Equal(t, node["modifiedIndex"], 2, "")
 	})
 	})
 }
 }
 
 

+ 26 - 32
store/event.go

@@ -1,9 +1,5 @@
 package store
 package store
 
 
-import (
-	"time"
-)
-
 const (
 const (
 	Get            = "get"
 	Get            = "get"
 	Create         = "create"
 	Create         = "create"
@@ -15,22 +11,20 @@ const (
 )
 )
 
 
 type Event struct {
 type Event struct {
-	Action        string     `json:"action"`
-	Key           string     `json:"key, omitempty"`
-	Dir           bool       `json:"dir,omitempty"`
-	PrevValue     string     `json:"prevValue,omitempty"`
-	Value         string     `json:"value,omitempty"`
-	KVPairs       kvPairs    `json:"kvs,omitempty"`
-	Expiration    *time.Time `json:"expiration,omitempty"`
-	TTL           int64      `json:"ttl,omitempty"` // Time to live in second
-	ModifiedIndex uint64     `json:"modifiedIndex"`
+	Action string `json:"action"`
+	Node   *Node  `json:"node,omitempty"`
 }
 }
 
 
-func newEvent(action string, key string, index uint64) *Event {
-	return &Event{
-		Action:        action,
+func newEvent(action string, key string, modifiedIndex, createdIndex uint64) *Event {
+	n := &Node{
 		Key:           key,
 		Key:           key,
-		ModifiedIndex: index,
+		ModifiedIndex: modifiedIndex,
+		CreatedIndex:  createdIndex,
+	}
+
+	return &Event{
+		Action: action,
+		Node:   n,
 	}
 	}
 }
 }
 
 
@@ -39,7 +33,7 @@ func (e *Event) IsCreated() bool {
 		return true
 		return true
 	}
 	}
 
 
-	if e.Action == Set && e.PrevValue == "" {
+	if e.Action == Set && e.Node.PrevValue == "" {
 		return true
 		return true
 	}
 	}
 
 
@@ -47,20 +41,20 @@ func (e *Event) IsCreated() bool {
 }
 }
 
 
 func (e *Event) Index() uint64 {
 func (e *Event) Index() uint64 {
-	return e.ModifiedIndex
+	return e.Node.ModifiedIndex
 }
 }
 
 
 // Converts an event object into a response object.
 // Converts an event object into a response object.
 func (event *Event) Response() interface{} {
 func (event *Event) Response() interface{} {
-	if !event.Dir {
+	if !event.Node.Dir {
 		response := &Response{
 		response := &Response{
 			Action:     event.Action,
 			Action:     event.Action,
-			Key:        event.Key,
-			Value:      event.Value,
-			PrevValue:  event.PrevValue,
-			Index:      event.ModifiedIndex,
-			TTL:        event.TTL,
-			Expiration: event.Expiration,
+			Key:        event.Node.Key,
+			Value:      event.Node.Value,
+			PrevValue:  event.Node.PrevValue,
+			Index:      event.Node.ModifiedIndex,
+			TTL:        event.Node.TTL,
+			Expiration: event.Node.Expiration,
 		}
 		}
 
 
 		if response.Action == Set {
 		if response.Action == Set {
@@ -75,15 +69,15 @@ func (event *Event) Response() interface{} {
 
 
 		return response
 		return response
 	} else {
 	} else {
-		responses := make([]*Response, len(event.KVPairs))
+		responses := make([]*Response, len(event.Node.Nodes))
 
 
-		for i, kv := range event.KVPairs {
+		for i, node := range event.Node.Nodes {
 			responses[i] = &Response{
 			responses[i] = &Response{
 				Action: event.Action,
 				Action: event.Action,
-				Key:    kv.Key,
-				Value:  kv.Value,
-				Dir:    kv.Dir,
-				Index:  event.ModifiedIndex,
+				Key:    node.Key,
+				Value:  node.Value,
+				Dir:    node.Dir,
+				Index:  node.ModifiedIndex,
 			}
 			}
 		}
 		}
 		return responses
 		return responses

+ 2 - 2
store/event_history.go

@@ -33,7 +33,7 @@ func (eh *EventHistory) addEvent(e *Event) *Event {
 
 
 	eh.LastIndex = e.Index()
 	eh.LastIndex = e.Index()
 
 
-	eh.StartIndex = eh.Queue.Events[eh.Queue.Front].ModifiedIndex
+	eh.StartIndex = eh.Queue.Events[eh.Queue.Front].Index()
 
 
 	return e
 	return e
 }
 }
@@ -62,7 +62,7 @@ func (eh *EventHistory) scan(prefix string, index uint64) (*Event, *etcdErr.Erro
 	for {
 	for {
 		e := eh.Queue.Events[i]
 		e := eh.Queue.Events[i]
 
 
-		if strings.HasPrefix(e.Key, prefix) && index <= e.Index() { // make sure we bypass the smaller one
+		if strings.HasPrefix(e.Node.Key, prefix) && index <= e.Index() { // make sure we bypass the smaller one
 			return e, nil
 			return e, nil
 		}
 		}
 
 

+ 0 - 31
store/kv_pairs.go

@@ -1,31 +0,0 @@
-package store
-
-import (
-	"time"
-)
-
-// When user list a directory, we add all the node into key-value pair slice
-type KeyValuePair struct {
-	Key           string     `json:"key, omitempty"`
-	Value         string     `json:"value,omitempty"`
-	Dir           bool       `json:"dir,omitempty"`
-	Expiration    *time.Time `json:"expiration,omitempty"`
-	TTL           int64      `json:"ttl,omitempty"` // Time to live in second
-	KVPairs       kvPairs    `json:"kvs,omitempty"`
-	ModifiedIndex uint64     `json:"modifiedIndex,omitempty"`
-}
-
-type kvPairs []KeyValuePair
-
-// interfaces for sorting
-func (kvs kvPairs) Len() int {
-	return len(kvs)
-}
-
-func (kvs kvPairs) Less(i, j int) bool {
-	return kvs[i].Key < kvs[j].Key
-}
-
-func (kvs kvPairs) Swap(i, j int) {
-	kvs[i], kvs[j] = kvs[j], kvs[i]
-}

+ 21 - 21
store/node.go

@@ -16,7 +16,7 @@ var Permanent time.Time
 type node struct {
 type node struct {
 	Path string
 	Path string
 
 
-	CreateIndex   uint64
+	CreatedIndex  uint64
 	ModifiedIndex uint64
 	ModifiedIndex uint64
 
 
 	Parent *node `json:"-"` // should not encode this field! avoid circular dependency.
 	Parent *node `json:"-"` // should not encode this field! avoid circular dependency.
@@ -31,13 +31,13 @@ type node struct {
 }
 }
 
 
 // newKV creates a Key-Value pair
 // newKV creates a Key-Value pair
-func newKV(store *store, nodePath string, value string, createIndex uint64,
+func newKV(store *store, nodePath string, value string, createdIndex uint64,
 	parent *node, ACL string, expireTime time.Time) *node {
 	parent *node, ACL string, expireTime time.Time) *node {
 
 
 	return &node{
 	return &node{
 		Path:          nodePath,
 		Path:          nodePath,
-		CreateIndex:   createIndex,
-		ModifiedIndex: createIndex,
+		CreatedIndex:  createdIndex,
+		ModifiedIndex: createdIndex,
 		Parent:        parent,
 		Parent:        parent,
 		ACL:           ACL,
 		ACL:           ACL,
 		store:         store,
 		store:         store,
@@ -47,13 +47,13 @@ func newKV(store *store, nodePath string, value string, createIndex uint64,
 }
 }
 
 
 // newDir creates a directory
 // newDir creates a directory
-func newDir(store *store, nodePath string, createIndex uint64, parent *node,
+func newDir(store *store, nodePath string, createdIndex uint64, parent *node,
 	ACL string, expireTime time.Time) *node {
 	ACL string, expireTime time.Time) *node {
 
 
 	return &node{
 	return &node{
 		Path:          nodePath,
 		Path:          nodePath,
-		CreateIndex:   createIndex,
-		ModifiedIndex: createIndex,
+		CreatedIndex:  createdIndex,
+		ModifiedIndex: createdIndex,
 		Parent:        parent,
 		Parent:        parent,
 		ACL:           ACL,
 		ACL:           ACL,
 		ExpireTime:    expireTime,
 		ExpireTime:    expireTime,
@@ -223,21 +223,21 @@ func (n *node) Remove(recursive bool, callback func(path string)) *etcdErr.Error
 	return nil
 	return nil
 }
 }
 
 
-func (n *node) Pair(recurisive, sorted bool) KeyValuePair {
+func (n *node) Repr(recurisive, sorted bool) Node {
 	if n.IsDir() {
 	if n.IsDir() {
-		pair := KeyValuePair{
+		node := Node{
 			Key:           n.Path,
 			Key:           n.Path,
 			Dir:           true,
 			Dir:           true,
 			ModifiedIndex: n.ModifiedIndex,
 			ModifiedIndex: n.ModifiedIndex,
 		}
 		}
-		pair.Expiration, pair.TTL = n.ExpirationAndTTL()
+		node.Expiration, node.TTL = n.ExpirationAndTTL()
 
 
 		if !recurisive {
 		if !recurisive {
-			return pair
+			return node
 		}
 		}
 
 
 		children, _ := n.List()
 		children, _ := n.List()
-		pair.KVPairs = make([]KeyValuePair, len(children))
+		node.Nodes = make(Nodes, len(children))
 
 
 		// we do not use the index in the children slice directly
 		// we do not use the index in the children slice directly
 		// we need to skip the hidden one
 		// we need to skip the hidden one
@@ -249,27 +249,27 @@ func (n *node) Pair(recurisive, sorted bool) KeyValuePair {
 				continue
 				continue
 			}
 			}
 
 
-			pair.KVPairs[i] = child.Pair(recurisive, sorted)
+			node.Nodes[i] = child.Repr(recurisive, sorted)
 
 
 			i++
 			i++
 		}
 		}
 
 
 		// eliminate hidden nodes
 		// eliminate hidden nodes
-		pair.KVPairs = pair.KVPairs[:i]
+		node.Nodes = node.Nodes[:i]
 		if sorted {
 		if sorted {
-			sort.Sort(pair.KVPairs)
+			sort.Sort(node.Nodes)
 		}
 		}
 
 
-		return pair
+		return node
 	}
 	}
 
 
-	pair := KeyValuePair{
+	node := Node{
 		Key:           n.Path,
 		Key:           n.Path,
 		Value:         n.Value,
 		Value:         n.Value,
 		ModifiedIndex: n.ModifiedIndex,
 		ModifiedIndex: n.ModifiedIndex,
 	}
 	}
-	pair.Expiration, pair.TTL = n.ExpirationAndTTL()
-	return pair
+	node.Expiration, node.TTL = n.ExpirationAndTTL()
+	return node
 }
 }
 
 
 func (n *node) UpdateTTL(expireTime time.Time) {
 func (n *node) UpdateTTL(expireTime time.Time) {
@@ -301,10 +301,10 @@ func (n *node) UpdateTTL(expireTime time.Time) {
 // If the node is a key-value pair, it will clone the pair.
 // If the node is a key-value pair, it will clone the pair.
 func (n *node) Clone() *node {
 func (n *node) Clone() *node {
 	if !n.IsDir() {
 	if !n.IsDir() {
-		return newKV(n.store, n.Path, n.Value, n.CreateIndex, n.Parent, n.ACL, n.ExpireTime)
+		return newKV(n.store, n.Path, n.Value, n.CreatedIndex, n.Parent, n.ACL, n.ExpireTime)
 	}
 	}
 
 
-	clone := newDir(n.store, n.Path, n.CreateIndex, n.Parent, n.ACL, n.ExpireTime)
+	clone := newDir(n.store, n.Path, n.CreatedIndex, n.Parent, n.ACL, n.ExpireTime)
 
 
 	for key, child := range n.Children {
 	for key, child := range n.Children {
 		clone.Children[key] = child.Clone()
 		clone.Children[key] = child.Clone()

+ 35 - 0
store/node_repr.go

@@ -0,0 +1,35 @@
+package store
+
+import (
+	"time"
+)
+
+// Node is the representation of the internal node with additional fields
+// PrevValue is the previous value of the node
+// TTL is time to live in second
+type Node struct {
+	Key           string     `json:"key, omitempty"`
+	PrevValue     string     `json:"prevValue,omitempty"`
+	Value         string     `json:"value,omitempty"`
+	Dir           bool       `json:"dir,omitempty"`
+	Expiration    *time.Time `json:"expiration,omitempty"`
+	TTL           int64      `json:"ttl,omitempty"`
+	Nodes         Nodes      `json:"nodes,omitempty"`
+	ModifiedIndex uint64     `json:"modifiedIndex,omitempty"`
+	CreatedIndex  uint64     `json:"createdIndex,omitempty"`
+}
+
+type Nodes []Node
+
+// interfaces for sorting
+func (ns Nodes) Len() int {
+	return len(ns)
+}
+
+func (ns Nodes) Less(i, j int) bool {
+	return ns[i].Key < ns[j].Key
+}
+
+func (ns Nodes) Swap(i, j int) {
+	ns[i], ns[j] = ns[j], ns[i]
+}

+ 33 - 26
store/store.go

@@ -113,13 +113,14 @@ func (s *store) Get(nodePath string, recursive, sorted bool) (*Event, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	e := newEvent(Get, nodePath, n.ModifiedIndex)
+	e := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
+	eNode := e.Node
 
 
 	if n.IsDir() { // node is a directory
 	if n.IsDir() { // node is a directory
-		e.Dir = true
+		eNode.Dir = true
 
 
 		children, _ := n.List()
 		children, _ := n.List()
-		e.KVPairs = make([]KeyValuePair, len(children))
+		eNode.Nodes = make(Nodes, len(children))
 
 
 		// we do not use the index in the children slice directly
 		// we do not use the index in the children slice directly
 		// we need to skip the hidden one
 		// we need to skip the hidden one
@@ -130,22 +131,22 @@ func (s *store) Get(nodePath string, recursive, sorted bool) (*Event, error) {
 				continue
 				continue
 			}
 			}
 
 
-			e.KVPairs[i] = child.Pair(recursive, sorted)
+			eNode.Nodes[i] = child.Repr(recursive, sorted)
 			i++
 			i++
 		}
 		}
 
 
 		// eliminate hidden nodes
 		// eliminate hidden nodes
-		e.KVPairs = e.KVPairs[:i]
+		eNode.Nodes = eNode.Nodes[:i]
 
 
 		if sorted {
 		if sorted {
-			sort.Sort(e.KVPairs)
+			sort.Sort(eNode.Nodes)
 		}
 		}
 
 
 	} else { // node is a file
 	} else { // node is a file
-		e.Value, _ = n.Read()
+		eNode.Value, _ = n.Read()
 	}
 	}
 
 
-	e.Expiration, e.TTL = n.ExpirationAndTTL()
+	eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
 
 
 	s.Stats.Inc(GetSuccess)
 	s.Stats.Inc(GetSuccess)
 
 
@@ -214,15 +215,17 @@ func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint
 		// update etcd index
 		// update etcd index
 		s.CurrentIndex++
 		s.CurrentIndex++
 
 
-		e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex)
-		e.PrevValue = n.Value
+		e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex, n.CreatedIndex)
+		eNode := e.Node
+
+		eNode.PrevValue = n.Value
 
 
 		// if test succeed, write the value
 		// if test succeed, write the value
 		n.Write(value, s.CurrentIndex)
 		n.Write(value, s.CurrentIndex)
 		n.UpdateTTL(expireTime)
 		n.UpdateTTL(expireTime)
 
 
-		e.Value = value
-		e.Expiration, e.TTL = n.ExpirationAndTTL()
+		eNode.Value = value
+		eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
 
 
 		s.WatcherHub.notify(e)
 		s.WatcherHub.notify(e)
 		s.Stats.Inc(CompareAndSwapSuccess)
 		s.Stats.Inc(CompareAndSwapSuccess)
@@ -251,12 +254,13 @@ func (s *store) Delete(nodePath string, recursive bool) (*Event, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	e := newEvent(Delete, nodePath, nextIndex)
+	e := newEvent(Delete, nodePath, nextIndex, n.CreatedIndex)
+	eNode := e.Node
 
 
 	if n.IsDir() {
 	if n.IsDir() {
-		e.Dir = true
+		eNode.Dir = true
 	} else {
 	} else {
-		e.PrevValue = n.Value
+		eNode.PrevValue = eNode.Value
 	}
 	}
 
 
 	callback := func(path string) { // notify function
 	callback := func(path string) { // notify function
@@ -348,7 +352,8 @@ func (s *store) Update(nodePath string, newValue string, expireTime time.Time) (
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	e := newEvent(Update, nodePath, nextIndex)
+	e := newEvent(Update, nodePath, nextIndex, n.CreatedIndex)
+	eNode := e.Node
 
 
 	if len(newValue) != 0 {
 	if len(newValue) != 0 {
 		if n.IsDir() {
 		if n.IsDir() {
@@ -357,18 +362,19 @@ func (s *store) Update(nodePath string, newValue string, expireTime time.Time) (
 			return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
 			return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
 		}
 		}
 
 
-		e.PrevValue = n.Value
+		eNode.PrevValue = n.Value
 		n.Write(newValue, nextIndex)
 		n.Write(newValue, nextIndex)
-		e.Value = newValue
+		eNode.Value = newValue
+
 	} else {
 	} else {
 		// do not update value
 		// do not update value
-		e.Value = n.Value
+		eNode.Value = n.Value
 	}
 	}
 
 
 	// update ttl
 	// update ttl
 	n.UpdateTTL(expireTime)
 	n.UpdateTTL(expireTime)
 
 
-	e.Expiration, e.TTL = n.ExpirationAndTTL()
+	eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
 
 
 	s.WatcherHub.notify(e)
 	s.WatcherHub.notify(e)
 
 
@@ -407,7 +413,8 @@ func (s *store) internalCreate(nodePath string, value string, unique bool, repla
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	e := newEvent(action, nodePath, nextIndex)
+	e := newEvent(action, nodePath, nextIndex, nextIndex)
+	eNode := e.Node
 
 
 	n, _ := d.GetChild(newnodeName)
 	n, _ := d.GetChild(newnodeName)
 
 
@@ -417,7 +424,7 @@ func (s *store) internalCreate(nodePath string, value string, unique bool, repla
 			if n.IsDir() {
 			if n.IsDir() {
 				return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
 				return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
 			}
 			}
-			e.PrevValue, _ = n.Read()
+			eNode.PrevValue, _ = n.Read()
 
 
 			n.Remove(false, nil)
 			n.Remove(false, nil)
 		} else {
 		} else {
@@ -426,12 +433,12 @@ func (s *store) internalCreate(nodePath string, value string, unique bool, repla
 	}
 	}
 
 
 	if len(value) != 0 { // create file
 	if len(value) != 0 { // create file
-		e.Value = value
+		eNode.Value = value
 
 
 		n = newKV(s, nodePath, value, nextIndex, d, "", expireTime)
 		n = newKV(s, nodePath, value, nextIndex, d, "", expireTime)
 
 
 	} else { // create directory
 	} else { // create directory
-		e.Dir = true
+		eNode.Dir = true
 
 
 		n = newDir(s, nodePath, nextIndex, d, "", expireTime)
 		n = newDir(s, nodePath, nextIndex, d, "", expireTime)
 
 
@@ -444,7 +451,7 @@ func (s *store) internalCreate(nodePath string, value string, unique bool, repla
 	if !n.IsPermanent() {
 	if !n.IsPermanent() {
 		s.ttlKeyHeap.push(n)
 		s.ttlKeyHeap.push(n)
 
 
-		e.Expiration, e.TTL = n.ExpirationAndTTL()
+		eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
 	}
 	}
 
 
 	s.CurrentIndex = nextIndex
 	s.CurrentIndex = nextIndex
@@ -497,7 +504,7 @@ func (s *store) DeleteExpiredKeys(cutoff time.Time) {
 		s.CurrentIndex++
 		s.CurrentIndex++
 
 
 		s.Stats.Inc(ExpireCount)
 		s.Stats.Inc(ExpireCount)
-		s.WatcherHub.notify(newEvent(Expire, node.Path, s.CurrentIndex))
+		s.WatcherHub.notify(newEvent(Expire, node.Path, s.CurrentIndex, node.CreatedIndex))
 	}
 	}
 
 
 }
 }

+ 62 - 62
store/store_test.go

@@ -31,8 +31,8 @@ func TestStoreGetValue(t *testing.T) {
 	e, err := s.Get("/foo", false, false)
 	e, err := s.Get("/foo", false, false)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "get", "")
 	assert.Equal(t, e.Action, "get", "")
-	assert.Equal(t, e.Key, "/foo", "")
-	assert.Equal(t, e.Value, "bar", "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
+	assert.Equal(t, e.Node.Value, "bar", "")
 }
 }
 
 
 // Ensure that the store can recrusively retrieve a directory listing.
 // Ensure that the store can recrusively retrieve a directory listing.
@@ -49,21 +49,21 @@ func TestStoreGetDirectory(t *testing.T) {
 	e, err := s.Get("/foo", true, false)
 	e, err := s.Get("/foo", true, false)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "get", "")
 	assert.Equal(t, e.Action, "get", "")
-	assert.Equal(t, e.Key, "/foo", "")
-	assert.Equal(t, len(e.KVPairs), 2, "")
-	assert.Equal(t, e.KVPairs[0].Key, "/foo/bar", "")
-	assert.Equal(t, e.KVPairs[0].Value, "X", "")
-	assert.Equal(t, e.KVPairs[0].Dir, false, "")
-	assert.Equal(t, e.KVPairs[1].Key, "/foo/baz", "")
-	assert.Equal(t, e.KVPairs[1].Dir, true, "")
-	assert.Equal(t, len(e.KVPairs[1].KVPairs), 2, "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[0].Key, "/foo/baz/bat", "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[0].Value, "Y", "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[0].Dir, false, "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[1].Key, "/foo/baz/ttl", "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[1].Value, "Y", "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[1].Dir, false, "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[1].TTL, 3, "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
+	assert.Equal(t, len(e.Node.Nodes), 2, "")
+	assert.Equal(t, e.Node.Nodes[0].Key, "/foo/bar", "")
+	assert.Equal(t, e.Node.Nodes[0].Value, "X", "")
+	assert.Equal(t, e.Node.Nodes[0].Dir, false, "")
+	assert.Equal(t, e.Node.Nodes[1].Key, "/foo/baz", "")
+	assert.Equal(t, e.Node.Nodes[1].Dir, true, "")
+	assert.Equal(t, len(e.Node.Nodes[1].Nodes), 2, "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[0].Key, "/foo/baz/bat", "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[0].Value, "Y", "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[0].Dir, false, "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[1].Key, "/foo/baz/ttl", "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[1].Value, "Y", "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[1].Dir, false, "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[1].TTL, 3, "")
 }
 }
 
 
 // Ensure that the store can retrieve a directory in sorted order.
 // Ensure that the store can retrieve a directory in sorted order.
@@ -77,11 +77,11 @@ func TestStoreGetSorted(t *testing.T) {
 	s.Create("/foo/y/b", "0", false, Permanent)
 	s.Create("/foo/y/b", "0", false, Permanent)
 	e, err := s.Get("/foo", true, true)
 	e, err := s.Get("/foo", true, true)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
-	assert.Equal(t, e.KVPairs[0].Key, "/foo/x", "")
-	assert.Equal(t, e.KVPairs[1].Key, "/foo/y", "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[0].Key, "/foo/y/a", "")
-	assert.Equal(t, e.KVPairs[1].KVPairs[1].Key, "/foo/y/b", "")
-	assert.Equal(t, e.KVPairs[2].Key, "/foo/z", "")
+	assert.Equal(t, e.Node.Nodes[0].Key, "/foo/x", "")
+	assert.Equal(t, e.Node.Nodes[1].Key, "/foo/y", "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[0].Key, "/foo/y/a", "")
+	assert.Equal(t, e.Node.Nodes[1].Nodes[1].Key, "/foo/y/b", "")
+	assert.Equal(t, e.Node.Nodes[2].Key, "/foo/z", "")
 }
 }
 
 
 // Ensure that the store can create a new key if it doesn't already exist.
 // Ensure that the store can create a new key if it doesn't already exist.
@@ -90,14 +90,14 @@ func TestStoreCreateValue(t *testing.T) {
 	e, err := s.Create("/foo", "bar", false, Permanent)
 	e, err := s.Create("/foo", "bar", false, Permanent)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "create", "")
 	assert.Equal(t, e.Action, "create", "")
-	assert.Equal(t, e.Key, "/foo", "")
-	assert.False(t, e.Dir, "")
-	assert.Equal(t, e.PrevValue, "", "")
-	assert.Equal(t, e.Value, "bar", "")
-	assert.Nil(t, e.KVPairs, "")
-	assert.Nil(t, e.Expiration, "")
-	assert.Equal(t, e.TTL, 0, "")
-	assert.Equal(t, e.ModifiedIndex, uint64(1), "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
+	assert.False(t, e.Node.Dir, "")
+	assert.Equal(t, e.Node.PrevValue, "", "")
+	assert.Equal(t, e.Node.Value, "bar", "")
+	assert.Nil(t, e.Node.Nodes, "")
+	assert.Nil(t, e.Node.Expiration, "")
+	assert.Equal(t, e.Node.TTL, 0, "")
+	assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "")
 }
 }
 
 
 // Ensure that the store can create a new directory if it doesn't already exist.
 // Ensure that the store can create a new directory if it doesn't already exist.
@@ -106,8 +106,8 @@ func TestStoreCreateDirectory(t *testing.T) {
 	e, err := s.Create("/foo", "", false, Permanent)
 	e, err := s.Create("/foo", "", false, Permanent)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "create", "")
 	assert.Equal(t, e.Action, "create", "")
-	assert.Equal(t, e.Key, "/foo", "")
-	assert.True(t, e.Dir, "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
+	assert.True(t, e.Node.Dir, "")
 }
 }
 
 
 // Ensure that the store fails to create a key if it already exists.
 // Ensure that the store fails to create a key if it already exists.
@@ -130,14 +130,14 @@ func TestStoreUpdateValue(t *testing.T) {
 	e, err := s.Update("/foo", "baz", Permanent)
 	e, err := s.Update("/foo", "baz", Permanent)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "update", "")
 	assert.Equal(t, e.Action, "update", "")
-	assert.Equal(t, e.Key, "/foo", "")
-	assert.False(t, e.Dir, "")
-	assert.Equal(t, e.PrevValue, "bar", "")
-	assert.Equal(t, e.Value, "baz", "")
-	assert.Equal(t, e.TTL, 0, "")
-	assert.Equal(t, e.ModifiedIndex, uint64(2), "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
+	assert.False(t, e.Node.Dir, "")
+	assert.Equal(t, e.Node.PrevValue, "bar", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
+	assert.Equal(t, e.Node.TTL, 0, "")
+	assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
 	e, _ = s.Get("/foo", false, false)
 	e, _ = s.Get("/foo", false, false)
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 }
 }
 
 
 // Ensure that the store cannot update a directory.
 // Ensure that the store cannot update a directory.
@@ -165,7 +165,7 @@ func TestStoreUpdateValueTTL(t *testing.T) {
 	s.Create("/foo", "bar", false, Permanent)
 	s.Create("/foo", "bar", false, Permanent)
 	_, err := s.Update("/foo", "baz", time.Now().Add(500*time.Millisecond))
 	_, err := s.Update("/foo", "baz", time.Now().Add(500*time.Millisecond))
 	e, _ := s.Get("/foo", false, false)
 	e, _ := s.Get("/foo", false, false)
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 
 
 	time.Sleep(600 * time.Millisecond)
 	time.Sleep(600 * time.Millisecond)
 	e, err = s.Get("/foo", false, false)
 	e, err = s.Get("/foo", false, false)
@@ -187,7 +187,7 @@ func TestStoreUpdateDirTTL(t *testing.T) {
 	s.Create("/foo/bar", "baz", false, Permanent)
 	s.Create("/foo/bar", "baz", false, Permanent)
 	_, err := s.Update("/foo", "", time.Now().Add(500*time.Millisecond))
 	_, err := s.Update("/foo", "", time.Now().Add(500*time.Millisecond))
 	e, _ := s.Get("/foo/bar", false, false)
 	e, _ := s.Get("/foo/bar", false, false)
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 
 
 	time.Sleep(600 * time.Millisecond)
 	time.Sleep(600 * time.Millisecond)
 	e, err = s.Get("/foo/bar", false, false)
 	e, err = s.Get("/foo/bar", false, false)
@@ -231,10 +231,10 @@ func TestStoreCompareAndSwapPrevValue(t *testing.T) {
 	e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
 	e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "compareAndSwap", "")
 	assert.Equal(t, e.Action, "compareAndSwap", "")
-	assert.Equal(t, e.PrevValue, "bar", "")
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.PrevValue, "bar", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 	e, _ = s.Get("/foo", false, false)
 	e, _ = s.Get("/foo", false, false)
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 }
 }
 
 
 // Ensure that the store cannot conditionally update a key if it has the wrong previous value.
 // Ensure that the store cannot conditionally update a key if it has the wrong previous value.
@@ -247,7 +247,7 @@ func TestStoreCompareAndSwapPrevValueFailsIfNotMatch(t *testing.T) {
 	assert.Equal(t, err.Message, "Test Failed", "")
 	assert.Equal(t, err.Message, "Test Failed", "")
 	assert.Nil(t, e, "")
 	assert.Nil(t, e, "")
 	e, _ = s.Get("/foo", false, false)
 	e, _ = s.Get("/foo", false, false)
-	assert.Equal(t, e.Value, "bar", "")
+	assert.Equal(t, e.Node.Value, "bar", "")
 }
 }
 
 
 // Ensure that the store can conditionally update a key if it has a previous index.
 // Ensure that the store can conditionally update a key if it has a previous index.
@@ -257,10 +257,10 @@ func TestStoreCompareAndSwapPrevIndex(t *testing.T) {
 	e, err := s.CompareAndSwap("/foo", "", 1, "baz", Permanent)
 	e, err := s.CompareAndSwap("/foo", "", 1, "baz", Permanent)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
 	assert.Equal(t, e.Action, "compareAndSwap", "")
 	assert.Equal(t, e.Action, "compareAndSwap", "")
-	assert.Equal(t, e.PrevValue, "bar", "")
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.PrevValue, "bar", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 	e, _ = s.Get("/foo", false, false)
 	e, _ = s.Get("/foo", false, false)
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 }
 }
 
 
 // Ensure that the store cannot conditionally update a key if it has the wrong previous index.
 // Ensure that the store cannot conditionally update a key if it has the wrong previous index.
@@ -273,7 +273,7 @@ func TestStoreCompareAndSwapPrevIndexFailsIfNotMatch(t *testing.T) {
 	assert.Equal(t, err.Message, "Test Failed", "")
 	assert.Equal(t, err.Message, "Test Failed", "")
 	assert.Nil(t, e, "")
 	assert.Nil(t, e, "")
 	e, _ = s.Get("/foo", false, false)
 	e, _ = s.Get("/foo", false, false)
-	assert.Equal(t, e.Value, "bar", "")
+	assert.Equal(t, e.Node.Value, "bar", "")
 }
 }
 
 
 // Ensure that the store can watch for key creation.
 // Ensure that the store can watch for key creation.
@@ -283,7 +283,7 @@ func TestStoreWatchCreate(t *testing.T) {
 	s.Create("/foo", "bar", false, Permanent)
 	s.Create("/foo", "bar", false, Permanent)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "create", "")
 	assert.Equal(t, e.Action, "create", "")
-	assert.Equal(t, e.Key, "/foo", "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
 	e = nbselect(c)
 	e = nbselect(c)
 	assert.Nil(t, e, "")
 	assert.Nil(t, e, "")
 }
 }
@@ -295,7 +295,7 @@ func TestStoreWatchRecursiveCreate(t *testing.T) {
 	s.Create("/foo/bar", "baz", false, Permanent)
 	s.Create("/foo/bar", "baz", false, Permanent)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "create", "")
 	assert.Equal(t, e.Action, "create", "")
-	assert.Equal(t, e.Key, "/foo/bar", "")
+	assert.Equal(t, e.Node.Key, "/foo/bar", "")
 }
 }
 
 
 // Ensure that the store can watch for key updates.
 // Ensure that the store can watch for key updates.
@@ -306,7 +306,7 @@ func TestStoreWatchUpdate(t *testing.T) {
 	s.Update("/foo", "baz", Permanent)
 	s.Update("/foo", "baz", Permanent)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "update", "")
 	assert.Equal(t, e.Action, "update", "")
-	assert.Equal(t, e.Key, "/foo", "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
 }
 }
 
 
 // Ensure that the store can watch for recursive key updates.
 // Ensure that the store can watch for recursive key updates.
@@ -317,7 +317,7 @@ func TestStoreWatchRecursiveUpdate(t *testing.T) {
 	s.Update("/foo/bar", "baz", Permanent)
 	s.Update("/foo/bar", "baz", Permanent)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "update", "")
 	assert.Equal(t, e.Action, "update", "")
-	assert.Equal(t, e.Key, "/foo/bar", "")
+	assert.Equal(t, e.Node.Key, "/foo/bar", "")
 }
 }
 
 
 // Ensure that the store can watch for key deletions.
 // Ensure that the store can watch for key deletions.
@@ -328,7 +328,7 @@ func TestStoreWatchDelete(t *testing.T) {
 	s.Delete("/foo", false)
 	s.Delete("/foo", false)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "delete", "")
 	assert.Equal(t, e.Action, "delete", "")
-	assert.Equal(t, e.Key, "/foo", "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
 }
 }
 
 
 // Ensure that the store can watch for recursive key deletions.
 // Ensure that the store can watch for recursive key deletions.
@@ -339,7 +339,7 @@ func TestStoreWatchRecursiveDelete(t *testing.T) {
 	s.Delete("/foo/bar", false)
 	s.Delete("/foo/bar", false)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "delete", "")
 	assert.Equal(t, e.Action, "delete", "")
-	assert.Equal(t, e.Key, "/foo/bar", "")
+	assert.Equal(t, e.Node.Key, "/foo/bar", "")
 }
 }
 
 
 // Ensure that the store can watch for CAS updates.
 // Ensure that the store can watch for CAS updates.
@@ -350,7 +350,7 @@ func TestStoreWatchCompareAndSwap(t *testing.T) {
 	s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
 	s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "compareAndSwap", "")
 	assert.Equal(t, e.Action, "compareAndSwap", "")
-	assert.Equal(t, e.Key, "/foo", "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
 }
 }
 
 
 // Ensure that the store can watch for recursive CAS updates.
 // Ensure that the store can watch for recursive CAS updates.
@@ -361,7 +361,7 @@ func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) {
 	s.CompareAndSwap("/foo/bar", "baz", 0, "bat", Permanent)
 	s.CompareAndSwap("/foo/bar", "baz", 0, "bat", Permanent)
 	e := nbselect(c)
 	e := nbselect(c)
 	assert.Equal(t, e.Action, "compareAndSwap", "")
 	assert.Equal(t, e.Action, "compareAndSwap", "")
-	assert.Equal(t, e.Key, "/foo/bar", "")
+	assert.Equal(t, e.Node.Key, "/foo/bar", "")
 }
 }
 
 
 // Ensure that the store can watch for key expiration.
 // Ensure that the store can watch for key expiration.
@@ -383,11 +383,11 @@ func TestStoreWatchExpire(t *testing.T) {
 	time.Sleep(600 * time.Millisecond)
 	time.Sleep(600 * time.Millisecond)
 	e = nbselect(c)
 	e = nbselect(c)
 	assert.Equal(t, e.Action, "expire", "")
 	assert.Equal(t, e.Action, "expire", "")
-	assert.Equal(t, e.Key, "/foo", "")
+	assert.Equal(t, e.Node.Key, "/foo", "")
 	c, _ = s.Watch("/", true, 4)
 	c, _ = s.Watch("/", true, 4)
 	e = nbselect(c)
 	e = nbselect(c)
 	assert.Equal(t, e.Action, "expire", "")
 	assert.Equal(t, e.Action, "expire", "")
-	assert.Equal(t, e.Key, "/foofoo", "")
+	assert.Equal(t, e.Node.Key, "/foofoo", "")
 }
 }
 
 
 // Ensure that the store can recover from a previously saved state.
 // Ensure that the store can recover from a previously saved state.
@@ -403,11 +403,11 @@ func TestStoreRecover(t *testing.T) {
 
 
 	e, err := s.Get("/foo/x", false, false)
 	e, err := s.Get("/foo/x", false, false)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
-	assert.Equal(t, e.Value, "bar", "")
+	assert.Equal(t, e.Node.Value, "bar", "")
 
 
 	e, err = s.Get("/foo/y", false, false)
 	e, err = s.Get("/foo/y", false, false)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
-	assert.Equal(t, e.Value, "baz", "")
+	assert.Equal(t, e.Node.Value, "baz", "")
 }
 }
 
 
 // Ensure that the store can recover from a previously saved state that includes an expiring key.
 // Ensure that the store can recover from a previously saved state that includes an expiring key.
@@ -441,7 +441,7 @@ func TestStoreRecoverWithExpiration(t *testing.T) {
 
 
 	e, err := s.Get("/foo/x", false, false)
 	e, err := s.Get("/foo/x", false, false)
 	assert.Nil(t, err, "")
 	assert.Nil(t, err, "")
-	assert.Equal(t, e.Value, "bar", "")
+	assert.Equal(t, e.Node.Value, "bar", "")
 
 
 	e, err = s.Get("/foo/y", false, false)
 	e, err = s.Get("/foo/y", false, false)
 	assert.NotNil(t, err, "")
 	assert.NotNil(t, err, "")

+ 2 - 2
store/watcher_hub.go

@@ -77,7 +77,7 @@ func (wh *watcherHub) watch(prefix string, recursive bool, index uint64) (<-chan
 func (wh *watcherHub) notify(e *Event) {
 func (wh *watcherHub) notify(e *Event) {
 	e = wh.EventHistory.addEvent(e) // add event into the eventHistory
 	e = wh.EventHistory.addEvent(e) // add event into the eventHistory
 
 
-	segments := strings.Split(e.Key, "/")
+	segments := strings.Split(e.Node.Key, "/")
 
 
 	currPath := "/"
 	currPath := "/"
 
 
@@ -111,7 +111,7 @@ func (wh *watcherHub) notifyWatchers(e *Event, path string, deleted bool) {
 
 
 			w, _ := curr.Value.(*watcher)
 			w, _ := curr.Value.(*watcher)
 
 
-			if w.notify(e, e.Key == path, deleted) {
+			if w.notify(e, e.Node.Key == path, deleted) {
 
 
 				// if we successfully notify a watcher
 				// if we successfully notify a watcher
 				// we need to remove the watcher from the list
 				// we need to remove the watcher from the list