mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
store: switch to fake clock
This commit is contained in:
parent
47c2421f7b
commit
3134658ded
@ -5,6 +5,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -119,7 +120,7 @@ func (n *node) Write(value string, index uint64) *etcdErr.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) ExpirationAndTTL() (*time.Time, int64) {
|
func (n *node) expirationAndTTL(clock clockwork.Clock) (*time.Time, int64) {
|
||||||
if !n.IsPermanent() {
|
if !n.IsPermanent() {
|
||||||
/* compute ttl as:
|
/* compute ttl as:
|
||||||
ceiling( (expireTime - timeNow) / nanosecondsPerSecond )
|
ceiling( (expireTime - timeNow) / nanosecondsPerSecond )
|
||||||
@ -128,7 +129,7 @@ func (n *node) ExpirationAndTTL() (*time.Time, int64) {
|
|||||||
( (expireTime - timeNow) / nanosecondsPerSecond ) + 1
|
( (expireTime - timeNow) / nanosecondsPerSecond ) + 1
|
||||||
which ranges 1..n+1
|
which ranges 1..n+1
|
||||||
*/
|
*/
|
||||||
ttlN := n.ExpireTime.Sub(time.Now())
|
ttlN := n.ExpireTime.Sub(clock.Now())
|
||||||
ttl := ttlN / time.Second
|
ttl := ttlN / time.Second
|
||||||
if (ttlN % time.Second) > 0 {
|
if (ttlN % time.Second) > 0 {
|
||||||
ttl++
|
ttl++
|
||||||
@ -251,7 +252,7 @@ func (n *node) Remove(dir, recursive bool, callback func(path string)) *etcdErr.
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) Repr(recursive, sorted bool) *NodeExtern {
|
func (n *node) Repr(recursive, sorted bool, clock clockwork.Clock) *NodeExtern {
|
||||||
if n.IsDir() {
|
if n.IsDir() {
|
||||||
node := &NodeExtern{
|
node := &NodeExtern{
|
||||||
Key: n.Path,
|
Key: n.Path,
|
||||||
@ -259,7 +260,7 @@ func (n *node) Repr(recursive, sorted bool) *NodeExtern {
|
|||||||
ModifiedIndex: n.ModifiedIndex,
|
ModifiedIndex: n.ModifiedIndex,
|
||||||
CreatedIndex: n.CreatedIndex,
|
CreatedIndex: n.CreatedIndex,
|
||||||
}
|
}
|
||||||
node.Expiration, node.TTL = n.ExpirationAndTTL()
|
node.Expiration, node.TTL = n.expirationAndTTL(clock)
|
||||||
|
|
||||||
if !recursive {
|
if !recursive {
|
||||||
return node
|
return node
|
||||||
@ -278,7 +279,7 @@ func (n *node) Repr(recursive, sorted bool) *NodeExtern {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Nodes[i] = child.Repr(recursive, sorted)
|
node.Nodes[i] = child.Repr(recursive, sorted, clock)
|
||||||
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -300,7 +301,7 @@ func (n *node) Repr(recursive, sorted bool) *NodeExtern {
|
|||||||
ModifiedIndex: n.ModifiedIndex,
|
ModifiedIndex: n.ModifiedIndex,
|
||||||
CreatedIndex: n.CreatedIndex,
|
CreatedIndex: n.CreatedIndex,
|
||||||
}
|
}
|
||||||
node.Expiration, node.TTL = n.ExpirationAndTTL()
|
node.Expiration, node.TTL = n.expirationAndTTL(clock)
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ package store
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeExtern is the external representation of the
|
// NodeExtern is the external representation of the
|
||||||
@ -20,7 +22,7 @@ type NodeExtern struct {
|
|||||||
CreatedIndex uint64 `json:"createdIndex,omitempty"`
|
CreatedIndex uint64 `json:"createdIndex,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eNode *NodeExtern) loadInternalNode(n *node, recursive, sorted bool) {
|
func (eNode *NodeExtern) loadInternalNode(n *node, recursive, sorted bool, clock clockwork.Clock) {
|
||||||
if n.IsDir() { // node is a directory
|
if n.IsDir() { // node is a directory
|
||||||
eNode.Dir = true
|
eNode.Dir = true
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ func (eNode *NodeExtern) loadInternalNode(n *node, recursive, sorted bool) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
eNode.Nodes[i] = child.Repr(recursive, sorted)
|
eNode.Nodes[i] = child.Repr(recursive, sorted, clock)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ func (eNode *NodeExtern) loadInternalNode(n *node, recursive, sorted bool) {
|
|||||||
eNode.Value = &value
|
eNode.Value = &value
|
||||||
}
|
}
|
||||||
|
|
||||||
eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
|
eNode.Expiration, eNode.TTL = n.expirationAndTTL(clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeExterns []*NodeExtern
|
type NodeExterns []*NodeExtern
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,15 +88,12 @@ func TestStoreStatsDeleteFail(t *testing.T) {
|
|||||||
//Ensure that the number of expirations is recorded in the stats.
|
//Ensure that the number of expirations is recorded in the stats.
|
||||||
func TestStoreStatsExpireCount(t *testing.T) {
|
func TestStoreStatsExpireCount(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
fc := clockwork.NewFakeClock()
|
||||||
|
s.clock = fc
|
||||||
|
|
||||||
c := make(chan bool)
|
s.Create("/foo", false, "bar", false, fc.Now().Add(500*time.Millisecond))
|
||||||
defer func() {
|
|
||||||
c <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
go mockSyncService(s.DeleteExpiredKeys, c)
|
|
||||||
s.Create("/foo", false, "bar", false, time.Now().Add(500*time.Millisecond))
|
|
||||||
assert.Equal(t, uint64(0), s.Stats.ExpireCount, "")
|
assert.Equal(t, uint64(0), s.Stats.ExpireCount, "")
|
||||||
time.Sleep(600 * time.Millisecond)
|
fc.Tick(600 * time.Millisecond)
|
||||||
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
assert.Equal(t, uint64(1), s.Stats.ExpireCount, "")
|
assert.Equal(t, uint64(1), s.Stats.ExpireCount, "")
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,10 +69,13 @@ type store struct {
|
|||||||
CurrentVersion int
|
CurrentVersion int
|
||||||
ttlKeyHeap *ttlKeyHeap // need to recovery manually
|
ttlKeyHeap *ttlKeyHeap // need to recovery manually
|
||||||
worldLock sync.RWMutex // stop the world lock
|
worldLock sync.RWMutex // stop the world lock
|
||||||
|
clock clockwork.Clock
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() Store {
|
func New() Store {
|
||||||
return newStore()
|
s := newStore()
|
||||||
|
s.clock = clockwork.NewRealClock()
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStore() *store {
|
func newStore() *store {
|
||||||
@ -114,7 +118,7 @@ func (s *store) Get(nodePath string, recursive, sorted bool) (*Event, error) {
|
|||||||
|
|
||||||
e := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
|
e := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
|
||||||
e.EtcdIndex = s.CurrentIndex
|
e.EtcdIndex = s.CurrentIndex
|
||||||
e.Node.loadInternalNode(n, recursive, sorted)
|
e.Node.loadInternalNode(n, recursive, sorted, s.clock)
|
||||||
|
|
||||||
s.Stats.Inc(GetSuccess)
|
s.Stats.Inc(GetSuccess)
|
||||||
|
|
||||||
@ -172,7 +176,7 @@ func (s *store) Set(nodePath string, dir bool, value string, expireTime time.Tim
|
|||||||
// Put prevNode into event
|
// Put prevNode into event
|
||||||
if getErr == nil {
|
if getErr == nil {
|
||||||
prev := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
|
prev := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
|
||||||
prev.Node.loadInternalNode(n, false, false)
|
prev.Node.loadInternalNode(n, false, false, s.clock)
|
||||||
e.PrevNode = prev.Node
|
e.PrevNode = prev.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +234,7 @@ func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint
|
|||||||
|
|
||||||
e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex, n.CreatedIndex)
|
e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex, n.CreatedIndex)
|
||||||
e.EtcdIndex = s.CurrentIndex
|
e.EtcdIndex = s.CurrentIndex
|
||||||
e.PrevNode = n.Repr(false, false)
|
e.PrevNode = n.Repr(false, false, s.clock)
|
||||||
eNode := e.Node
|
eNode := e.Node
|
||||||
|
|
||||||
// if test succeed, write the value
|
// if test succeed, write the value
|
||||||
@ -240,7 +244,7 @@ func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint
|
|||||||
// copy the value for safety
|
// copy the value for safety
|
||||||
valueCopy := value
|
valueCopy := value
|
||||||
eNode.Value = &valueCopy
|
eNode.Value = &valueCopy
|
||||||
eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
|
eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
|
||||||
|
|
||||||
s.WatcherHub.notify(e)
|
s.WatcherHub.notify(e)
|
||||||
s.Stats.Inc(CompareAndSwapSuccess)
|
s.Stats.Inc(CompareAndSwapSuccess)
|
||||||
@ -275,7 +279,7 @@ func (s *store) Delete(nodePath string, dir, recursive bool) (*Event, error) {
|
|||||||
nextIndex := s.CurrentIndex + 1
|
nextIndex := s.CurrentIndex + 1
|
||||||
e := newEvent(Delete, nodePath, nextIndex, n.CreatedIndex)
|
e := newEvent(Delete, nodePath, nextIndex, n.CreatedIndex)
|
||||||
e.EtcdIndex = nextIndex
|
e.EtcdIndex = nextIndex
|
||||||
e.PrevNode = n.Repr(false, false)
|
e.PrevNode = n.Repr(false, false, s.clock)
|
||||||
eNode := e.Node
|
eNode := e.Node
|
||||||
|
|
||||||
if n.IsDir() {
|
if n.IsDir() {
|
||||||
@ -335,7 +339,7 @@ func (s *store) CompareAndDelete(nodePath string, prevValue string, prevIndex ui
|
|||||||
|
|
||||||
e := newEvent(CompareAndDelete, nodePath, s.CurrentIndex, n.CreatedIndex)
|
e := newEvent(CompareAndDelete, nodePath, s.CurrentIndex, n.CreatedIndex)
|
||||||
e.EtcdIndex = s.CurrentIndex
|
e.EtcdIndex = s.CurrentIndex
|
||||||
e.PrevNode = n.Repr(false, false)
|
e.PrevNode = n.Repr(false, false, s.clock)
|
||||||
|
|
||||||
callback := func(path string) { // notify function
|
callback := func(path string) { // notify function
|
||||||
// notify the watchers with deleted set true
|
// notify the watchers with deleted set true
|
||||||
@ -414,7 +418,7 @@ func (s *store) Update(nodePath string, newValue string, expireTime time.Time) (
|
|||||||
|
|
||||||
e := newEvent(Update, nodePath, nextIndex, n.CreatedIndex)
|
e := newEvent(Update, nodePath, nextIndex, n.CreatedIndex)
|
||||||
e.EtcdIndex = nextIndex
|
e.EtcdIndex = nextIndex
|
||||||
e.PrevNode = n.Repr(false, false)
|
e.PrevNode = n.Repr(false, false, s.clock)
|
||||||
eNode := e.Node
|
eNode := e.Node
|
||||||
|
|
||||||
if n.IsDir() && len(newValue) != 0 {
|
if n.IsDir() && len(newValue) != 0 {
|
||||||
@ -436,7 +440,7 @@ func (s *store) Update(nodePath string, newValue string, expireTime time.Time) (
|
|||||||
// update ttl
|
// update ttl
|
||||||
n.UpdateTTL(expireTime)
|
n.UpdateTTL(expireTime)
|
||||||
|
|
||||||
eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
|
eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
|
||||||
|
|
||||||
s.WatcherHub.notify(e)
|
s.WatcherHub.notify(e)
|
||||||
|
|
||||||
@ -463,12 +467,6 @@ func (s *store) internalCreate(nodePath string, dir bool, value string, unique,
|
|||||||
return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", currIndex)
|
return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", currIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume expire times that are way in the past are not valid.
|
|
||||||
// This can occur when the time is serialized to JSON and read back in.
|
|
||||||
if expireTime.Before(minExpireTime) {
|
|
||||||
expireTime = Permanent
|
|
||||||
}
|
|
||||||
|
|
||||||
dirName, nodeName := path.Split(nodePath)
|
dirName, nodeName := path.Split(nodePath)
|
||||||
|
|
||||||
// walk through the nodePath, create dirs and get the last directory node
|
// walk through the nodePath, create dirs and get the last directory node
|
||||||
@ -491,7 +489,7 @@ func (s *store) internalCreate(nodePath string, dir bool, value string, unique,
|
|||||||
if n.IsDir() {
|
if n.IsDir() {
|
||||||
return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
|
return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
|
||||||
}
|
}
|
||||||
e.PrevNode = n.Repr(false, false)
|
e.PrevNode = n.Repr(false, false, s.clock)
|
||||||
|
|
||||||
n.Remove(false, false, nil)
|
n.Remove(false, false, nil)
|
||||||
} else {
|
} else {
|
||||||
@ -519,7 +517,7 @@ func (s *store) internalCreate(nodePath string, dir bool, value string, unique,
|
|||||||
if !n.IsPermanent() {
|
if !n.IsPermanent() {
|
||||||
s.ttlKeyHeap.push(n)
|
s.ttlKeyHeap.push(n)
|
||||||
|
|
||||||
eNode.Expiration, eNode.TTL = n.ExpirationAndTTL()
|
eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.CurrentIndex = nextIndex
|
s.CurrentIndex = nextIndex
|
||||||
@ -568,7 +566,7 @@ func (s *store) DeleteExpiredKeys(cutoff time.Time) {
|
|||||||
s.CurrentIndex++
|
s.CurrentIndex++
|
||||||
e := newEvent(Expire, node.Path, s.CurrentIndex, node.CreatedIndex)
|
e := newEvent(Expire, node.Path, s.CurrentIndex, node.CreatedIndex)
|
||||||
e.EtcdIndex = s.CurrentIndex
|
e.EtcdIndex = s.CurrentIndex
|
||||||
e.PrevNode = node.Repr(false, false)
|
e.PrevNode = node.Repr(false, false, s.clock)
|
||||||
|
|
||||||
callback := func(path string) { // notify function
|
callback := func(path string) { // notify function
|
||||||
// notify the watchers with deleted set true
|
// notify the watchers with deleted set true
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
)
|
)
|
||||||
@ -41,13 +42,15 @@ func TestStoreGetValue(t *testing.T) {
|
|||||||
// Note that hidden files should not be returned.
|
// Note that hidden files should not be returned.
|
||||||
func TestStoreGetDirectory(t *testing.T) {
|
func TestStoreGetDirectory(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
fc := clockwork.NewFakeClock()
|
||||||
|
s.clock = fc
|
||||||
s.Create("/foo", true, "", false, Permanent)
|
s.Create("/foo", true, "", false, Permanent)
|
||||||
s.Create("/foo/bar", false, "X", false, Permanent)
|
s.Create("/foo/bar", false, "X", false, Permanent)
|
||||||
s.Create("/foo/_hidden", false, "*", false, Permanent)
|
s.Create("/foo/_hidden", false, "*", false, Permanent)
|
||||||
s.Create("/foo/baz", true, "", false, Permanent)
|
s.Create("/foo/baz", true, "", false, Permanent)
|
||||||
s.Create("/foo/baz/bat", false, "Y", false, Permanent)
|
s.Create("/foo/baz/bat", false, "Y", false, Permanent)
|
||||||
s.Create("/foo/baz/_hidden", false, "*", false, Permanent)
|
s.Create("/foo/baz/_hidden", false, "*", false, Permanent)
|
||||||
s.Create("/foo/baz/ttl", false, "Y", false, time.Now().Add(time.Second*3))
|
s.Create("/foo/baz/ttl", false, "Y", false, fc.Now().Add(time.Second*3))
|
||||||
var eidx uint64 = 7
|
var eidx uint64 = 7
|
||||||
e, err := s.Get("/foo", true, false)
|
e, err := s.Get("/foo", true, false)
|
||||||
assert.Nil(t, err, "")
|
assert.Nil(t, err, "")
|
||||||
@ -311,21 +314,17 @@ func TestStoreUpdateFailsIfDirectory(t *testing.T) {
|
|||||||
// Ensure that the store can update the TTL on a value.
|
// Ensure that the store can update the TTL on a value.
|
||||||
func TestStoreUpdateValueTTL(t *testing.T) {
|
func TestStoreUpdateValueTTL(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
fc := clockwork.NewFakeClock()
|
||||||
c := make(chan bool)
|
s.clock = fc
|
||||||
defer func() {
|
|
||||||
c <- true
|
|
||||||
}()
|
|
||||||
go mockSyncService(s.DeleteExpiredKeys, c)
|
|
||||||
|
|
||||||
var eidx uint64 = 2
|
var eidx uint64 = 2
|
||||||
s.Create("/foo", false, "bar", false, Permanent)
|
s.Create("/foo", false, "bar", false, Permanent)
|
||||||
_, err := s.Update("/foo", "baz", time.Now().Add(500*time.Millisecond))
|
_, err := s.Update("/foo", "baz", fc.Now().Add(500*time.Millisecond))
|
||||||
e, _ := s.Get("/foo", false, false)
|
e, _ := s.Get("/foo", false, false)
|
||||||
assert.Equal(t, *e.Node.Value, "baz", "")
|
assert.Equal(t, *e.Node.Value, "baz", "")
|
||||||
assert.Equal(t, e.EtcdIndex, eidx, "")
|
assert.Equal(t, e.EtcdIndex, eidx, "")
|
||||||
|
fc.Tick(600 * time.Millisecond)
|
||||||
time.Sleep(600 * time.Millisecond)
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
e, err = s.Get("/foo", false, false)
|
e, err = s.Get("/foo", false, false)
|
||||||
assert.Nil(t, e, "")
|
assert.Nil(t, e, "")
|
||||||
assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
|
assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
|
||||||
@ -334,24 +333,21 @@ func TestStoreUpdateValueTTL(t *testing.T) {
|
|||||||
// Ensure that the store can update the TTL on a directory.
|
// Ensure that the store can update the TTL on a directory.
|
||||||
func TestStoreUpdateDirTTL(t *testing.T) {
|
func TestStoreUpdateDirTTL(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
fc := clockwork.NewFakeClock()
|
||||||
c := make(chan bool)
|
s.clock = fc
|
||||||
defer func() {
|
|
||||||
c <- true
|
|
||||||
}()
|
|
||||||
go mockSyncService(s.DeleteExpiredKeys, c)
|
|
||||||
|
|
||||||
var eidx uint64 = 3
|
var eidx uint64 = 3
|
||||||
s.Create("/foo", true, "", false, Permanent)
|
s.Create("/foo", true, "", false, Permanent)
|
||||||
s.Create("/foo/bar", false, "baz", false, Permanent)
|
s.Create("/foo/bar", false, "baz", false, Permanent)
|
||||||
e, err := s.Update("/foo", "", time.Now().Add(500*time.Millisecond))
|
e, err := s.Update("/foo", "", fc.Now().Add(500*time.Millisecond))
|
||||||
assert.Equal(t, e.Node.Dir, true, "")
|
assert.Equal(t, e.Node.Dir, true, "")
|
||||||
assert.Equal(t, e.EtcdIndex, eidx, "")
|
assert.Equal(t, e.EtcdIndex, eidx, "")
|
||||||
e, _ = s.Get("/foo/bar", false, false)
|
e, _ = s.Get("/foo/bar", false, false)
|
||||||
assert.Equal(t, *e.Node.Value, "baz", "")
|
assert.Equal(t, *e.Node.Value, "baz", "")
|
||||||
assert.Equal(t, e.EtcdIndex, eidx, "")
|
assert.Equal(t, e.EtcdIndex, eidx, "")
|
||||||
|
|
||||||
time.Sleep(600 * time.Millisecond)
|
fc.Tick(600 * time.Millisecond)
|
||||||
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
e, err = s.Get("/foo/bar", false, false)
|
e, err = s.Get("/foo/bar", false, false)
|
||||||
assert.Nil(t, e, "")
|
assert.Nil(t, e, "")
|
||||||
assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
|
assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
|
||||||
@ -707,23 +703,20 @@ func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) {
|
|||||||
// Ensure that the store can watch for key expiration.
|
// Ensure that the store can watch for key expiration.
|
||||||
func TestStoreWatchExpire(t *testing.T) {
|
func TestStoreWatchExpire(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
fc := clockwork.NewFakeClock()
|
||||||
stopChan := make(chan bool)
|
s.clock = fc
|
||||||
defer func() {
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
go mockSyncService(s.DeleteExpiredKeys, stopChan)
|
|
||||||
|
|
||||||
var eidx uint64 = 2
|
var eidx uint64 = 2
|
||||||
s.Create("/foo", false, "bar", false, time.Now().Add(500*time.Millisecond))
|
s.Create("/foo", false, "bar", false, fc.Now().Add(500*time.Millisecond))
|
||||||
s.Create("/foofoo", false, "barbarbar", false, time.Now().Add(500*time.Millisecond))
|
s.Create("/foofoo", false, "barbarbar", false, fc.Now().Add(500*time.Millisecond))
|
||||||
|
|
||||||
w, _ := s.Watch("/", true, false, 0)
|
w, _ := s.Watch("/", true, false, 0)
|
||||||
assert.Equal(t, w.StartIndex(), eidx, "")
|
assert.Equal(t, w.StartIndex(), eidx, "")
|
||||||
c := w.EventChan()
|
c := w.EventChan()
|
||||||
e := nbselect(c)
|
e := nbselect(c)
|
||||||
assert.Nil(t, e, "")
|
assert.Nil(t, e, "")
|
||||||
time.Sleep(600 * time.Millisecond)
|
fc.Tick(600 * time.Millisecond)
|
||||||
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
eidx = 3
|
eidx = 3
|
||||||
e = nbselect(c)
|
e = nbselect(c)
|
||||||
assert.Equal(t, e.EtcdIndex, eidx, "")
|
assert.Equal(t, e.EtcdIndex, eidx, "")
|
||||||
@ -790,32 +783,25 @@ func TestStoreRecover(t *testing.T) {
|
|||||||
// 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.
|
||||||
func TestStoreRecoverWithExpiration(t *testing.T) {
|
func TestStoreRecoverWithExpiration(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
s.clock = clockwork.NewFakeClock()
|
||||||
|
|
||||||
c := make(chan bool)
|
fc := clockwork.NewFakeClock()
|
||||||
defer func() {
|
|
||||||
c <- true
|
|
||||||
}()
|
|
||||||
go mockSyncService(s.DeleteExpiredKeys, c)
|
|
||||||
|
|
||||||
var eidx uint64 = 4
|
var eidx uint64 = 4
|
||||||
s.Create("/foo", true, "", false, Permanent)
|
s.Create("/foo", true, "", false, Permanent)
|
||||||
s.Create("/foo/x", false, "bar", false, Permanent)
|
s.Create("/foo/x", false, "bar", false, Permanent)
|
||||||
s.Create("/foo/y", false, "baz", false, time.Now().Add(5*time.Millisecond))
|
s.Create("/foo/y", false, "baz", false, fc.Now().Add(5*time.Millisecond))
|
||||||
b, err := s.Save()
|
b, err := s.Save()
|
||||||
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
s2 := newStore()
|
s2 := newStore()
|
||||||
|
s2.clock = fc
|
||||||
c2 := make(chan bool)
|
|
||||||
defer func() {
|
|
||||||
c2 <- true
|
|
||||||
}()
|
|
||||||
go mockSyncService(s2.DeleteExpiredKeys, c2)
|
|
||||||
|
|
||||||
s2.Recovery(b)
|
s2.Recovery(b)
|
||||||
|
|
||||||
time.Sleep(600 * time.Millisecond)
|
fc.Tick(600 * time.Millisecond)
|
||||||
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
|
|
||||||
e, err := s.Get("/foo/x", false, false)
|
e, err := s.Get("/foo/x", false, false)
|
||||||
assert.Nil(t, err, "")
|
assert.Nil(t, err, "")
|
||||||
@ -908,24 +894,22 @@ func TestStoreWatchRecursiveDeleteWithHiddenKey(t *testing.T) {
|
|||||||
// Ensure that the store doesn't see expirations of hidden keys.
|
// Ensure that the store doesn't see expirations of hidden keys.
|
||||||
func TestStoreWatchExpireWithHiddenKey(t *testing.T) {
|
func TestStoreWatchExpireWithHiddenKey(t *testing.T) {
|
||||||
s := newStore()
|
s := newStore()
|
||||||
|
fc := clockwork.NewFakeClock()
|
||||||
|
s.clock = fc
|
||||||
|
|
||||||
stopChan := make(chan bool)
|
s.Create("/_foo", false, "bar", false, fc.Now().Add(500*time.Millisecond))
|
||||||
defer func() {
|
s.Create("/foofoo", false, "barbarbar", false, fc.Now().Add(1000*time.Millisecond))
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
go mockSyncService(s.DeleteExpiredKeys, stopChan)
|
|
||||||
|
|
||||||
s.Create("/_foo", false, "bar", false, time.Now().Add(500*time.Millisecond))
|
|
||||||
s.Create("/foofoo", false, "barbarbar", false, time.Now().Add(1000*time.Millisecond))
|
|
||||||
|
|
||||||
w, _ := s.Watch("/", true, false, 0)
|
w, _ := s.Watch("/", true, false, 0)
|
||||||
c := w.EventChan()
|
c := w.EventChan()
|
||||||
e := nbselect(c)
|
e := nbselect(c)
|
||||||
assert.Nil(t, e, "")
|
assert.Nil(t, e, "")
|
||||||
time.Sleep(600 * time.Millisecond)
|
fc.Tick(600 * time.Millisecond)
|
||||||
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
e = nbselect(c)
|
e = nbselect(c)
|
||||||
assert.Nil(t, e, "")
|
assert.Nil(t, e, "")
|
||||||
time.Sleep(600 * time.Millisecond)
|
fc.Tick(600 * time.Millisecond)
|
||||||
|
s.DeleteExpiredKeys(fc.Now())
|
||||||
e = nbselect(c)
|
e = nbselect(c)
|
||||||
assert.Equal(t, e.Action, "expire", "")
|
assert.Equal(t, e.Action, "expire", "")
|
||||||
assert.Equal(t, e.Node.Key, "/foofoo", "")
|
assert.Equal(t, e.Node.Key, "/foofoo", "")
|
||||||
@ -969,15 +953,3 @@ func nbselect(c <-chan *Event) *Event {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mockSyncService(f func(now time.Time), c chan bool) {
|
|
||||||
ticker := time.Tick(time.Millisecond * 500)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-c:
|
|
||||||
return
|
|
||||||
case now := <-ticker:
|
|
||||||
f(now)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user