mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #695 from mavenugo/watch_retry
Add watch retrigger when store restarts
This commit is contained in:
commit
15871f32fc
5 changed files with 72 additions and 17 deletions
|
@ -218,7 +218,14 @@ func (c *controller) initDiscovery(watcher discovery.Watcher) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.discovery = hostdiscovery.NewHostDiscovery(watcher)
|
c.discovery = hostdiscovery.NewHostDiscovery(watcher)
|
||||||
return c.discovery.Watch(c.hostJoinCallback, c.hostLeaveCallback)
|
return c.discovery.Watch(c.activeCallback, c.hostJoinCallback, c.hostLeaveCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) activeCallback() {
|
||||||
|
ds := c.getStore(datastore.GlobalScope)
|
||||||
|
if ds != nil && !ds.Active() {
|
||||||
|
ds.RestartWatch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) hostJoinCallback(nodes []net.IP) {
|
func (c *controller) hostJoinCallback(nodes []net.IP) {
|
||||||
|
|
|
@ -34,6 +34,10 @@ type DataStore interface {
|
||||||
Watchable() bool
|
Watchable() bool
|
||||||
// Watch for changes on a KVObject
|
// Watch for changes on a KVObject
|
||||||
Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error)
|
Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error)
|
||||||
|
// RestartWatch retriggers stopped Watches
|
||||||
|
RestartWatch()
|
||||||
|
// Active returns if the store is active
|
||||||
|
Active() bool
|
||||||
// List returns of a list of KVObjects belonging to the parent
|
// List returns of a list of KVObjects belonging to the parent
|
||||||
// key. The caller must pass a KVObject of the same type as
|
// key. The caller must pass a KVObject of the same type as
|
||||||
// the objects that need to be listed
|
// the objects that need to be listed
|
||||||
|
@ -53,9 +57,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type datastore struct {
|
type datastore struct {
|
||||||
scope string
|
scope string
|
||||||
store store.Store
|
store store.Store
|
||||||
cache *cache
|
cache *cache
|
||||||
|
watchCh chan struct{}
|
||||||
|
active bool
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +210,7 @@ func newClient(scope string, kv string, addr string, config *store.Config, cache
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ds := &datastore{scope: scope, store: store}
|
ds := &datastore{scope: scope, store: store, active: true, watchCh: make(chan struct{})}
|
||||||
if cached {
|
if cached {
|
||||||
ds.cache = newCache(ds)
|
ds.cache = newCache(ds)
|
||||||
}
|
}
|
||||||
|
@ -239,6 +245,10 @@ func (ds *datastore) Scope() string {
|
||||||
return ds.scope
|
return ds.scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ds *datastore) Active() bool {
|
||||||
|
return ds.active
|
||||||
|
}
|
||||||
|
|
||||||
func (ds *datastore) Watchable() bool {
|
func (ds *datastore) Watchable() bool {
|
||||||
return ds.scope != LocalScope
|
return ds.scope != LocalScope
|
||||||
}
|
}
|
||||||
|
@ -259,6 +269,15 @@ func (ds *datastore) Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KV
|
||||||
kvoCh := make(chan KVObject)
|
kvoCh := make(chan KVObject)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
retry_watch:
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Make sure to get a new instance of watch channel
|
||||||
|
ds.Lock()
|
||||||
|
watchCh := ds.watchCh
|
||||||
|
ds.Unlock()
|
||||||
|
|
||||||
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-stopCh:
|
case <-stopCh:
|
||||||
|
@ -269,12 +288,15 @@ func (ds *datastore) Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KV
|
||||||
// for the watch can exit resulting in a nil value in
|
// for the watch can exit resulting in a nil value in
|
||||||
// channel.
|
// channel.
|
||||||
if kvPair == nil {
|
if kvPair == nil {
|
||||||
close(sCh)
|
ds.Lock()
|
||||||
return
|
ds.active = false
|
||||||
|
ds.Unlock()
|
||||||
|
break loop
|
||||||
}
|
}
|
||||||
|
|
||||||
dstO := ctor.New()
|
dstO := ctor.New()
|
||||||
|
|
||||||
if err := dstO.SetValue(kvPair.Value); err != nil {
|
if err = dstO.SetValue(kvPair.Value); err != nil {
|
||||||
log.Printf("Could not unmarshal kvpair value = %s", string(kvPair.Value))
|
log.Printf("Could not unmarshal kvpair value = %s", string(kvPair.Value))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -283,11 +305,31 @@ func (ds *datastore) Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KV
|
||||||
kvoCh <- dstO
|
kvoCh <- dstO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait on watch channel for a re-trigger when datastore becomes active
|
||||||
|
<-watchCh
|
||||||
|
|
||||||
|
kvpCh, err = ds.store.Watch(Key(kvObject.Key()...), sCh)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not watch the key %s in store: %v", Key(kvObject.Key()...), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
goto retry_watch
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return kvoCh, nil
|
return kvoCh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ds *datastore) RestartWatch() {
|
||||||
|
ds.Lock()
|
||||||
|
defer ds.Unlock()
|
||||||
|
|
||||||
|
ds.active = true
|
||||||
|
watchCh := ds.watchCh
|
||||||
|
ds.watchCh = make(chan struct{})
|
||||||
|
close(watchCh)
|
||||||
|
}
|
||||||
|
|
||||||
func (ds *datastore) KVStore() store.Store {
|
func (ds *datastore) KVStore() store.Store {
|
||||||
return ds.store
|
return ds.store
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func NewHostDiscovery(watcher discovery.Watcher) HostDiscovery {
|
||||||
return &hostDiscovery{watcher: watcher, nodes: mapset.NewSet(), stopChan: make(chan struct{})}
|
return &hostDiscovery{watcher: watcher, nodes: mapset.NewSet(), stopChan: make(chan struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hostDiscovery) Watch(joinCallback JoinCallback, leaveCallback LeaveCallback) error {
|
func (h *hostDiscovery) Watch(activeCallback ActiveCallback, joinCallback JoinCallback, leaveCallback LeaveCallback) error {
|
||||||
h.Lock()
|
h.Lock()
|
||||||
d := h.watcher
|
d := h.watcher
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
|
@ -42,15 +42,16 @@ func (h *hostDiscovery) Watch(joinCallback JoinCallback, leaveCallback LeaveCall
|
||||||
return types.BadRequestErrorf("invalid discovery watcher")
|
return types.BadRequestErrorf("invalid discovery watcher")
|
||||||
}
|
}
|
||||||
discoveryCh, errCh := d.Watch(h.stopChan)
|
discoveryCh, errCh := d.Watch(h.stopChan)
|
||||||
go h.monitorDiscovery(discoveryCh, errCh, joinCallback, leaveCallback)
|
go h.monitorDiscovery(discoveryCh, errCh, activeCallback, joinCallback, leaveCallback)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hostDiscovery) monitorDiscovery(ch <-chan discovery.Entries, errCh <-chan error, joinCallback JoinCallback, leaveCallback LeaveCallback) {
|
func (h *hostDiscovery) monitorDiscovery(ch <-chan discovery.Entries, errCh <-chan error,
|
||||||
|
activeCallback ActiveCallback, joinCallback JoinCallback, leaveCallback LeaveCallback) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case entries := <-ch:
|
case entries := <-ch:
|
||||||
h.processCallback(entries, joinCallback, leaveCallback)
|
h.processCallback(entries, activeCallback, joinCallback, leaveCallback)
|
||||||
case err := <-errCh:
|
case err := <-errCh:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("discovery error: %v", err)
|
log.Errorf("discovery error: %v", err)
|
||||||
|
@ -71,7 +72,8 @@ func (h *hostDiscovery) StopDiscovery() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hostDiscovery) processCallback(entries discovery.Entries, joinCallback JoinCallback, leaveCallback LeaveCallback) {
|
func (h *hostDiscovery) processCallback(entries discovery.Entries,
|
||||||
|
activeCallback ActiveCallback, joinCallback JoinCallback, leaveCallback LeaveCallback) {
|
||||||
updated := hosts(entries)
|
updated := hosts(entries)
|
||||||
h.Lock()
|
h.Lock()
|
||||||
existing := h.nodes
|
existing := h.nodes
|
||||||
|
@ -79,6 +81,7 @@ func (h *hostDiscovery) processCallback(entries discovery.Entries, joinCallback
|
||||||
h.nodes = updated
|
h.nodes = updated
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
|
|
||||||
|
activeCallback()
|
||||||
if len(added) > 0 {
|
if len(added) > 0 {
|
||||||
joinCallback(added)
|
joinCallback(added)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,16 @@ import "net"
|
||||||
// JoinCallback provides a callback event for new node joining the cluster
|
// JoinCallback provides a callback event for new node joining the cluster
|
||||||
type JoinCallback func(entries []net.IP)
|
type JoinCallback func(entries []net.IP)
|
||||||
|
|
||||||
|
// ActiveCallback provides a callback event for active discovery event
|
||||||
|
type ActiveCallback func()
|
||||||
|
|
||||||
// LeaveCallback provides a callback event for node leaving the cluster
|
// LeaveCallback provides a callback event for node leaving the cluster
|
||||||
type LeaveCallback func(entries []net.IP)
|
type LeaveCallback func(entries []net.IP)
|
||||||
|
|
||||||
// HostDiscovery primary interface
|
// HostDiscovery primary interface
|
||||||
type HostDiscovery interface {
|
type HostDiscovery interface {
|
||||||
//Watch Node join and leave cluster events
|
//Watch Node join and leave cluster events
|
||||||
Watch(joinCallback JoinCallback, leaveCallback LeaveCallback) error
|
Watch(activeCallback ActiveCallback, joinCallback JoinCallback, leaveCallback LeaveCallback) error
|
||||||
// StopDiscovery stops the discovery perocess
|
// StopDiscovery stops the discovery perocess
|
||||||
StopDiscovery() error
|
StopDiscovery() error
|
||||||
// Fetch returns a list of host IPs that are currently discovered
|
// Fetch returns a list of host IPs that are currently discovered
|
||||||
|
|
|
@ -44,7 +44,7 @@ func TestAddedCallback(t *testing.T) {
|
||||||
|
|
||||||
added := false
|
added := false
|
||||||
removed := false
|
removed := false
|
||||||
hd.processCallback(update, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true })
|
hd.processCallback(update, func() {}, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true })
|
||||||
if !added {
|
if !added {
|
||||||
t.Fatalf("Expecting a Added callback notification. But none received")
|
t.Fatalf("Expecting a Added callback notification. But none received")
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func TestRemovedCallback(t *testing.T) {
|
||||||
|
|
||||||
added := false
|
added := false
|
||||||
removed := false
|
removed := false
|
||||||
hd.processCallback(update, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true })
|
hd.processCallback(update, func() {}, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true })
|
||||||
if !removed {
|
if !removed {
|
||||||
t.Fatalf("Expecting a Removed callback notification. But none received")
|
t.Fatalf("Expecting a Removed callback notification. But none received")
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ func TestNoCallback(t *testing.T) {
|
||||||
|
|
||||||
added := false
|
added := false
|
||||||
removed := false
|
removed := false
|
||||||
hd.processCallback(update, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true })
|
hd.processCallback(update, func() {}, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true })
|
||||||
if added || removed {
|
if added || removed {
|
||||||
t.Fatalf("Not expecting any callback notification. But received a callback")
|
t.Fatalf("Not expecting any callback notification. But received a callback")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue