discovery service

This commit is contained in:
Stephan D
2026-01-02 02:44:01 +01:00
parent 97ba7500dc
commit ea1c69f14a
47 changed files with 2799 additions and 701 deletions

View File

@@ -0,0 +1,126 @@
package discovery
import (
"encoding/json"
"errors"
"sync"
"time"
"github.com/nats-io/nats.go"
mb "github.com/tech/sendico/pkg/messaging/broker"
"github.com/tech/sendico/pkg/mlogger"
"go.uber.org/zap"
)
type RegistryWatcher struct {
logger mlogger.Logger
registry *Registry
kv *KVStore
watcher nats.KeyWatcher
stopOnce sync.Once
}
func NewRegistryWatcher(logger mlogger.Logger, msgBroker mb.Broker, registry *Registry) (*RegistryWatcher, error) {
if msgBroker == nil {
return nil, errors.New("discovery watcher: broker is nil")
}
if registry == nil {
registry = NewRegistry()
}
if logger != nil {
logger = logger.Named("discovery_watcher")
}
provider, ok := msgBroker.(jetStreamProvider)
if !ok {
return nil, errors.New("discovery watcher: jetstream not available")
}
js := provider.JetStream()
if js == nil {
return nil, errors.New("discovery watcher: jetstream not configured")
}
store, err := NewKVStore(logger, js, "")
if err != nil {
return nil, err
}
return &RegistryWatcher{
logger: logger,
registry: registry,
kv: store,
}, nil
}
func (w *RegistryWatcher) Start() error {
if w == nil || w.kv == nil {
return errors.New("discovery watcher: not configured")
}
watcher, err := w.kv.WatchAll()
if err != nil {
return err
}
w.watcher = watcher
if w.logger != nil {
w.logger.Info("Discovery registry watcher started", zap.String("bucket", w.kv.Bucket()))
}
go w.consume(watcher)
return nil
}
func (w *RegistryWatcher) Stop() {
if w == nil {
return
}
w.stopOnce.Do(func() {
if w.watcher != nil {
_ = w.watcher.Stop()
}
if w.logger != nil {
w.logger.Info("Discovery registry watcher stopped")
}
})
}
func (w *RegistryWatcher) Registry() *Registry {
if w == nil {
return nil
}
return w.registry
}
func (w *RegistryWatcher) consume(watcher nats.KeyWatcher) {
if w == nil || watcher == nil {
return
}
for entry := range watcher.Updates() {
if entry == nil {
continue
}
switch entry.Operation() {
case nats.KeyValueDelete, nats.KeyValuePurge:
key := registryKeyFromKVKey(entry.Key())
if key != "" {
if w.registry.Delete(key) && w.logger != nil {
w.logger.Info("Discovery registry entry removed", zap.String("key", key))
}
}
continue
case nats.KeyValuePut:
default:
continue
}
var payload RegistryEntry
if err := json.Unmarshal(entry.Value(), &payload); err != nil {
if w.logger != nil {
w.logger.Warn("Failed to decode discovery KV entry", zap.String("key", entry.Key()), zap.Error(err))
}
continue
}
result := w.registry.UpsertEntry(payload, time.Now())
if w.logger != nil && (result.IsNew || result.BecameHealthy) {
fields := append(entryFields(result.Entry), zap.Bool("is_new", result.IsNew), zap.Bool("became_healthy", result.BecameHealthy))
w.logger.Info("Discovery registry entry updated from KV", fields...)
}
}
}