doc: add detailed namespace rewrite logic
This commit is contained in:
@@ -0,0 +1,589 @@
|
||||
# 2026-04-26 — Layer 1 namespace rewrite: подробная логика правок
|
||||
|
||||
## Зачем этот документ
|
||||
|
||||
Нужен не просто список коммитов, а объяснение инженерной логики:
|
||||
|
||||
- что именно было не так в коде;
|
||||
- почему исправление выбрано именно таким;
|
||||
- почему изменения разбиты на маленькие шаги;
|
||||
- какие инварианты я старался сохранить;
|
||||
- что уже исправлено, а что еще нет.
|
||||
|
||||
Этот документ описывает серию маленьких безопасных шагов в ветке
|
||||
`rewrite/layer1-namespace-manager-step1`.
|
||||
|
||||
Основной принцип серии:
|
||||
|
||||
1. Не делать большой взрывной rewrite.
|
||||
2. Сначала сузить race-surface и разъединить старую статическую модель от новой динамической.
|
||||
3. Исправлять реальные дефекты отдельно от mechanical refactor.
|
||||
4. После каждого шага отдельно проверять соответствующий пакет тестами.
|
||||
|
||||
---
|
||||
|
||||
## Исходная архитектурная проблема
|
||||
|
||||
Переделанный Layer 1 жил в гибридном состоянии.
|
||||
|
||||
Старая модель Fission:
|
||||
|
||||
- список resource namespaces задается один раз на старте;
|
||||
- компоненты считают этот список immutable;
|
||||
- informer factories строятся из startup configuration.
|
||||
|
||||
Новая multi-tenant модель:
|
||||
|
||||
- namespace появляется позже, уже после старта процесса;
|
||||
- watcher видит label `fission.io/managed=true`;
|
||||
- компоненты должны подключить новый namespace на лету.
|
||||
|
||||
Из-за этого в коде образовался разрыв между двумя мирами:
|
||||
|
||||
1. Часть кода уже работает как dynamic system.
|
||||
2. Часть кода все еще читает глобальную map namespace-ов напрямую, как будто она immutable.
|
||||
3. В некоторых компонентах startup-path и dynamic-path оказались несимметричными.
|
||||
4. В некоторых местах общий global dedup конфликтует с локальной логикой конкретного компонента.
|
||||
|
||||
Это и есть корневой дефект всей подсистемы: не один конкретный баг, а отсутствие единого namespace lifecycle contract.
|
||||
|
||||
---
|
||||
|
||||
## Что было решено не делать сразу
|
||||
|
||||
Я сознательно не пошел в большой rewrite в один коммит.
|
||||
|
||||
Почему:
|
||||
|
||||
1. Слишком много точек входа: executor, router, buildermgr, storagesvc, utils.
|
||||
2. Если переписать все сразу, невозможно будет локализовать регрессию.
|
||||
3. Уже были реальные functional дефекты в нескольких местах, их удобнее чинить изолированно.
|
||||
4. Пользователь отдельно попросил идти последовательно и проверять после каждого изменения.
|
||||
|
||||
Поэтому выбран bounded rewrite: сначала вычищать старые опасные предположения, затем исправлять функциональные несовпадения, и только потом идти к более крупному NamespaceManager.
|
||||
|
||||
---
|
||||
|
||||
## Инварианты серии
|
||||
|
||||
Во всех шагах я старался держать одинаковые правила.
|
||||
|
||||
### 1. Не ломать действующий onboarding contract
|
||||
|
||||
Если namespace приходит через label watcher, компоненты должны продолжать подключать его без рестарта. Нельзя было ради рефактора возвращаться к статической модели.
|
||||
|
||||
### 2. Не менять лишние контракты одновременно
|
||||
|
||||
Если шаг про snapshot API, он не должен заодно переписывать cleanup semantics.
|
||||
|
||||
### 3. Сначала механические и безопасные сдвиги, потом functional fixes
|
||||
|
||||
Это нужно, чтобы понимать, баг возник из-за новой логики или уже существовал ранее.
|
||||
|
||||
### 4. Каждый шаг должен быть проверяем локально
|
||||
|
||||
После каждого шага запускались тесты по затронутому пакету, а не абстрактное «кажется, всё нормально».
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Snapshot API для namespace resolver
|
||||
|
||||
Коммит: `c987fa0`
|
||||
|
||||
### Что было не так
|
||||
|
||||
`NamespaceResolver` уже имел mutex для записи через `AddNamespace`, но многие потребители читали `FissionResourceNS` напрямую.
|
||||
|
||||
Это означало следующее:
|
||||
|
||||
1. Запись в map уже динамическая.
|
||||
2. Чтение в части мест по-прежнему не thread-safe.
|
||||
3. Код внешне выглядел как безопасный, потому что mutex в структуре есть, но контракт чтения не был централизован.
|
||||
|
||||
То есть защита существовала только наполовину.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
В `pkg/utils/namespace.go` добавлены:
|
||||
|
||||
- `Snapshot()`
|
||||
- `SnapshotWithOptions()`
|
||||
|
||||
Их логика:
|
||||
|
||||
1. Под read lock взять текущее состояние.
|
||||
2. Скопировать его в detached slice.
|
||||
3. Отсортировать, чтобы получить стабильный детерминированный порядок.
|
||||
|
||||
Почему именно slice snapshot, а не снова map:
|
||||
|
||||
1. Читателям в основном нужен именно проход по namespace-ам.
|
||||
2. Slice удобнее для безопасной итерации.
|
||||
3. Сортировка убирает дрожание порядка и делает поведение более предсказуемым в тестах и логике startup factory generation.
|
||||
|
||||
### Почему это был правильный первый шаг
|
||||
|
||||
Этот шаг почти не меняет бизнес-логику. Он не трогает watchers, RBAC, cleanup, lifecycle events. Он вводит базовый безопасный API, на который потом можно переводить потребителей.
|
||||
|
||||
### Что было переведено сразу
|
||||
|
||||
Чтобы snapshot API не оставался мертвым кодом, на него были переведены:
|
||||
|
||||
- `pkg/utils/informer.go`
|
||||
- startup factory creation в `pkg/executor/executor.go`
|
||||
|
||||
Логика этого выбора:
|
||||
|
||||
1. Это общие helper path.
|
||||
2. Они касаются большого числа компонентов.
|
||||
3. Но при этом change поверхностный: вместо прямой итерации по map берется snapshot.
|
||||
|
||||
### Отдельный мелкий дефект, найденный на шаге 1
|
||||
|
||||
Новые тесты создали локальный `NamespaceResolver` без logger. Выяснилось, что часть методов предполагает ненулевой logger. Это нехорошо само по себе: utility object не должен падать только потому, что его используют вне global singleton.
|
||||
|
||||
Поэтому были добавлены nil checks вокруг debug/info логов в resolver.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/utils/...`
|
||||
- `go test ./pkg/executor/...`
|
||||
|
||||
Смысл проверки:
|
||||
|
||||
1. Убедиться, что snapshot API корректен как utility layer.
|
||||
2. Убедиться, что startup path executor не поменял поведение.
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Исправление namespace routing в serviceaccount checker
|
||||
|
||||
Коммит: `9ce9829`
|
||||
|
||||
### Что было не так
|
||||
|
||||
В `pkg/utils/serviceaccount.go` был более тонкий дефект, чем просто прямое чтение map.
|
||||
|
||||
В `runSACheck()` одна и та же переменная `ns` переиспользовалась внутри цикла по permission groups.
|
||||
|
||||
Смысл проблемы:
|
||||
|
||||
1. Есть исходный base namespace.
|
||||
2. Для fetcher нужен путь через `GetFunctionNS(baseNS)`.
|
||||
3. Для builder нужен путь через `GetBuilderNS(baseNS)`.
|
||||
4. Но код мутировал саму переменную `ns` по мере обхода permission sets.
|
||||
|
||||
Это опасно, потому что builder resolution начинает зависеть от предыдущего шага цикла, а не от исходного namespace.
|
||||
|
||||
Если `FunctionNamespace` и `BuilderNamespace` различаются, route builder SA может поехать.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
Изменение было разбито на две части:
|
||||
|
||||
1. Итерироваться не по `FissionResourceNS` напрямую, а по `Snapshot()`.
|
||||
2. Явно вычислять `targetNS` из `baseNS` через отдельный метод `resolveSANamespace(baseNS, saName)`.
|
||||
|
||||
Почему выделен отдельный метод:
|
||||
|
||||
1. Логика namespace routing становится читаемой как отдельный контракт.
|
||||
2. Её можно тестировать отдельно.
|
||||
3. В коде исчезает скрытая мутация переменной цикла.
|
||||
|
||||
### Почему я не переписывал весь serviceaccount.go сразу
|
||||
|
||||
В файле еще остаются спорные места:
|
||||
|
||||
- глобальные `fetcherCheck` / `builderCheck`;
|
||||
- мутация `permission.exists`;
|
||||
- runtime provisioning через `LocalSubjectAccessReview`.
|
||||
|
||||
Но если решать всё сразу, шаг становится слишком широким. На этом этапе была цель исправить именно namespace routing bug и убрать прямую итерацию по общей map.
|
||||
|
||||
### Какой тест был добавлен
|
||||
|
||||
Добавлен unit test на `resolveSANamespace()`:
|
||||
|
||||
- fetcher на default namespace должен идти в function namespace;
|
||||
- builder на default namespace должен идти в builder namespace;
|
||||
- tenant namespace должен сохраняться как tenant namespace.
|
||||
|
||||
Тест важен не из-за синтаксиса, а потому что он фиксирует смысловую развязку между двумя namespace path.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/utils/...`
|
||||
- `go test ./pkg/executor/...`
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Перевод runtime loops на snapshot API
|
||||
|
||||
Коммит: `6102b27`
|
||||
|
||||
### Что было не так
|
||||
|
||||
Даже после появления snapshot API ещё оставались runtime loops, которые напрямую читали общую map namespace-ов в горячих путях:
|
||||
|
||||
- adopt existing resources;
|
||||
- idle object reaper;
|
||||
- orphan archive pruning.
|
||||
|
||||
Это плохо не только из-за race. Это также концептуально закрепляет старую модель «список namespace-ов — это просто глобальная map, в которую можно смотреть отовсюду».
|
||||
|
||||
### Что я сделал
|
||||
|
||||
Перевёл на `Snapshot()` следующие места:
|
||||
|
||||
- `pkg/executor/executortype/container/containermgr.go`
|
||||
- `pkg/executor/executortype/newdeploy/newdeploymgr.go`
|
||||
- `pkg/executor/executortype/poolmgr/gpm.go`
|
||||
- `pkg/storagesvc/archivePruner.go`
|
||||
|
||||
### Почему именно эти места были хорошим кандидатом
|
||||
|
||||
Потому что это mechanical refactor:
|
||||
|
||||
1. Логика списков не меняется.
|
||||
2. Namespace source меняется с raw map на stable snapshot.
|
||||
3. Поведение должно оставаться тем же, кроме устранения unsafe read.
|
||||
|
||||
### Что это дало
|
||||
|
||||
1. Уменьшило площадь прямого доступа к глобальному mutable состоянию.
|
||||
2. Подготовило код к следующему этапу, когда namespace registry станет ещё более централизованным.
|
||||
3. Сделало background loops более предсказуемыми при одновременном dynamic onboarding.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/executor/... ./pkg/storagesvc/...`
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Исправление buildermgr dedup bug
|
||||
|
||||
Коммит: `56a499a`
|
||||
|
||||
### Это уже не mechanical refactor, а реальный functional fix
|
||||
|
||||
### Что было не так
|
||||
|
||||
`buildermgr.StartNSWatcher()` при появлении нового namespace делал:
|
||||
|
||||
1. `envw.AddNamespace()`
|
||||
2. `pkgw.AddNamespace()`
|
||||
|
||||
Но оба watcher-а использовали один и тот же глобальный dedup через `nsResolver.AddNamespace()`.
|
||||
|
||||
Фактический эффект:
|
||||
|
||||
1. Первый вызов успешно добавляет namespace в global resolver.
|
||||
2. Второй вызов видит, что namespace уже «есть».
|
||||
3. И просто выходит.
|
||||
|
||||
То есть в buildermgr динамический namespace мог получить только часть подписок.
|
||||
|
||||
Это уже не theoretical risk, а реальный дефект логики.
|
||||
|
||||
### Почему проблема архитектурная
|
||||
|
||||
Здесь смешались два уровня ответственности:
|
||||
|
||||
1. Global registry должен знать, что namespace существует.
|
||||
2. Конкретный компонент должен знать, подписался ли он уже на этот namespace.
|
||||
|
||||
Это разные виды dedup.
|
||||
|
||||
Один глобальный dedup не может корректно заменить локальный dedup для двух разных subcomponents.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
Логику развёл по уровням:
|
||||
|
||||
1. В `pkg/buildermgr/ns_watcher.go` global resolver обновляется один раз.
|
||||
2. `environmentWatcher` dedup делает по своей map `envWatchInformer`.
|
||||
3. `packageWatcher` dedup делает по своей map `pkgInformer`.
|
||||
|
||||
### Почему это правильнее
|
||||
|
||||
Теперь структура похожа на executor path:
|
||||
|
||||
1. Глобальный реестр говорит: namespace известен системе.
|
||||
2. Каждый компонент сам решает: свои informers он уже поднял или нет.
|
||||
|
||||
Именно так должен выглядеть multi-component dynamic onboarding.
|
||||
|
||||
### Что я сознательно не делал
|
||||
|
||||
Не добавлял remove/cleanup и не переделывал buildermgr lifecycle целиком. На шаге требовалось только убрать ошибку дедупликации.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/buildermgr/...`
|
||||
|
||||
Тестов в пакете немного, но для этого шага важно было хотя бы подтвердить, что wiring собирается и не поломан compile-time.
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Исправление parity gap в newdeploy
|
||||
|
||||
Коммит: `94f26b6`
|
||||
|
||||
### Что было не так
|
||||
|
||||
`MakeNewDeploy()` на старте процесса регистрировал оба типа handler-ов:
|
||||
|
||||
- `FunctionEventHandlers()`
|
||||
- `EnvEventHandlers()`
|
||||
|
||||
Но `AddNamespace()` для динамически появившегося namespace регистрировал только `FunctionEventHandlers()`.
|
||||
|
||||
Это значит, что два namespace-а с одинаковым содержимым вели себя по-разному только из-за времени появления:
|
||||
|
||||
1. startup namespace обслуживается полным code path;
|
||||
2. dynamic namespace обслуживается урезанным code path.
|
||||
|
||||
Это очень плохое свойство для Layer 1, потому что поведение перестаёт зависеть только от данных и начинает зависеть от истории запуска процесса.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
В `newdeploy.AddNamespace()` добавил регистрацию `EnvEventHandlers()` рядом с `FunctionEventHandlers()`.
|
||||
|
||||
### Почему fix именно такой
|
||||
|
||||
Потому что это минимальное исправление семантической несимметрии.
|
||||
|
||||
Я не придумывал новую абстракцию, а привёл dynamic path к уже существующему startup contract.
|
||||
|
||||
### Инженерный смысл шага
|
||||
|
||||
Это важный принцип всей серии: если startup-path и late onboarding-path делают похожую работу, они должны проходить через один и тот же контракт, а не через два слегка разных набора side effects.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/executor/executortype/newdeploy`
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Защита router informer maps от гонок
|
||||
|
||||
Коммит: `87477d4`
|
||||
|
||||
### Что было не так
|
||||
|
||||
В router динамический namespace добавляет новые informer-ы в две map:
|
||||
|
||||
- `triggerInformer`
|
||||
- `funcInformer`
|
||||
|
||||
Параллельно `updateRouter()` итерируется по тем же map, собирая триггеры и функции для rebuild router-а.
|
||||
|
||||
Плюс `functionReferenceResolver` получает `funcInformer` и тоже читает его напрямую.
|
||||
|
||||
Это создаёт классическую проблему:
|
||||
|
||||
1. одна goroutine пишет в map;
|
||||
2. другая одновременно по ней итерируется;
|
||||
3. третья читает её через resolver.
|
||||
|
||||
Результат может быть от паники `concurrent map iteration and map write` до тихого чтения неполного состояния.
|
||||
|
||||
### Почему шаг стал чуть шире
|
||||
|
||||
Простой mutex только вокруг `HTTPTriggerSet.AddNamespace()` не решал бы проблему полностью, потому что `functionReferenceResolver` держал свою ссылку на ту же mutable структуру.
|
||||
|
||||
Поэтому понадобилось сделать две вещи одновременно:
|
||||
|
||||
1. Защитить maps в `HTTPTriggerSet` через `RWMutex` и snapshot helpers.
|
||||
2. Дать `functionReferenceResolver` собственный thread-safe путь доступа к informer registry.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
В `HTTPTriggerSet`:
|
||||
|
||||
- добавлен `RWMutex`;
|
||||
- добавлены `snapshotTriggerInformers()`;
|
||||
- добавлены `snapshotFuncInformers()`;
|
||||
- `updateRouter()` и setup handlers теперь работают по snapshot-спискам.
|
||||
|
||||
В `functionReferenceResolver`:
|
||||
|
||||
- добавлен `RWMutex`;
|
||||
- чтение informer-а по namespace теперь под read lock;
|
||||
- добавлен `addInformer()` для безопасного добавления нового namespace.
|
||||
|
||||
В `router.AddNamespace()`:
|
||||
|
||||
- запись в `triggerInformer` и `funcInformer` идёт под lock;
|
||||
- resolver получает новый informer через собственный безопасный метод.
|
||||
|
||||
### Почему именно snapshot-helpers, а не держать lock во время всей итерации
|
||||
|
||||
Потому что rebuild router-а и чтение store-ов могут быть относительно дорогими. Держать глобальный lock на всё это время было бы лишним. Нам нужен был не coarse lock на длинный процесс, а короткий lock на получение стабильного снимка ссылок на informer-ы.
|
||||
|
||||
То есть стратегия такая:
|
||||
|
||||
1. Быстро снять snapshot ссылок.
|
||||
2. Отпустить lock.
|
||||
3. Работать со snapshot уже без блокировки записи.
|
||||
|
||||
Это лучше и по безопасности, и по latency.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/router/...`
|
||||
|
||||
---
|
||||
|
||||
## Почему шаги документировались отдельно
|
||||
|
||||
Я сохранял отдельный thinking-файл на каждый шаг не ради бюрократии, а ради трассируемости.
|
||||
|
||||
Когда изменения маленькие, отдельные документы позволяют понять:
|
||||
|
||||
1. какой дефект исправлял именно этот коммит;
|
||||
2. что было осознанно оставлено за рамками;
|
||||
3. какой тест подтверждал именно этот шаг;
|
||||
4. где functional fix, а где только mechanical safety refactor.
|
||||
|
||||
Именно это позволяет потом анализировать regressions не по памяти, а по истории.
|
||||
|
||||
---
|
||||
|
||||
## Что осталось нерешённым после step 6
|
||||
|
||||
Несмотря на шесть шагов, это ещё не финальный NamespaceManager rewrite.
|
||||
|
||||
Остаются важные вопросы.
|
||||
|
||||
### 1. Нет remove/cleanup semantics
|
||||
|
||||
Система умеет add, но почти не умеет delete/relabel cleanup.
|
||||
|
||||
Что это значит practically:
|
||||
|
||||
- informer-ы и локальные registry entries живут вечно;
|
||||
- once onboarded, always onboarded;
|
||||
- короткоживущие tenant namespace-ы будут оставлять мусор.
|
||||
|
||||
### 2. `serviceaccount.go` всё ещё не идеален
|
||||
|
||||
Текущий `serviceaccount.go` уже лучше, чем до step 2, но файл всё ещё сложный:
|
||||
|
||||
- глобальные `fetcherCheck` / `builderCheck` живут как process-wide mutable objects;
|
||||
- `permission.exists` мутируется в runtime;
|
||||
- provisioning и permission-check тесно сцеплены.
|
||||
|
||||
Это отдельный кандидат на следующий bounded refactor, но уже не маленький mechanical шаг.
|
||||
|
||||
### 3. Глобальный resolver всё ещё остаётся transitional abstraction
|
||||
|
||||
`NamespaceResolver` теперь безопаснее для чтения, но это пока ещё не полноценный NamespaceManager с событиями, remove lifecycle и подписками.
|
||||
|
||||
Он всё ещё ближе к thread-safe registry, чем к полной orchestration layer.
|
||||
|
||||
### 4. Cleanup/restart/backfill lifecycle ещё не централизован
|
||||
|
||||
Часть компонентов уже ближе к единообразию, но по-прежнему нет одного центрального orchestration contract вида:
|
||||
|
||||
- add existing namespaces on startup;
|
||||
- reconcile on relabel;
|
||||
- remove on delete;
|
||||
- rebuild after restart;
|
||||
- re-register late component safely.
|
||||
|
||||
---
|
||||
|
||||
## Почему я не стал сразу делать remove/cleanup
|
||||
|
||||
Потому что это уже следующая категория сложности.
|
||||
|
||||
До step 6 изменения укладывались в схему:
|
||||
|
||||
- локальный и понятный дефект;
|
||||
- ограниченный blast radius;
|
||||
- тестируемый пакет;
|
||||
- отдельный маленький commit.
|
||||
|
||||
Remove/cleanup меняет уже жизненный цикл системы и затрагивает много мест одновременно:
|
||||
|
||||
- watcher behavior;
|
||||
- manager lifecycle;
|
||||
- informer shutdown semantics;
|
||||
- cache invalidation;
|
||||
- resolver state.
|
||||
|
||||
Это не тот шаг, который разумно смешивать с небольшими safety fixes.
|
||||
|
||||
---
|
||||
|
||||
## Почему такая стратегия лучше, чем «переписать всё сразу»
|
||||
|
||||
Потому что сейчас уже есть видимый результат с низким риском:
|
||||
|
||||
1. Уменьшено число прямых доступов к общей mutable map.
|
||||
2. Исправлен реальный functional bug в buildermgr.
|
||||
3. Исправлена реальная логическая ошибка в serviceaccount namespace routing.
|
||||
4. Исправлена несимметрия в newdeploy dynamic path.
|
||||
5. Закрыта явная router race-surface.
|
||||
|
||||
И всё это не одним большим коммитом, а серией шагов с локальной верификацией.
|
||||
|
||||
Для инфраструктурного кода это важнее, чем «красивый большой rewrite», который сложно раскладывать при регрессиях.
|
||||
|
||||
---
|
||||
|
||||
## Какие проверки были прогнаны по ходу серии
|
||||
|
||||
После шагов запускались:
|
||||
|
||||
- `go test ./pkg/utils/...`
|
||||
- `go test ./pkg/executor/...`
|
||||
- `go test ./pkg/storagesvc/...`
|
||||
- `go test ./pkg/buildermgr/...`
|
||||
- `go test ./pkg/router/...`
|
||||
|
||||
Логика была такая:
|
||||
|
||||
1. Не гонять каждый раз всю репу, если шаг локальный.
|
||||
2. Но обязательно проверять затронутый пакет и соседний пакет, если change касается shared utility layer.
|
||||
|
||||
---
|
||||
|
||||
## Текущее состояние после серии
|
||||
|
||||
Серия шагов 1-6 не завершает rewrite, но заметно улучшает базу для следующего этапа.
|
||||
|
||||
Что теперь стало лучше:
|
||||
|
||||
1. Namespace reads стали заметно более дисциплинированными.
|
||||
2. Dynamic namespace onboarding стал логически ровнее между компонентами.
|
||||
3. В router исчезла наиболее явная race-surface на informer maps.
|
||||
4. Buildermgr больше не теряет часть подписок на новый namespace из-за неправильного dedup.
|
||||
|
||||
Что остаётся следующим осмысленным этапом:
|
||||
|
||||
1. Вынесение уже полноценного NamespaceManager как orchestration layer.
|
||||
2. Remove/cleanup lifecycle.
|
||||
3. Разделение discovery, registry и provisioning.
|
||||
4. Дополнительные тесты на restart/relabel/delete/burst onboarding.
|
||||
|
||||
---
|
||||
|
||||
## Отдельная заметка про `serviceaccount.go`
|
||||
|
||||
На момент написания этого документа файл `pkg/utils/serviceaccount.go` был заново перечитан по текущему содержимому. Документ описывает актуальную логику файла в его текущем состоянии, а не только то состояние, которое было в момент коммита step 2.
|
||||
|
||||
Это важно, потому что именно в этом файле пользовательский контекст отдельно предупредил о возможных дополнительных изменениях между сообщениями.
|
||||
Reference in New Issue
Block a user