mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
refactor; add save and recover
This commit is contained in:
parent
a7eb09a557
commit
2c9c278e4d
@ -176,18 +176,18 @@ func dispatch(c Command, w http.ResponseWriter, req *http.Request, etcd bool) er
|
|||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
|
|
||||||
// Handler to return the current leader's raft address
|
// Handler to return the current leader's raft address
|
||||||
// func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
func LeaderHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
||||||
// leader := r.Leader()
|
leader := r.Leader()
|
||||||
|
|
||||||
// if leader != "" {
|
if leader != "" {
|
||||||
// w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
// raftURL, _ := nameToRaftURL(leader)
|
raftURL, _ := nameToRaftURL(leader)
|
||||||
// w.Write([]byte(raftURL))
|
w.Write([]byte(raftURL))
|
||||||
// return nil
|
return nil
|
||||||
// } else {
|
} else {
|
||||||
// return etcdErr.NewError(etcdErr.EcodeLeaderElect, "")
|
return etcdErr.NewError(etcdErr.EcodeLeaderElect, "")
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Handler to return all the known machines in the current cluster
|
// Handler to return all the known machines in the current cluster
|
||||||
func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
func MachinesHttpHandler(w http.ResponseWriter, req *http.Request) error {
|
||||||
|
|||||||
@ -2,10 +2,11 @@ package fileSystem
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -61,26 +62,26 @@ func newEvent(action string, key string, index uint64, term uint64) *Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type eventQueue struct {
|
type eventQueue struct {
|
||||||
events []*Event
|
Events []*Event
|
||||||
size int
|
Size int
|
||||||
front int
|
Front int
|
||||||
capacity int
|
Capacity int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eq *eventQueue) back() int {
|
func (eq *eventQueue) back() int {
|
||||||
return (eq.front + eq.size - 1 + eq.capacity) % eq.capacity
|
return (eq.Front + eq.Size - 1 + eq.Capacity) % eq.Capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eq *eventQueue) insert(e *Event) {
|
func (eq *eventQueue) insert(e *Event) {
|
||||||
|
|
||||||
index := (eq.back() + 1) % eq.capacity
|
index := (eq.back() + 1) % eq.Capacity
|
||||||
|
|
||||||
eq.events[index] = e
|
eq.Events[index] = e
|
||||||
|
|
||||||
if eq.size == eq.capacity { //dequeue
|
if eq.Size == eq.Capacity { //dequeue
|
||||||
eq.front = (index + 1) % eq.capacity
|
eq.Front = (index + 1) % eq.Capacity
|
||||||
} else {
|
} else {
|
||||||
eq.size++
|
eq.Size++
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -94,8 +95,8 @@ type EventHistory struct {
|
|||||||
func newEventHistory(capacity int) *EventHistory {
|
func newEventHistory(capacity int) *EventHistory {
|
||||||
return &EventHistory{
|
return &EventHistory{
|
||||||
Queue: eventQueue{
|
Queue: eventQueue{
|
||||||
capacity: capacity,
|
Capacity: capacity,
|
||||||
events: make([]*Event, capacity),
|
Events: make([]*Event, capacity),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +108,7 @@ func (eh *EventHistory) addEvent(e *Event) {
|
|||||||
|
|
||||||
eh.Queue.insert(e)
|
eh.Queue.insert(e)
|
||||||
|
|
||||||
eh.StartIndex = eh.Queue.events[eh.Queue.front].Index
|
eh.StartIndex = eh.Queue.Events[eh.Queue.Front].Index
|
||||||
}
|
}
|
||||||
|
|
||||||
// scan function is enumerating events from the index in history and
|
// scan function is enumerating events from the index in history and
|
||||||
@ -129,19 +130,19 @@ func (eh *EventHistory) scan(prefix string, index uint64) (*Event, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if start >= uint64(eh.Queue.size) {
|
if start >= uint64(eh.Queue.Size) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
i := int((start + uint64(eh.Queue.front)) % uint64(eh.Queue.capacity))
|
i := int((start + uint64(eh.Queue.Front)) % uint64(eh.Queue.Capacity))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
e := eh.Queue.events[i]
|
e := eh.Queue.Events[i]
|
||||||
if strings.HasPrefix(e.Key, prefix) {
|
if strings.HasPrefix(e.Key, prefix) {
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
i = (i + 1) % eh.Queue.capacity
|
i = (i + 1) % eh.Queue.Capacity
|
||||||
|
|
||||||
if i == eh.Queue.back() {
|
if i == eh.Queue.back() {
|
||||||
// TODO: Add error type
|
// TODO: Add error type
|
||||||
|
|||||||
@ -19,15 +19,15 @@ func TestEventQueue(t *testing.T) {
|
|||||||
|
|
||||||
// Test
|
// Test
|
||||||
j := 100
|
j := 100
|
||||||
i := eh.Queue.front
|
i := eh.Queue.Front
|
||||||
n := eh.Queue.size
|
n := eh.Queue.Size
|
||||||
for ; n > 0; n-- {
|
for ; n > 0; n-- {
|
||||||
e := eh.Queue.events[i]
|
e := eh.Queue.Events[i]
|
||||||
if e.Index != uint64(j) {
|
if e.Index != uint64(j) {
|
||||||
t.Fatalf("queue error!")
|
t.Fatalf("queue error!")
|
||||||
}
|
}
|
||||||
j++
|
j++
|
||||||
i = (i + 1) % eh.Queue.capacity
|
i = (i + 1) % eh.Queue.Capacity
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package fileSystem
|
package fileSystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
@ -11,11 +12,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type FileSystem struct {
|
type FileSystem struct {
|
||||||
Root *Node
|
Root *Node
|
||||||
EventHistory *EventHistory
|
WatcherHub *watcherHub
|
||||||
WatcherHub *watcherHub
|
Index uint64
|
||||||
Index uint64
|
Term uint64
|
||||||
Term uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *FileSystem {
|
func New() *FileSystem {
|
||||||
@ -126,7 +126,7 @@ func (fs *FileSystem) Create(nodePath string, value string, expireTime time.Time
|
|||||||
|
|
||||||
// Node with TTL
|
// Node with TTL
|
||||||
if expireTime != Permanent {
|
if expireTime != Permanent {
|
||||||
go n.Expire()
|
n.Expire()
|
||||||
e.Expiration = &n.ExpireTime
|
e.Expiration = &n.ExpireTime
|
||||||
e.TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
e.TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
||||||
}
|
}
|
||||||
@ -164,13 +164,13 @@ func (fs *FileSystem) Update(nodePath string, value string, expireTime time.Time
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update ttl
|
// update ttl
|
||||||
if n.ExpireTime != Permanent && expireTime != Permanent {
|
if !n.IsPermanent() && expireTime != Permanent {
|
||||||
n.stopExpire <- true
|
n.stopExpire <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
if expireTime != Permanent {
|
if expireTime.Sub(Permanent) != 0 {
|
||||||
n.ExpireTime = expireTime
|
n.ExpireTime = expireTime
|
||||||
go n.Expire()
|
n.Expire()
|
||||||
e.Expiration = &n.ExpireTime
|
e.Expiration = &n.ExpireTime
|
||||||
e.TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
e.TTL = int64(expireTime.Sub(time.Now()) / time.Second)
|
||||||
}
|
}
|
||||||
@ -298,7 +298,6 @@ func (fs *FileSystem) InternalGet(nodePath string, index uint64, term uint64) (*
|
|||||||
// If it does not exist, this function will create a new directory and 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.
|
// If it is a file, this function will return error.
|
||||||
func (fs *FileSystem) checkDir(parent *Node, dirName string) (*Node, error) {
|
func (fs *FileSystem) checkDir(parent *Node, dirName string) (*Node, error) {
|
||||||
|
|
||||||
subDir, ok := parent.Children[dirName]
|
subDir, ok := parent.Children[dirName]
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
@ -311,3 +310,35 @@ func (fs *FileSystem) checkDir(parent *Node, dirName string) (*Node, error) {
|
|||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save function saves the static state of the store system.
|
||||||
|
// 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 {
|
||||||
|
cloneFs := New()
|
||||||
|
cloneFs.Root = fs.Root.Clone()
|
||||||
|
|
||||||
|
b, err := json.Marshal(fs)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// recovery function recovery the store system from a static state.
|
||||||
|
// 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) Recover(state []byte) {
|
||||||
|
err := json.Unmarshal(state, fs)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Root.recoverAndclean()
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -311,7 +311,6 @@ func TestTestAndSet(t *testing.T) { // TODO prevValue == nil ?
|
|||||||
t.Fatalf("[%v/%v] [%v/%v]", e.PrevValue, "car", e.Value, "bar")
|
t.Fatalf("[%v/%v] [%v/%v]", e.PrevValue, "car", e.Value, "bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
//e, err = fs.TestAndSet("/foo", )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatch(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
@ -377,6 +376,115 @@ func TestWatch(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSort(t *testing.T) {
|
||||||
|
fs := New()
|
||||||
|
|
||||||
|
// simulating random creation
|
||||||
|
keys := GenKeys(80, 4)
|
||||||
|
|
||||||
|
i := uint64(1)
|
||||||
|
for _, k := range keys {
|
||||||
|
_, err := fs.Create(k, "bar", Permanent, i, 1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := fs.Get("/foo", true, true, i, 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get dir nodes failed [%s]", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, k := range e.KVPairs[:len(e.KVPairs)-1] {
|
||||||
|
|
||||||
|
if k.Key >= e.KVPairs[i+1].Key {
|
||||||
|
t.Fatalf("sort failed, [%s] should be placed after [%s]", k.Key, e.KVPairs[i+1].Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Dir {
|
||||||
|
recursiveTestSort(k, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if k := e.KVPairs[len(e.KVPairs)-1]; k.Dir {
|
||||||
|
recursiveTestSort(k, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveAndRecover(t *testing.T) {
|
||||||
|
fs := New()
|
||||||
|
|
||||||
|
// simulating random creation
|
||||||
|
keys := GenKeys(8, 4)
|
||||||
|
|
||||||
|
i := uint64(1)
|
||||||
|
for _, k := range keys {
|
||||||
|
_, err := fs.Create(k, "bar", Permanent, i, 1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a node with expiration
|
||||||
|
// test if we can reach the node before expiration
|
||||||
|
|
||||||
|
expire := time.Now().Add(time.Second)
|
||||||
|
fs.Create("/foo/foo", "bar", expire, 1, 1)
|
||||||
|
|
||||||
|
b := fs.Save()
|
||||||
|
|
||||||
|
cloneFs := New()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
cloneFs.Recover(b)
|
||||||
|
|
||||||
|
for i, k := range keys {
|
||||||
|
_, err := cloneFs.Get(k, false, false, uint64(i), 1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.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 !=
|
||||||
|
cloneFs.WatcherHub.EventHistory.Queue.Events[i].Key {
|
||||||
|
t.Fatal("Error recovered event history")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := fs.Get("/foo/foo", false, false, 1, 1)
|
||||||
|
|
||||||
|
if err == nil || err.Error() != "Key Not Found" {
|
||||||
|
t.Fatalf("can get the node after deletion ")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenKeys randomly generate num of keys with max depth
|
||||||
|
func GenKeys(num int, depth int) []string {
|
||||||
|
keys := make([]string, num)
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
|
||||||
|
keys[i] = "/foo"
|
||||||
|
depth := rand.Intn(depth) + 1
|
||||||
|
|
||||||
|
for j := 0; j < depth; j++ {
|
||||||
|
keys[i] += "/" + strconv.Itoa(rand.Int())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
func createAndGet(fs *FileSystem, path string, t *testing.T) {
|
func createAndGet(fs *FileSystem, path string, t *testing.T) {
|
||||||
_, err := fs.Create(path, "bar", Permanent, 1, 1)
|
_, err := fs.Create(path, "bar", Permanent, 1, 1)
|
||||||
|
|
||||||
@ -396,58 +504,8 @@ func createAndGet(fs *FileSystem, path string, t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func nonblockingRetrive(c <-chan *Event) *Event {
|
|
||||||
select {
|
|
||||||
case e := <-c:
|
|
||||||
return e
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSort(t *testing.T) {
|
|
||||||
fs := New()
|
|
||||||
|
|
||||||
// simulating random creation
|
|
||||||
keys := GenKeys(80, 4)
|
|
||||||
|
|
||||||
//t.Log(keys)
|
|
||||||
i := uint64(1)
|
|
||||||
for _, k := range keys {
|
|
||||||
_, err := fs.Create(k, "bar", Permanent, i, 1)
|
|
||||||
if err != nil {
|
|
||||||
//t.Logf("create node[%s] failed %s", k, err.Error())
|
|
||||||
} else {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e, err := fs.Get("/foo", true, true, i, 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("get dir nodes failed [%s]", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, k := range e.KVPairs[:len(e.KVPairs)-1] {
|
|
||||||
//t.Log("root:")
|
|
||||||
//t.Log(k)
|
|
||||||
if k.Key >= e.KVPairs[i+1].Key {
|
|
||||||
t.Fatalf("sort failed, [%s] should be placed after [%s]", k.Key, e.KVPairs[i+1].Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Dir {
|
|
||||||
recursiveTestSort(k, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if k := e.KVPairs[len(e.KVPairs)-1]; k.Dir {
|
|
||||||
recursiveTestSort(k, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func recursiveTestSort(k KeyValuePair, t *testing.T) {
|
func recursiveTestSort(k KeyValuePair, t *testing.T) {
|
||||||
//t.Log("recursive in")
|
|
||||||
//t.Log(k)
|
|
||||||
for i, v := range k.KVPairs[:len(k.KVPairs)-1] {
|
for i, v := range k.KVPairs[:len(k.KVPairs)-1] {
|
||||||
if v.Key >= k.KVPairs[i+1].Key {
|
if v.Key >= k.KVPairs[i+1].Key {
|
||||||
t.Fatalf("sort failed, [%s] should be placed after [%s]", v.Key, k.KVPairs[i+1].Key)
|
t.Fatalf("sort failed, [%s] should be placed after [%s]", v.Key, k.KVPairs[i+1].Key)
|
||||||
@ -464,17 +522,11 @@ func recursiveTestSort(k KeyValuePair, t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenKeys randomly generate num of keys with max depth
|
func nonblockingRetrive(c <-chan *Event) *Event {
|
||||||
func GenKeys(num int, depth int) []string {
|
select {
|
||||||
keys := make([]string, num)
|
case e := <-c:
|
||||||
for i := 0; i < num; i++ {
|
return e
|
||||||
|
default:
|
||||||
keys[i] = "/foo"
|
return nil
|
||||||
depth := rand.Intn(depth) + 1
|
|
||||||
|
|
||||||
for j := 0; j < depth; j++ {
|
|
||||||
keys[i] += "/" + strconv.Itoa(rand.Int()%20)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return keys
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package fileSystem
|
package fileSystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
@ -25,7 +24,7 @@ type Node struct {
|
|||||||
CreateTerm uint64
|
CreateTerm uint64
|
||||||
ModifiedIndex uint64
|
ModifiedIndex uint64
|
||||||
ModifiedTerm uint64
|
ModifiedTerm uint64
|
||||||
Parent *Node
|
Parent *Node `json:"-"`
|
||||||
ExpireTime time.Time
|
ExpireTime time.Time
|
||||||
ACL string
|
ACL string
|
||||||
Value string // for key-value pair
|
Value string // for key-value pair
|
||||||
@ -237,62 +236,89 @@ func (n *Node) Clone() *Node {
|
|||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDir function checks whether the node is a directory.
|
func (n *Node) recoverAndclean() {
|
||||||
// If the node is a directory, the function will return true.
|
if n.IsDir() {
|
||||||
// Otherwise the function will return false.
|
for _, child := range n.Children {
|
||||||
func (n *Node) IsDir() bool {
|
child.Parent = n
|
||||||
|
child.recoverAndclean()
|
||||||
if n.Children == nil { // key-value pair
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
n.stopExpire = make(chan bool, 1)
|
||||||
|
|
||||||
|
n.Expire()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Expire() {
|
func (n *Node) Expire() {
|
||||||
duration := n.ExpireTime.Sub(time.Now())
|
expired, duration := n.IsExpired()
|
||||||
if duration <= 0 {
|
|
||||||
|
if expired { // has been expired
|
||||||
n.Remove(true, nil)
|
n.Remove(true, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
if duration == 0 { // Permanent Node
|
||||||
// if timeout, delete the node
|
|
||||||
case <-time.After(duration):
|
|
||||||
n.Remove(true, nil)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
// if stopped, return
|
|
||||||
case <-n.stopExpire:
|
|
||||||
fmt.Println("expire stopped")
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go func() { // do monitoring
|
||||||
|
select {
|
||||||
|
// if timeout, delete the node
|
||||||
|
case <-time.After(duration):
|
||||||
|
n.Remove(true, nil)
|
||||||
|
return
|
||||||
|
|
||||||
|
// if stopped, return
|
||||||
|
case <-n.stopExpire:
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsHidden function checks if the node is a hidden node. A hidden node
|
// IsHidden function checks if the node is a hidden node. A hidden node
|
||||||
// will begin with '_'
|
// will begin with '_'
|
||||||
|
|
||||||
// A hidden node will not be shown via get command under a directory
|
// A hidden node will not be shown via get command under a directory
|
||||||
// For example if we have /foo/_hidden and /foo/notHidden, get "/foo"
|
// For example if we have /foo/_hidden and /foo/notHidden, get "/foo"
|
||||||
// will only return /foo/notHidden
|
// will only return /foo/notHidden
|
||||||
func (n *Node) IsHidden() bool {
|
func (n *Node) IsHidden() bool {
|
||||||
_, name := path.Split(n.Path)
|
_, name := path.Split(n.Path)
|
||||||
|
|
||||||
if name[0] == '_' { //hidden
|
return name[0] == '_'
|
||||||
return true
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) IsPermanent() bool {
|
||||||
|
return n.ExpireTime.Sub(Permanent) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) IsExpired() (bool, time.Duration) {
|
||||||
|
if n.IsPermanent() {
|
||||||
|
return false, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
duration := n.ExpireTime.Sub(time.Now())
|
||||||
|
if duration <= 0 {
|
||||||
|
return true, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDir function checks whether the node is a directory.
|
||||||
|
// If the node is a directory, the function will return true.
|
||||||
|
// Otherwise the function will return false.
|
||||||
|
func (n *Node) IsDir() bool {
|
||||||
|
return !(n.Children == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Pair(recurisive, sorted bool) KeyValuePair {
|
func (n *Node) Pair(recurisive, sorted bool) KeyValuePair {
|
||||||
|
|
||||||
if n.IsDir() {
|
if n.IsDir() {
|
||||||
pair := KeyValuePair{
|
pair := KeyValuePair{
|
||||||
Key: n.Path,
|
Key: n.Path,
|
||||||
Dir: true,
|
Dir: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !recurisive {
|
if !recurisive {
|
||||||
return pair
|
return pair
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,7 +99,6 @@ func (wh *watcherHub) notifyWithPath(e *Event, path string, force bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wh *watcherHub) notify(e *Event) {
|
func (wh *watcherHub) notify(e *Event) {
|
||||||
|
|
||||||
segments := strings.Split(e.Key, "/")
|
segments := strings.Split(e.Key, "/")
|
||||||
|
|
||||||
currPath := "/"
|
currPath := "/"
|
||||||
@ -109,4 +108,6 @@ func (wh *watcherHub) notify(e *Event) {
|
|||||||
currPath = path.Join(currPath, segment)
|
currPath = path.Join(currPath, segment)
|
||||||
wh.notifyWithPath(e, currPath, false)
|
wh.notifyWithPath(e, currPath, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wh.EventHistory.addEvent(e)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user