diff --git a/doc/FORENSIC_ARCHITECTURE_AUDIT.md b/doc/FORENSIC_ARCHITECTURE_AUDIT.md index c484929a..667f3972 100644 --- a/doc/FORENSIC_ARCHITECTURE_AUDIT.md +++ b/doc/FORENSIC_ARCHITECTURE_AUDIT.md @@ -15,7 +15,7 @@ | Синхронный subscriber dispatch (onboarding latency) | ✅ ЗАКРЫТ | предыдущая сессия | | `DefaultNSResolver` только append (нет RemoveNamespace) | ✅ ЗАКРЫТ | предыдущая сессия | | Stuck-failed namespace без auto-recovery | ✅ ЗАКРЫТ | `919e8439` | -| AdoptExistingResources race при rolling update | ❌ ОТКРЫТ | — | +| AdoptExistingResources race при rolling update | ✅ ЗАКРЫТ | `2a7d6101` | | No Explicit State Machine (implicit phase transitions) | ⚠️ СМЯГЧЕНО | `919e8439` | | Sharded mutex (bottleneck при >500 concurrent tenant) | ⏳ BACKLOG | не актуально при текущей нагрузке | @@ -57,7 +57,7 @@ ## Workaround-Driven Decisions - ~~**Track-Only Removal**~~ → **ЗАМЕНЕНО** на `DispatchRemove` — cleanup вызывается всегда. -- **Manual Adoption**: При старте executor-ы делают adopt orphaned ресурсов — workaround для несовершенного lifecycle. Активная проблема (см. §1.3). +- **Manual Adoption**: При старте executor-ы делают adopt orphaned ресурсов. Закрыто: `PreRegisterManagedNamespaces` обеспечивает полный NS snapshot до adopt/cleanup (§1.3). - **Explicit Reaper Loops**: `idleObjectReaper` чистит `FunctionServiceCache` вместо event-driven подхода. Приемлемо: `IsValid()` check достаточен при корректной работе per-NS informer-ов. --- @@ -66,7 +66,7 @@ - **RBAC/SA Drift**: Неконсистентность между созданием и удалением SA/ролей при сбое в `deregisterNamespace`. - **Cache Invalidation**: `FunctionServiceCache` не очищается при `RemoveNamespace` — расчёт на `idleObjectReaper`. При высоком churn rate может накапливать stale записи быстрее, чем reaper убирает. -- **Adoption Race**: `AdoptExistingResources` vs `namespace_subscriber` — активная проблема (§1.3). +- **Adoption Race**: ~~`AdoptExistingResources` vs `namespace_subscriber` — активная проблема~~ — закрыто: `PreRegisterManagedNamespaces` перед adopt/cleanup (`2a7d6101`). - **Stuck Failed Phase**: ~~Namespace в `failed` не восстанавливается без рестарта~~ — закрыто: `RunReconciler` + полная цепочка error propagation (§1.4). --- @@ -95,7 +95,7 @@ |---------|--------|------------| | Informer Lifecycle Management | ✅ Hardened | per-NS context cancel + RemoveNamespace во всех компонентах | | Centralized Mutex | ⚠️ Приемлемо | sharding в backlog, не актуально до >500 NS | -| Manual Adoption | ❌ Активная проблема | race при rolling update | +| Manual Adoption | ✅ Закрыто | race при rolling update (`2a7d6101`) | | No Explicit State Machine | ✅ Частично закрыто | stuck-failed закрыт (`919e8439`); явная state machine в backlog | | Eventual Consistency | ⚠️ Смягчено | параллельный dispatch уменьшает окно, но не устраняет | @@ -117,7 +117,7 @@ - **Maturity**: Архитектурно зрелый, хорошо документированный, с явным reasoning и поэтапным внедрением. - **Complexity**: Высокая в синхронизации и lifecycle. Снижена за счёт формализации `RemoveNamespace` контракта. - **Maintainability**: Среднесрочная — без явной state machine сложность будет расти. Auto-recovery из failed работает. -- **Production-Grade**: Близко — informer lifecycle корректен, dispatch параллелен, cleanup симметричен, stuck-failed закрыт. Основной gap: AdoptExistingResources race. +- **Production-Grade**: Близко — informer lifecycle корректен, dispatch параллелен, cleanup симметричен, stuck-failed закрыт, AdoptExistingResources race закрыт. --- @@ -155,18 +155,21 @@ --- -### 1.3 Manual Adoption (AdoptExistingResources) — ❌ АКТИВНАЯ ПРОБЛЕМА +### 1.3 Manual Adoption (AdoptExistingResources) — ✅ ЗАКРЫТ (коммит `2a7d6101`) **Сценарий: гонка adoption vs watcher при старте** -1. Executor стартует. `AdoptExistingResources` берёт `DefaultNSResolver().Snapshot()` — только статические NS из env. -2. Параллельно: `RunManagedNamespaceWatcher` → `BootstrapAndDispatch()` → `registerNamespace()` добавляет managed NS. -3. `AdoptExistingResources` уже завершила loop — managed NS не попал в snapshot. Orphaned pods в tenant NS не приняты. -4. `CleanupOldExecutorObjects` сочтёт старые pod-ы orphaned → удалит → cold start для всех функций. +**Что было:** `AdoptExistingResources` и `CleanupOldExecutorObjects` запускались до `StartNSWatcher`. `DefaultNSResolver().Snapshot()` возвращал только статические NS из `FISSION_RESOURCE_NAMESPACES` → managed NS не покрывались: +- Pods от предыдущего executor в managed NS не adoptировались (сохраняли старый `instanceID`) → poolmgr создавал новые pool pods → cold start. +- Старые RS/deployments в managed NS не чистились → накапливались. -**При rolling update:** два executor-а параллельно патчат `instanceID` на одних pod-ах → race. +**Что сделано:** `multitenant.PreRegisterManagedNamespaces(ctx, logger, kubernetesClient)` — синхронный `Namespaces.List` с label `fission.io/managed=true` вызывается в `executor.go` **до** goroutines adopt+cleanup. Добавляет все managed NS в `DefaultNSResolver`. Идемпотентен с последующим `AddFunc` из watcher. Не ломает при ошибке API (warn + proceed). -**Статус:** не исправлено. Требует либо задержки `AdoptExistingResources` до завершения первого `BootstrapAndDispatch`, либо включения managed NS в `Snapshot()` на момент adoption. +**End-to-end после фикса:** +1. `PreRegisterManagedNamespaces` → `DefaultNSResolver` содержит static + managed NS +2. `AdoptExistingResources` → патчит pods в managed NS с новым `instanceID` +3. `CleanupOldExecutorObjects` / `GetReaperNamespace()` → видит managed NS → чистит стale объекты +4. `StartNSWatcher` → `AddFunc` срабатывает для тех же NS — `DefaultNSResolver().AddNamespace()` idempotent, `AddNamespace` executor types dedup-protected --- @@ -226,13 +229,13 @@ 100 NS × 5 informer-типов × LIST каждые 30 мин = **500 concurrent LIST** к Kubernetes API. Не изменилось, не исправлено. -### Stuck Failed Accumulation +### Stuck Failed Accumulation — ✅ ЗАКРЫТ -10 ns/час × 1% API error rate = ~2.4 failed NS/сутки. ~72 за 30 дней. Не исправлено (§1.4). +Failed NS автоматически ретраятся `RunReconciler` каждые 30с и выходят из `failed` при восстановлении API. Накопления больше не происходит. -### AdoptExistingResources Race +### AdoptExistingResources Race — ✅ ЗАКРЫТ -При rolling update — race на `instanceID` патч. Не исправлено (§1.3). +`PreRegisterManagedNamespaces` синхронно добавляет managed NS в `DefaultNSResolver` до adopt/cleanup. Старые pods adoptируются, stale объекты чистятся. Подробно — §1.3. --- @@ -242,11 +245,11 @@ Error propagation исправлена во всей цепочке: `setupSAAndRoleBindings` → `EnsureNamespaceSA` → `registerNamespace` → executor subscriber. `RunReconciler` логирует retry и исход. -### P2 — AdoptExistingResources после BootstrapAndDispatch +### P2 — AdoptExistingResources после BootstrapAndDispatch — ✅ ЗАКРЫТ (`2a7d6101`) -Либо: подождать первый `BootstrapAndDispatch()` через канал-сигнал, затем запускать `AdoptExistingResources`. Либо: в `AdoptExistingResources` использовать `NamespaceManager.Snapshot()` вместо `DefaultNSResolver().Snapshot()` (managed NS уже добавлены к этому моменту через BootstrapAndDispatch). +`PreRegisterManagedNamespaces` вызывается синхронно до adopt/cleanup. Делает один `Namespaces.List(label=fission.io/managed=true)` → добавляет все managed NS в `DefaultNSResolver`. После этого adopt и cleanup покрывают полный tenant NS set. -**Влияние:** устраняет orphaned pods при холодном старте и rolling update. +**Влияние:** устранены orphaned pods при холодном старте и resource leak (stale RS/deployments). ### P3 — NamespaceCondition на k8s Namespace объекте @@ -273,4 +276,4 @@ Error propagation исправлена во всей цепочке: `setupSAAnd **Технически реализуемо** без breaking interface change. Полный код — в `FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md §5`. -**Вердикт:** не оправдано при текущей нагрузке. Реальный bottleneck — AdoptExistingResources race, не mutex. Sharded mutex — в backlog, актуально при >500 concurrent tenant с >1 onboarding/sec. +**Вердикт:** не оправдано при текущей нагрузке. Реальный bottleneck — AdoptExistingResources race — закрыт (`2a7d6101`). Sharded mutex — в backlog, актуально при >500 concurrent tenant с >1 onboarding/sec.