discovery service
This commit is contained in:
@@ -13,6 +13,7 @@ const (
|
||||
|
||||
type RegistryEntry struct {
|
||||
ID string `json:"id"`
|
||||
InstanceID string `bson:"instanceId" json:"instanceId"`
|
||||
Service string `json:"service"`
|
||||
Rail string `json:"rail,omitempty"`
|
||||
Network string `json:"network,omitempty"`
|
||||
@@ -29,8 +30,10 @@ type RegistryEntry struct {
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
entries map[string]*RegistryEntry
|
||||
mu sync.RWMutex
|
||||
entries map[string]*RegistryEntry
|
||||
byID map[string]map[string]struct{}
|
||||
byInstance map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
type UpdateResult struct {
|
||||
@@ -42,23 +45,31 @@ type UpdateResult struct {
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
entries: map[string]*RegistryEntry{},
|
||||
entries: map[string]*RegistryEntry{},
|
||||
byID: map[string]map[string]struct{}{},
|
||||
byInstance: map[string]map[string]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) UpsertFromAnnouncement(announce Announcement, now time.Time) UpdateResult {
|
||||
entry := registryEntryFromAnnouncement(normalizeAnnouncement(announce), now)
|
||||
key := registryEntryKey(entry)
|
||||
if key == "" {
|
||||
return UpdateResult{Entry: entry}
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
existing, ok := r.entries[entry.ID]
|
||||
existing, ok := r.entries[key]
|
||||
wasHealthy := false
|
||||
if ok && existing != nil {
|
||||
wasHealthy = existing.isHealthyAt(now)
|
||||
r.unindexEntry(key, existing)
|
||||
}
|
||||
entry.Healthy = entry.isHealthyAt(now)
|
||||
r.entries[entry.ID] = &entry
|
||||
r.entries[key] = &entry
|
||||
r.indexEntry(key, &entry)
|
||||
|
||||
return UpdateResult{
|
||||
Entry: entry,
|
||||
@@ -68,10 +79,45 @@ func (r *Registry) UpsertFromAnnouncement(announce Announcement, now time.Time)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) UpdateHeartbeat(id string, status string, ts time.Time, now time.Time) (UpdateResult, bool) {
|
||||
func (r *Registry) UpsertEntry(entry RegistryEntry, now time.Time) UpdateResult {
|
||||
entry = normalizeEntry(entry)
|
||||
key := registryEntryKey(entry)
|
||||
if key == "" {
|
||||
return UpdateResult{Entry: entry}
|
||||
}
|
||||
if entry.LastHeartbeat.IsZero() {
|
||||
entry.LastHeartbeat = now
|
||||
}
|
||||
if strings.TrimSpace(entry.Status) == "" {
|
||||
entry.Status = "ok"
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
existing, ok := r.entries[key]
|
||||
wasHealthy := false
|
||||
if ok && existing != nil {
|
||||
wasHealthy = existing.isHealthyAt(now)
|
||||
r.unindexEntry(key, existing)
|
||||
}
|
||||
entry.Healthy = entry.isHealthyAt(now)
|
||||
r.entries[key] = &entry
|
||||
r.indexEntry(key, &entry)
|
||||
|
||||
return UpdateResult{
|
||||
Entry: entry,
|
||||
IsNew: !ok,
|
||||
WasHealthy: wasHealthy,
|
||||
BecameHealthy: !wasHealthy && entry.Healthy,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) UpdateHeartbeat(id string, instanceID string, status string, ts time.Time, now time.Time) []UpdateResult {
|
||||
id = strings.TrimSpace(id)
|
||||
if id == "" {
|
||||
return UpdateResult{}, false
|
||||
instanceID = strings.TrimSpace(instanceID)
|
||||
if id == "" && instanceID == "" {
|
||||
return nil
|
||||
}
|
||||
if status == "" {
|
||||
status = "ok"
|
||||
@@ -83,21 +129,54 @@ func (r *Registry) UpdateHeartbeat(id string, status string, ts time.Time, now t
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
entry, ok := r.entries[id]
|
||||
if !ok || entry == nil {
|
||||
return UpdateResult{}, false
|
||||
keys := keysFromIndex(r.byInstance[instanceID])
|
||||
if len(keys) == 0 && id != "" {
|
||||
keys = keysFromIndex(r.byID[id])
|
||||
}
|
||||
wasHealthy := entry.isHealthyAt(now)
|
||||
entry.Status = status
|
||||
entry.LastHeartbeat = ts
|
||||
entry.Healthy = entry.isHealthyAt(now)
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]UpdateResult, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
entry := r.entries[key]
|
||||
if entry == nil {
|
||||
continue
|
||||
}
|
||||
if id != "" && entry.ID != id {
|
||||
continue
|
||||
}
|
||||
if instanceID != "" && entry.InstanceID != instanceID {
|
||||
continue
|
||||
}
|
||||
wasHealthy := entry.isHealthyAt(now)
|
||||
entry.Status = status
|
||||
entry.LastHeartbeat = ts
|
||||
entry.Healthy = entry.isHealthyAt(now)
|
||||
|
||||
return UpdateResult{
|
||||
Entry: *entry,
|
||||
IsNew: false,
|
||||
WasHealthy: wasHealthy,
|
||||
BecameHealthy: !wasHealthy && entry.Healthy,
|
||||
}, true
|
||||
results = append(results, UpdateResult{
|
||||
Entry: *entry,
|
||||
IsNew: false,
|
||||
WasHealthy: wasHealthy,
|
||||
BecameHealthy: !wasHealthy && entry.Healthy,
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (r *Registry) Delete(key string) bool {
|
||||
key = strings.TrimSpace(key)
|
||||
if key == "" {
|
||||
return false
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
entry, ok := r.entries[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
delete(r.entries, key)
|
||||
r.unindexEntry(key, entry)
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Registry) List(now time.Time, onlyHealthy bool) []RegistryEntry {
|
||||
@@ -123,6 +202,7 @@ func registryEntryFromAnnouncement(announce Announcement, now time.Time) Registr
|
||||
status := "ok"
|
||||
return RegistryEntry{
|
||||
ID: strings.TrimSpace(announce.ID),
|
||||
InstanceID: strings.TrimSpace(announce.InstanceID),
|
||||
Service: strings.TrimSpace(announce.Service),
|
||||
Rail: strings.ToUpper(strings.TrimSpace(announce.Rail)),
|
||||
Network: strings.ToUpper(strings.TrimSpace(announce.Network)),
|
||||
@@ -138,8 +218,33 @@ func registryEntryFromAnnouncement(announce Announcement, now time.Time) Registr
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeEntry(entry RegistryEntry) RegistryEntry {
|
||||
entry.ID = strings.TrimSpace(entry.ID)
|
||||
entry.InstanceID = strings.TrimSpace(entry.InstanceID)
|
||||
if entry.InstanceID == "" {
|
||||
entry.InstanceID = entry.ID
|
||||
}
|
||||
entry.Service = strings.TrimSpace(entry.Service)
|
||||
entry.Rail = strings.ToUpper(strings.TrimSpace(entry.Rail))
|
||||
entry.Network = strings.ToUpper(strings.TrimSpace(entry.Network))
|
||||
entry.Operations = normalizeStrings(entry.Operations, false)
|
||||
entry.Currencies = normalizeStrings(entry.Currencies, true)
|
||||
entry.InvokeURI = strings.TrimSpace(entry.InvokeURI)
|
||||
entry.Version = strings.TrimSpace(entry.Version)
|
||||
entry.Status = strings.TrimSpace(entry.Status)
|
||||
entry.Health = normalizeHealth(entry.Health)
|
||||
if entry.Limits != nil {
|
||||
entry.Limits = normalizeLimits(*entry.Limits)
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
func normalizeAnnouncement(announce Announcement) Announcement {
|
||||
announce.ID = strings.TrimSpace(announce.ID)
|
||||
announce.InstanceID = strings.TrimSpace(announce.InstanceID)
|
||||
if announce.InstanceID == "" {
|
||||
announce.InstanceID = announce.ID
|
||||
}
|
||||
announce.Service = strings.TrimSpace(announce.Service)
|
||||
announce.Rail = strings.ToUpper(strings.TrimSpace(announce.Rail))
|
||||
announce.Network = strings.ToUpper(strings.TrimSpace(announce.Network))
|
||||
@@ -239,6 +344,67 @@ func cloneStrings(values []string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *Registry) indexEntry(key string, entry *RegistryEntry) {
|
||||
if r == nil || entry == nil || key == "" {
|
||||
return
|
||||
}
|
||||
if entry.ID != "" {
|
||||
addIndex(r.byID, entry.ID, key)
|
||||
}
|
||||
if entry.InstanceID != "" {
|
||||
addIndex(r.byInstance, entry.InstanceID, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) unindexEntry(key string, entry *RegistryEntry) {
|
||||
if r == nil || entry == nil || key == "" {
|
||||
return
|
||||
}
|
||||
if entry.ID != "" {
|
||||
removeIndex(r.byID, entry.ID, key)
|
||||
}
|
||||
if entry.InstanceID != "" {
|
||||
removeIndex(r.byInstance, entry.InstanceID, key)
|
||||
}
|
||||
}
|
||||
|
||||
func addIndex(index map[string]map[string]struct{}, id string, key string) {
|
||||
if id == "" || key == "" {
|
||||
return
|
||||
}
|
||||
set := index[id]
|
||||
if set == nil {
|
||||
set = map[string]struct{}{}
|
||||
index[id] = set
|
||||
}
|
||||
set[key] = struct{}{}
|
||||
}
|
||||
|
||||
func removeIndex(index map[string]map[string]struct{}, id string, key string) {
|
||||
if id == "" || key == "" {
|
||||
return
|
||||
}
|
||||
set := index[id]
|
||||
if set == nil {
|
||||
return
|
||||
}
|
||||
delete(set, key)
|
||||
if len(set) == 0 {
|
||||
delete(index, id)
|
||||
}
|
||||
}
|
||||
|
||||
func keysFromIndex(index map[string]struct{}) []string {
|
||||
if len(index) == 0 {
|
||||
return nil
|
||||
}
|
||||
keys := make([]string, 0, len(index))
|
||||
for key := range index {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (e *RegistryEntry) isHealthyAt(now time.Time) bool {
|
||||
if e == nil {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user