layer1: add namespace manager summary

This commit is contained in:
Naeel
2026-04-26 10:55:56 +03:00
parent 67db8d71f1
commit 5c481b7293
5 changed files with 74 additions and 0 deletions
@@ -0,0 +1,20 @@
# 2026-04-26 — NamespaceManager rewrite, step 45
## Цель шага
Подготовить компактный status/debug surface для `NamespaceManager`.
## Что меняем
1. Добавляем `NamespaceManagerSummary`.
2. В `NamespaceManager` добавляем `Summary()`.
3. Summary считает:
- общее число namespace-ов;
- число по phase;
- список subscriber-ов.
4. Добавляем unit tests.
## Что НЕ меняем
- не публикуем summary наружу через HTTP;
- не меняем watcher behavior.
+14
View File
@@ -59,6 +59,7 @@ func (s NamespaceSubscriberFuncs) OnNamespaceResync(ctx context.Context, record
type NamespaceManager interface { type NamespaceManager interface {
Snapshot() []string Snapshot() []string
SnapshotRecords() []NamespaceRecord SnapshotRecords() []NamespaceRecord
Summary() NamespaceManagerSummary
Get(name string) (NamespaceRecord, bool) Get(name string) (NamespaceRecord, bool)
Bootstrap(namespaces []string, source NamespaceSource, observedAt time.Time) []NamespaceRecord Bootstrap(namespaces []string, source NamespaceSource, observedAt time.Time) []NamespaceRecord
BootstrapAndDispatch(ctx context.Context, namespaces []string, source NamespaceSource, observedAt time.Time) ([]NamespaceRecord, error) BootstrapAndDispatch(ctx context.Context, namespaces []string, source NamespaceSource, observedAt time.Time) ([]NamespaceRecord, error)
@@ -240,6 +241,19 @@ func StartManagedNamespaceWatcher(ctx context.Context, logger *zap.Logger, compo
}) })
} }
func (m *inMemoryNamespaceManager) Summary() NamespaceManagerSummary {
records := m.SnapshotRecords()
summary := NamespaceManagerSummary{
TotalNamespaces: len(records),
PhaseCounts: make(map[NamespacePhase]int),
Subscribers: m.SnapshotSubscribers(),
}
for _, record := range records {
summary.PhaseCounts[record.Phase]++
}
return summary
}
func (m *inMemoryNamespaceManager) Subscribe(subscriber NamespaceSubscriber) { func (m *inMemoryNamespaceManager) Subscribe(subscriber NamespaceSubscriber) {
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()
+6
View File
@@ -73,6 +73,12 @@ type NamespaceEvent struct {
ObservedAt time.Time ObservedAt time.Time
} }
type NamespaceManagerSummary struct {
TotalNamespaces int
PhaseCounts map[NamespacePhase]int
Subscribers []string
}
func NewNamespaceEvent(eventType NamespaceEventType, name string, labels map[string]string, source NamespaceSource, observedAt time.Time) NamespaceEvent { func NewNamespaceEvent(eventType NamespaceEventType, name string, labels map[string]string, source NamespaceSource, observedAt time.Time) NamespaceEvent {
return NamespaceEvent{ return NamespaceEvent{
Type: eventType, Type: eventType,
@@ -154,3 +154,10 @@ func TestNamespaceRemovalStrategyValues(t *testing.T) {
t.Fatalf("unexpected dispatch-remove strategy value") t.Fatalf("unexpected dispatch-remove strategy value")
} }
} }
func TestNamespaceManagerSummaryZeroValue(t *testing.T) {
summary := NamespaceManagerSummary{}
if summary.TotalNamespaces != 0 {
t.Fatalf("expected zero total namespaces")
}
}
+27
View File
@@ -59,6 +59,10 @@ func TestNamespaceManagerSnapshotAndGet(t *testing.T) {
if record.Name != "tenant-a" || record.Phase != NamespacePhaseDiscovered { if record.Name != "tenant-a" || record.Phase != NamespacePhaseDiscovered {
t.Fatalf("unexpected record returned: %+v", record) t.Fatalf("unexpected record returned: %+v", record)
} }
summary := manager.Summary()
if summary.TotalNamespaces != 2 {
t.Fatalf("expected summary total namespaces to be 2, got %d", summary.TotalNamespaces)
}
} }
func TestNamespaceManagerUpsertIncrementsGeneration(t *testing.T) { func TestNamespaceManagerUpsertIncrementsGeneration(t *testing.T) {
@@ -448,6 +452,29 @@ func TestPrepareManagedNamespaceWatcher(t *testing.T) {
} }
} }
func TestNamespaceManagerSummary(t *testing.T) {
manager := NewNamespaceManager()
manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher})
manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-b", Source: NamespaceSourceWatcher})
_, _ = manager.MarkPartRegistering("tenant-a", "router")
manager.Upsert(NamespaceEvent{Type: NamespaceEventRemove, Name: "tenant-b", Source: NamespaceSourceWatcher})
manager.Subscribe(&testNamespaceSubscriber{name: "router"})
summary := manager.Summary()
if summary.TotalNamespaces != 2 {
t.Fatalf("expected total namespaces 2, got %d", summary.TotalNamespaces)
}
if summary.PhaseCounts[NamespacePhaseRegistering] != 1 {
t.Fatalf("expected one registering namespace")
}
if summary.PhaseCounts[NamespacePhaseRemoved] != 1 {
t.Fatalf("expected one removed namespace")
}
if !reflect.DeepEqual([]string{"router"}, summary.Subscribers) {
t.Fatalf("expected router subscriber in summary")
}
}
func TestNamespaceManagerDispatchAdd(t *testing.T) { func TestNamespaceManagerDispatchAdd(t *testing.T) {
manager := NewNamespaceManager() manager := NewNamespaceManager()
manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher}) manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher})