Refresh TTL; prune expired nodes.

This commit is contained in:
Milos Gajdos 2019-09-25 18:19:18 +01:00
parent 16c7b3a390
commit 27bd9581bf

View File

@ -3,6 +3,7 @@ package memory
import ( import (
"context" "context"
"strings"
"sync" "sync"
"time" "time"
@ -12,12 +13,14 @@ import (
) )
var ( var (
timeout = time.Millisecond * 10 sendEventTime = 10 * time.Millisecond
ttlPruneTime = 1 * time.Minute
DefaultTTL = 1 * time.Minute
) )
// node tracks node registration timestamp and TTL // node tracks node registration timestamp and TTL
type node struct { type node struct {
ts time.Time lastSeen time.Time
ttl time.Duration ttl time.Duration
} }
@ -44,12 +47,67 @@ func NewRegistry(opts ...registry.Option) registry.Registry {
services = make(map[string][]*registry.Service) services = make(map[string][]*registry.Service)
} }
return &Registry{ reg := &Registry{
options: options, options: options,
Services: services, Services: services,
nodes: make(map[string]*node), nodes: make(map[string]*node),
Watchers: make(map[string]*Watcher), Watchers: make(map[string]*Watcher),
} }
go reg.ttlPrune()
return reg
}
// nodeTrackId returns a string we use to track a node of a given service
func nodeTrackId(svcName, svcVersion, nodeId string) string {
return svcName + "+" + svcVersion + "+" + nodeId
}
func (m *Registry) ttlPrune() {
prune := time.NewTicker(ttlPruneTime)
defer prune.Stop()
for {
select {
case <-prune.C:
m.Lock()
for nodeTrackId, node := range m.nodes {
// if we exceed the TTL threshold we need to stop tracking the node
if time.Since(node.lastSeen) > node.ttl {
// split nodeTrackID into service Name, Version and Node Id
trackIdSplit := strings.Split(nodeTrackId, "+")
svcName, svcVersion, nodeId := trackIdSplit[0], trackIdSplit[1], trackIdSplit[2]
log.Logf("TTL threshold reached for node %s for service %s", nodeId, svcName)
// we need to find a node that expired and delete it from service nodes
if _, ok := m.Services[svcName]; ok {
for _, service := range m.Services[svcName] {
if service.Version != svcVersion {
continue
}
// find expired service node and delete it
var nodes []*registry.Node
for _, n := range service.Nodes {
var del bool
if n.Id == nodeId {
del = true
}
if !del {
nodes = append(nodes, n)
}
}
service.Nodes = nodes
}
}
// stop tracking the node
delete(m.nodes, nodeTrackId)
}
}
m.Unlock()
}
}
return
} }
func (m *Registry) sendEvent(r *registry.Result) { func (m *Registry) sendEvent(r *registry.Result) {
@ -70,7 +128,7 @@ func (m *Registry) sendEvent(r *registry.Result) {
default: default:
select { select {
case w.res <- r: case w.res <- r:
case <-time.After(timeout): case <-time.After(sendEventTime):
} }
} }
} }
@ -125,13 +183,18 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
o(&options) o(&options)
} }
// if no TTL has been set, set it to DefaultTTL
if options.TTL.Seconds() == 0.0 {
options.TTL = DefaultTTL
}
if service, ok := m.Services[s.Name]; !ok { if service, ok := m.Services[s.Name]; !ok {
m.Services[s.Name] = []*registry.Service{s} m.Services[s.Name] = []*registry.Service{s}
// add all nodes into nodes map to track their TTL // add all nodes into nodes map to track their TTL
for _, n := range s.Nodes { for _, n := range s.Nodes {
log.Logf("Tracking node %s for service %s", n.Id, s.Name) log.Logf("Tracking node %s for service %s", n.Id, s.Name)
m.nodes[s.Name+n.Id] = &node{ m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
ts: time.Now(), lastSeen: time.Now(),
ttl: options.TTL, ttl: options.TTL,
} }
} }
@ -159,8 +222,8 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
// we know s is the new [version of] service; we need to strart tracking its nodes // we know s is the new [version of] service; we need to strart tracking its nodes
for _, n := range s.Nodes { for _, n := range s.Nodes {
log.Logf("Tracking node %s for service %s", n.Id, s.Name) log.Logf("Tracking node %s for service %s", n.Id, s.Name)
m.nodes[s.Name+n.Id] = &node{ m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
ts: time.Now(), lastSeen: time.Now(),
ttl: options.TTL, ttl: options.TTL,
} }
} }
@ -181,8 +244,8 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
} }
if !found { if !found {
log.Logf("Tracking node %s for service %s", n.Id, s.Name) log.Logf("Tracking node %s for service %s", n.Id, s.Name)
m.nodes[s.Name+n.Id] = &node{ m.nodes[nodeTrackId(s.Name, s.Version, n.Id)] = &node{
ts: time.Now(), lastSeen: time.Now(),
ttl: options.TTL, ttl: options.TTL,
} }
} }
@ -191,6 +254,15 @@ func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption
go m.sendEvent(&registry.Result{Action: "update", Service: s}) go m.sendEvent(&registry.Result{Action: "update", Service: s})
return nil return nil
} }
// refresh the timestamp and TTL of the service node
for _, n := range s.Nodes {
trackId := nodeTrackId(s.Name, s.Version, n.Id)
log.Logf("Refreshing TTL for node %s for service %s", n.Id, s.Name)
if trackedNode, ok := m.nodes[trackId]; ok {
trackedNode.lastSeen = time.Now()
trackedNode.ttl = options.TTL
}
}
} }
} }
@ -218,7 +290,7 @@ func (m *Registry) Deregister(s *registry.Service) error {
if service := registry.Remove(service, []*registry.Service{s}); len(service) == 0 { if service := registry.Remove(service, []*registry.Service{s}); len(service) == 0 {
id := svcNodes[s.Name][s.Version][0] id := svcNodes[s.Name][s.Version][0]
log.Logf("Stopped tracking node %s for service %s", id, s.Name) log.Logf("Stopped tracking node %s for service %s", id, s.Name)
delete(m.nodes, s.Name+id) delete(m.nodes, nodeTrackId(s.Name, s.Version, id))
delete(m.Services, s.Name) delete(m.Services, s.Name)
} else { } else {
// find out which nodes have been removed // find out which nodes have been removed
@ -233,7 +305,7 @@ func (m *Registry) Deregister(s *registry.Service) error {
} }
if !found { if !found {
log.Logf("Stopped tracking node %s for service %s", id, s.Name) log.Logf("Stopped tracking node %s for service %s", id, s.Name)
delete(m.nodes, s.Name+id) delete(m.nodes, nodeTrackId(s.Name, s.Version, id))
} }
} }
m.Services[s.Name] = service m.Services[s.Name] = service