doc(audit): mark stuck-failed as CLOSED after 919e8439 (RunReconciler + error propagation)
Updated FORENSIC_ARCHITECTURE_AUDIT.md to reflect the fix from commit919e8439: Status table: - 'Stuck-failed namespace без auto-recovery': ❌ ОТКРЫТ → ✅ ЗАКРЫТ (919e8439) - 'No Explicit State Machine': ❌ ОТКРЫТ → ⚠️ СМЯГЧЕНО (stuck-failed закрыт; явная state machine остаётся в backlog) Sections updated: - §1.4: полное описание что было (void-функции, мёртвый reconciler) и что сделано (error propagation chain, end-to-end flow retry) - Fragile Components: Stuck Failed Phase — вычеркнуто как закрытое - Risky Decisions table: No Explicit State Machine → частично закрыто - Lifecycle Management: добавлено что auto-recovery работает через RunReconciler - Operational Burden: убрано упоминание stuck-failed как активной проблемы - Maintainability/Production-Grade: обновлены под текущее состояние - §2 Stuck Failed Accumulation: ✅ ЗАКРЫТ - §3 P1 Reconcile-очередь: ✅ ЗАКРЫТ - §5 Sharded mutex вердикт: убрано упоминание stuck-failed
This commit is contained in:
@@ -14,9 +14,9 @@
|
||||
| Executor dedup dirty state при re-add NS | ✅ ЗАКРЫТ | `4eedf95f` |
|
||||
| Синхронный subscriber dispatch (onboarding latency) | ✅ ЗАКРЫТ | предыдущая сессия |
|
||||
| `DefaultNSResolver` только append (нет RemoveNamespace) | ✅ ЗАКРЫТ | предыдущая сессия |
|
||||
| Stuck-failed namespace без auto-recovery | ❌ ОТКРЫТ | — |
|
||||
| Stuck-failed namespace без auto-recovery | ✅ ЗАКРЫТ | `919e8439` |
|
||||
| AdoptExistingResources race при rolling update | ❌ ОТКРЫТ | — |
|
||||
| No explicit state machine (implicit phase transitions) | ❌ ОТКРЫТ | — |
|
||||
| No Explicit State Machine (implicit phase transitions) | ⚠️ СМЯГЧЕНО | `919e8439` |
|
||||
| Sharded mutex (bottleneck при >500 concurrent tenant) | ⏳ BACKLOG | не актуально при текущей нагрузке |
|
||||
|
||||
---
|
||||
@@ -67,7 +67,7 @@
|
||||
- **RBAC/SA Drift**: Неконсистентность между созданием и удалением SA/ролей при сбое в `deregisterNamespace`.
|
||||
- **Cache Invalidation**: `FunctionServiceCache` не очищается при `RemoveNamespace` — расчёт на `idleObjectReaper`. При высоком churn rate может накапливать stale записи быстрее, чем reaper убирает.
|
||||
- **Adoption Race**: `AdoptExistingResources` vs `namespace_subscriber` — активная проблема (§1.3).
|
||||
- **Stuck Failed Phase**: Namespace в `failed` не восстанавливается без рестарта — активная проблема (§1.4).
|
||||
- **Stuck Failed Phase**: ~~Namespace в `failed` не восстанавливается без рестарта~~ — закрыто: `RunReconciler` + полная цепочка error propagation (§1.4).
|
||||
|
||||
---
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
| Informer Lifecycle Management | ✅ Hardened | per-NS context cancel + RemoveNamespace во всех компонентах |
|
||||
| Centralized Mutex | ⚠️ Приемлемо | sharding в backlog, не актуально до >500 NS |
|
||||
| Manual Adoption | ❌ Активная проблема | race при rolling update |
|
||||
| No Explicit State Machine | ❌ Активная проблема | stuck-failed без retry |
|
||||
| No Explicit State Machine | ✅ Частично закрыто | stuck-failed закрыт (`919e8439`); явная state machine в backlog |
|
||||
| Eventual Consistency | ⚠️ Смягчено | параллельный dispatch уменьшает окно, но не устраняет |
|
||||
|
||||
---
|
||||
@@ -105,10 +105,10 @@
|
||||
|
||||
- **Multi-Tenancy**: Label-based discovery, каждый tenant — отдельный namespace, изоляция на уровне k8s.
|
||||
- **Isolation Model**: Namespace-level isolation, per-NS SA/RBAC, per-NS informer factory.
|
||||
- **Lifecycle Management**: Фазы (discovered → registering → active → deregistering → removed / failed) реализованы, но без явной state machine и без auto-recovery из failed.
|
||||
- **Lifecycle Management**: Фазы (discovered → registering → active → deregistering → removed / failed) реализованы. Auto-recovery из failed работает через `RunReconciler`. Явная state machine в backlog.
|
||||
- **State Handling**: Глобальный `DefaultNSResolver` + локальные lister-ы. После `RemoveNamespace` — оба синхронизованы. После re-add — оба корректно инициализируются заново.
|
||||
- **Reconciliation Logic**: Каждый компонент через subscribe. Отсутствует reconcile-очередь для failed state.
|
||||
- **Operational Burden**: Средний — goroutine leak устранён, stale informer устранён. Требуется мониторинг: stuck-failed накопление, orphaned SA/RBAC при неудачном deregister.
|
||||
- **Operational Burden**: Средний — goroutine leak устранён, stale informer устранён, stuck-failed закрыт. Требуется мониторинг: orphaned SA/RBAC при неудачном deregister.
|
||||
|
||||
---
|
||||
|
||||
@@ -116,8 +116,8 @@
|
||||
|
||||
- **Maturity**: Архитектурно зрелый, хорошо документированный, с явным reasoning и поэтапным внедрением.
|
||||
- **Complexity**: Высокая в синхронизации и lifecycle. Снижена за счёт формализации `RemoveNamespace` контракта.
|
||||
- **Maintainability**: Среднесрочная — без явной state machine и auto-recovery возможны stuck state при API нестабильности.
|
||||
- **Production-Grade**: Близко — informer lifecycle корректен, dispatch параллелен, cleanup симметричен. Основной gap: stuck-failed и AdoptExistingResources race.
|
||||
- **Maintainability**: Среднесрочная — без явной state machine сложность будет расти. Auto-recovery из failed работает.
|
||||
- **Production-Grade**: Близко — informer lifecycle корректен, dispatch параллелен, cleanup симметричен, stuck-failed закрыт. Основной gap: AdoptExistingResources race.
|
||||
|
||||
---
|
||||
|
||||
@@ -170,20 +170,29 @@
|
||||
|
||||
---
|
||||
|
||||
### 1.4 No Explicit State Machine — ❌ АКТИВНАЯ ПРОБЛЕМА
|
||||
### 1.4 No Explicit State Machine — ✅ ЗАКРЫТ (коммит `919e8439`)
|
||||
|
||||
**Сценарий: stuck в `failed` без auto-recovery**
|
||||
|
||||
1. `registerNamespace()` вызывает `EnsureNamespaceSA()` — Kubernetes API возвращает 503.
|
||||
2. Часть помечается `NamespacePartStateFailed` → фаза NS → `NamespacePhaseFailed`.
|
||||
3. **Нет reconcile-цикла**: фаза остаётся `failed` до рестарта процесса.
|
||||
4. Router уже создал informer-ы (параллельный dispatch), executor — нет (SA не создан). Dirty state: router видит NS активным, executor — нет. Вызовы → 503.
|
||||
**Что было:** `EnsureNamespaceSA` и `registerNamespace` были void-функциями — ошибки только логировались, до `MarkPartFailed` не доходили. Executor subscriber всегда возвращал nil → namespace никогда не попадал в `NamespacePhaseFailed` → `RunReconciler` для executor был мёртвым кодом.
|
||||
|
||||
**Накопление при churn:** 10 ns/час × 1% API error rate = ~2.4 failed NS/сутки. За 30 дней = ~72 "мёртвых" записи. `Snapshot()` возвращает их в `idleObjectReaper` → лишние LIST запросы к k8s.
|
||||
**Что сделано:**
|
||||
- `setupSAAndRoleBindings` → возвращает `error`
|
||||
- `EnsureNamespaceSA` → возвращает `error`, пробрасывает
|
||||
- `registerNamespace` → возвращает `error` (SA + executorTypes) с `fmt.Errorf` wrapping
|
||||
- Executor `AddFunc`/`ResyncFunc` → пробрасывают ошибку вместо `return nil`
|
||||
- `RunReconciler` → принимает `*zap.Logger`, логирует каждый retry и исход
|
||||
|
||||
**Предложение (не реализовано):** reconcile-очередь в `inMemoryNamespaceManager` — см. FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md §3 для кода.
|
||||
**End-to-end flow:**
|
||||
1. `EnsureNamespaceSA` fails (k8s 503) → `registerNamespace` returns error
|
||||
2. Executor AddFunc returns error → `dispatch()` → `MarkPartFailed("executor")`
|
||||
3. `deriveNamespacePhase` → `NamespacePhaseFailed`
|
||||
4. `RunReconciler` tick (30s) находит namespace → `DispatchResync` → retry
|
||||
5. Если API восстановился: `MarkPartActive` → `NamespacePhaseActive` → лог `resync succeeded`
|
||||
|
||||
**Статус:** не исправлено.
|
||||
**Накопление при churn:** ликвидировано — failed NS автоматически выходят из этой фазы при восстановлении API.
|
||||
|
||||
**Ограничение:** нет max-retries. Namespace, у которого SA создать принципиально невозможно (например, удалённый k8s namespace), будет ретраиться вечно. Приемлемо на текущем масштабе.
|
||||
|
||||
---
|
||||
|
||||
@@ -229,11 +238,9 @@
|
||||
|
||||
## 3. Рекомендации (приоритизированные)
|
||||
|
||||
### P1 — Reconcile-очередь для failed NS
|
||||
### P1 — Reconcile-очередь для failed NS — ✅ ЗАКРЫТ (`919e8439`)
|
||||
|
||||
Минимальное изменение без breaking interface change. Полный код — в `FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md §3`. Суть: горутина читает из `reconcileQueue chan`, делает exponential backoff retry для failed NS. Max 5 попыток.
|
||||
|
||||
**Влияние:** устраняет stuck-failed накопление, dirty state между router и executor.
|
||||
Error propagation исправлена во всей цепочке: `setupSAAndRoleBindings` → `EnsureNamespaceSA` → `registerNamespace` → executor subscriber. `RunReconciler` логирует retry и исход.
|
||||
|
||||
### P2 — AdoptExistingResources после BootstrapAndDispatch
|
||||
|
||||
@@ -266,4 +273,4 @@
|
||||
|
||||
**Технически реализуемо** без breaking interface change. Полный код — в `FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md §5`.
|
||||
|
||||
**Вердикт:** не оправдано при текущей нагрузке. Реальные bottleneck-и — AdoptExistingResources race и stuck-failed, не mutex. Sharded mutex — в backlog, актуально при >500 concurrent tenant с >1 onboarding/sec.
|
||||
**Вердикт:** не оправдано при текущей нагрузке. Реальный bottleneck — AdoptExistingResources race, не mutex. Sharded mutex — в backlog, актуально при >500 concurrent tenant с >1 onboarding/sec.
|
||||
|
||||
Reference in New Issue
Block a user