|
|
@@ -64,17 +64,17 @@ type Node struct {
|
|
|
type Response struct {
|
|
|
Action string `json:"action"`
|
|
|
Key string `json:"key"`
|
|
|
- PrevValue string `json:"prevValue"`
|
|
|
- Value string `json:"value"`
|
|
|
+ PrevValue string `json:"prevValue,omitempty"`
|
|
|
+ Value string `json:"value,omitempty"`
|
|
|
|
|
|
// If the key existed before the action, this field should be true
|
|
|
// If the key did not exist before the action, this field should be false
|
|
|
- Exist bool `json:"exist"`
|
|
|
+ NewKey bool `json:"newKey,omitempty"`
|
|
|
|
|
|
- Expiration time.Time `json:"expiration"`
|
|
|
+ Expiration *time.Time `json:"expiration,omitempty"`
|
|
|
|
|
|
// Time to live in second
|
|
|
- TTL int64 `json:"ttl"`
|
|
|
+ TTL int64 `json:"ttl,omitempty"`
|
|
|
|
|
|
// The command index of the raft machine when the command is executed
|
|
|
Index uint64 `json:"index"`
|
|
|
@@ -142,6 +142,14 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
|
|
|
|
|
|
isExpire := !expireTime.Equal(PERMANENT)
|
|
|
|
|
|
+ // base response
|
|
|
+ resp := Response{
|
|
|
+ Action: "SET",
|
|
|
+ Key: key,
|
|
|
+ Value: value,
|
|
|
+ Index: index,
|
|
|
+ }
|
|
|
+
|
|
|
// When the slow follower receive the set command
|
|
|
// the key may be expired, we should not add the node
|
|
|
// also if the node exist, we need to delete the node
|
|
|
@@ -154,10 +162,9 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
|
|
|
// Update ttl
|
|
|
if isExpire {
|
|
|
TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
|
|
- } else {
|
|
|
- // For permanent value, we set ttl to -1
|
|
|
- TTL = -1
|
|
|
- }
|
|
|
+ resp.Expiration = &expireTime
|
|
|
+ resp.TTL = TTL
|
|
|
+ }
|
|
|
|
|
|
// Get the node
|
|
|
node, ok := s.Tree.get(key)
|
|
|
@@ -186,7 +193,7 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
|
|
|
// Update the information of the node
|
|
|
s.Tree.set(key, Node{value, expireTime, node.update})
|
|
|
|
|
|
- resp := Response{"SET", key, node.Value, value, true, expireTime, TTL, index}
|
|
|
+ resp.PrevValue = node.Value
|
|
|
|
|
|
s.watcher.notify(resp)
|
|
|
|
|
|
@@ -207,13 +214,18 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
|
|
|
|
|
|
update := make(chan time.Time)
|
|
|
|
|
|
- s.Tree.set(key, Node{value, expireTime, update})
|
|
|
+ ok := s.Tree.set(key, Node{value, expireTime, update})
|
|
|
+
|
|
|
+ if !ok {
|
|
|
+ err := NotFile(key)
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
|
|
|
if isExpire {
|
|
|
go s.monitorExpiration(key, update, expireTime)
|
|
|
}
|
|
|
|
|
|
- resp := Response{"SET", key, "", value, false, expireTime, TTL, index}
|
|
|
+ resp.NewKey = true
|
|
|
|
|
|
msg, err := json.Marshal(resp)
|
|
|
|
|
|
@@ -232,7 +244,19 @@ func (s *Store) Set(key string, value string, expireTime time.Time, index uint64
|
|
|
}
|
|
|
|
|
|
// Get the value of the key
|
|
|
-func (s *Store) Get(key string) Response {
|
|
|
+func (s *Store) Get(key string) ([]byte, error) {
|
|
|
+ resp := s.internalGet(key)
|
|
|
+
|
|
|
+ if resp != nil {
|
|
|
+ return json.Marshal(resp)
|
|
|
+ } else {
|
|
|
+ err := NotFoundError(key)
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Get the value of the key and return the raw response
|
|
|
+func (s *Store) internalGet(key string) *Response {
|
|
|
|
|
|
key = path.Clean("/" + key)
|
|
|
|
|
|
@@ -244,21 +268,29 @@ func (s *Store) Get(key string) Response {
|
|
|
|
|
|
isExpire = !node.ExpireTime.Equal(PERMANENT)
|
|
|
|
|
|
+ resp := &Response{
|
|
|
+ Action: "GET",
|
|
|
+ Key: key,
|
|
|
+ Value: node.Value,
|
|
|
+ Index: s.Index,
|
|
|
+ }
|
|
|
+
|
|
|
// Update ttl
|
|
|
if isExpire {
|
|
|
TTL = int64(node.ExpireTime.Sub(time.Now()) / time.Second)
|
|
|
- } else {
|
|
|
- TTL = -1
|
|
|
- }
|
|
|
+ resp.Expiration = &node.ExpireTime
|
|
|
+ resp.TTL = TTL
|
|
|
+ }
|
|
|
|
|
|
- return Response{"GET", key, node.Value, node.Value, true, node.ExpireTime, TTL, s.Index}
|
|
|
+ return resp
|
|
|
|
|
|
} else {
|
|
|
// we do not found the key
|
|
|
- return Response{"GET", key, "", "", false, time.Unix(0, 0), 0, s.Index}
|
|
|
+ return nil
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
// List all the item in the prefix
|
|
|
func (s *Store) List(prefix string) ([]byte, error) {
|
|
|
|
|
|
@@ -273,7 +305,8 @@ func (s *Store) List(prefix string) ([]byte, error) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return json.Marshal(ln)
|
|
|
+ err := NotFoundError(prefix)
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
// Delete the key
|
|
|
@@ -288,20 +321,25 @@ func (s *Store) Delete(key string, index uint64) ([]byte, error) {
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
+ resp := Response{
|
|
|
+ Action: "DELETE",
|
|
|
+ Key: key,
|
|
|
+ PrevValue: node.Value,
|
|
|
+ Index: index,
|
|
|
+ }
|
|
|
+
|
|
|
if node.ExpireTime.Equal(PERMANENT) {
|
|
|
|
|
|
s.Tree.delete(key)
|
|
|
|
|
|
} else {
|
|
|
-
|
|
|
+ resp.Expiration = &node.ExpireTime
|
|
|
// Kill the expire go routine
|
|
|
node.update <- PERMANENT
|
|
|
s.Tree.delete(key)
|
|
|
|
|
|
}
|
|
|
|
|
|
- resp := Response{"DELETE", key, node.Value, "", true, node.ExpireTime, 0, index}
|
|
|
-
|
|
|
msg, err := json.Marshal(resp)
|
|
|
|
|
|
s.watcher.notify(resp)
|
|
|
@@ -317,28 +355,29 @@ func (s *Store) Delete(key string, index uint64) ([]byte, error) {
|
|
|
return msg, err
|
|
|
|
|
|
} else {
|
|
|
-
|
|
|
- resp := Response{"DELETE", key, "", "", false, time.Unix(0, 0), 0, index}
|
|
|
-
|
|
|
- s.addToResponseMap(index, &resp)
|
|
|
-
|
|
|
- return json.Marshal(resp)
|
|
|
+ err := NotFoundError(key)
|
|
|
+ return nil, err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Set the value of the key to the value if the given prevValue is equal to the value of the key
|
|
|
func (s *Store) TestAndSet(key string, prevValue string, value string, expireTime time.Time, index uint64) ([]byte, error) {
|
|
|
- resp := s.Get(key)
|
|
|
+ resp := s.internalGet(key)
|
|
|
+
|
|
|
+ if resp == nil {
|
|
|
+ err := NotFoundError(key)
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
|
|
|
- if resp.PrevValue == prevValue {
|
|
|
+ if resp.Value == prevValue {
|
|
|
|
|
|
// If test success, do set
|
|
|
return s.Set(key, value, expireTime, index)
|
|
|
} else {
|
|
|
|
|
|
- // If fails, return the result of get which contains the current
|
|
|
- // status of the key-value pair
|
|
|
- return json.Marshal(resp)
|
|
|
+ // If fails, return err
|
|
|
+ err := TestFail(fmt.Sprintf("%s==%s", resp.Value, prevValue))
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
}
|
|
|
@@ -371,7 +410,13 @@ func (s *Store) monitorExpiration(key string, update chan time.Time, expireTime
|
|
|
|
|
|
s.Tree.delete(key)
|
|
|
|
|
|
- resp := Response{"DELETE", key, node.Value, "", true, node.ExpireTime, 0, s.Index}
|
|
|
+ resp := Response{
|
|
|
+ Action: "DELETE",
|
|
|
+ Key: key,
|
|
|
+ PrevValue: node.Value,
|
|
|
+ Expiration: &node.ExpireTime,
|
|
|
+ Index: s.Index,
|
|
|
+ }
|
|
|
|
|
|
msg, err := json.Marshal(resp)
|
|
|
|