layer1: add namespace remove dispatch step 33

This commit is contained in:
Naeel
2026-04-26 10:36:48 +03:00
parent 488157963a
commit d09bee3431
3 changed files with 83 additions and 1 deletions
@@ -0,0 +1,17 @@
# 2026-04-26 — NamespaceManager rewrite, step 33
## Цель шага
Убрать несоответствие между contract и manager implementation: `OnNamespaceRemove()` уже есть, а `DispatchRemove()` ещё нет.
## Что меняем
1. В `NamespaceManager` добавляем `DispatchRemove()`.
2. Manager вызывает `OnNamespaceRemove()` у всех subscriber-ов.
3. После dispatch namespace переводится в `removed` через `NamespaceEventRemove`.
4. Добавляем unit tests на success и failure path.
## Что НЕ меняем
- не подключаем remove events в watcher-ы;
- не реализуем physical cleanup в runtime components.
+25
View File
@@ -57,6 +57,7 @@ type NamespaceManager interface {
SnapshotSubscribers() []string SnapshotSubscribers() []string
Upsert(event NamespaceEvent) NamespaceRecord Upsert(event NamespaceEvent) NamespaceRecord
DispatchAdd(ctx context.Context, namespace string) (NamespaceRecord, bool, error) DispatchAdd(ctx context.Context, namespace string) (NamespaceRecord, bool, error)
DispatchRemove(ctx context.Context, namespace string) (NamespaceRecord, bool, error)
DispatchResync(ctx context.Context, namespace string) (NamespaceRecord, bool, error) DispatchResync(ctx context.Context, namespace string) (NamespaceRecord, bool, error)
MarkPartState(namespace string, part string, state NamespacePartState) (NamespaceRecord, bool) MarkPartState(namespace string, part string, state NamespacePartState) (NamespaceRecord, bool)
MarkPartRegistering(namespace string, part string) (NamespaceRecord, bool) MarkPartRegistering(namespace string, part string) (NamespaceRecord, bool)
@@ -250,6 +251,30 @@ func (m *inMemoryNamespaceManager) DispatchAdd(ctx context.Context, namespace st
}) })
} }
func (m *inMemoryNamespaceManager) DispatchRemove(ctx context.Context, namespace string) (NamespaceRecord, bool, error) {
record, ok := m.Get(namespace)
if !ok {
return NamespaceRecord{}, false, nil
}
var firstErr error
for _, subscriber := range m.snapshotSubscriberObjects() {
err := subscriber.OnNamespaceRemove(ctx, record)
if err != nil && firstErr == nil {
firstErr = err
}
}
removedRecord := m.Upsert(NamespaceEvent{
Type: NamespaceEventRemove,
Name: record.Name,
Labels: record.Labels,
Source: record.Source,
ObservedAt: time.Now().UTC(),
})
return removedRecord, true, firstErr
}
func (m *inMemoryNamespaceManager) DispatchResync(ctx context.Context, namespace string) (NamespaceRecord, bool, error) { func (m *inMemoryNamespaceManager) DispatchResync(ctx context.Context, namespace string) (NamespaceRecord, bool, error) {
return m.dispatch(ctx, namespace, func(subscriber NamespaceSubscriber, record NamespaceRecord) error { return m.dispatch(ctx, namespace, func(subscriber NamespaceSubscriber, record NamespaceRecord) error {
return subscriber.OnNamespaceResync(ctx, record) return subscriber.OnNamespaceResync(ctx, record)
+41 -1
View File
@@ -11,8 +11,10 @@ import (
type testNamespaceSubscriber struct { type testNamespaceSubscriber struct {
name string name string
addErr error addErr error
removeErr error
resyncErr error resyncErr error
addCalls int addCalls int
removeCalls int
resyncCalls int resyncCalls int
} }
@@ -26,7 +28,8 @@ func (s *testNamespaceSubscriber) OnNamespaceAdd(ctx context.Context, record Nam
} }
func (s *testNamespaceSubscriber) OnNamespaceRemove(ctx context.Context, record NamespaceRecord) error { func (s *testNamespaceSubscriber) OnNamespaceRemove(ctx context.Context, record NamespaceRecord) error {
return nil s.removeCalls++
return s.removeErr
} }
func (s *testNamespaceSubscriber) OnNamespaceResync(ctx context.Context, record NamespaceRecord) error { func (s *testNamespaceSubscriber) OnNamespaceResync(ctx context.Context, record NamespaceRecord) error {
@@ -310,6 +313,43 @@ func TestNamespaceManagerDispatchResyncFailure(t *testing.T) {
} }
} }
func TestNamespaceManagerDispatchRemove(t *testing.T) {
manager := NewNamespaceManager()
manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher})
router := &testNamespaceSubscriber{name: "router"}
builder := &testNamespaceSubscriber{name: "buildermgr"}
manager.Subscribe(router)
manager.Subscribe(builder)
record, ok, err := manager.DispatchRemove(context.Background(), "tenant-a")
if !ok || err != nil {
t.Fatalf("expected dispatch remove success, ok=%v err=%v", ok, err)
}
if router.removeCalls != 1 || builder.removeCalls != 1 {
t.Fatalf("expected both subscribers to receive remove call")
}
if record.Phase != NamespacePhaseRemoved {
t.Fatalf("expected removed phase after dispatch remove, got %s", record.Phase)
}
}
func TestNamespaceManagerDispatchRemoveFailure(t *testing.T) {
manager := NewNamespaceManager()
manager.Upsert(NamespaceEvent{Type: NamespaceEventAdd, Name: "tenant-a", Source: NamespaceSourceWatcher})
router := &testNamespaceSubscriber{name: "router", removeErr: errors.New("remove failed")}
builder := &testNamespaceSubscriber{name: "buildermgr"}
manager.Subscribe(router)
manager.Subscribe(builder)
record, ok, err := manager.DispatchRemove(context.Background(), "tenant-a")
if !ok || err == nil {
t.Fatalf("expected dispatch remove failure, ok=%v err=%v", ok, err)
}
if record.Phase != NamespacePhaseRemoved {
t.Fatalf("expected removed phase even on dispatch remove error, got %s", record.Phase)
}
}
func TestNamespaceSubscriberFuncs(t *testing.T) { func TestNamespaceSubscriberFuncs(t *testing.T) {
addCalls := 0 addCalls := 0
removeCalls := 0 removeCalls := 0