Задача Егора 2ч.

This commit is contained in:
2026-07-17 01:27:02 +03:00
parent 3f685d3e22
commit 3d798c13e3
20 changed files with 2003 additions and 275 deletions

View File

@@ -126,16 +126,24 @@ env:
снимают readiness; резервная H2-БД не считается готовой tenant-БД.
При добавлении тенанта через API:
1. `TenantLifecycleService` создаёт отдельный candidate pool, проверяет соединение и Flyway
2. `KubernetesTenantSecretUpdater` выполняет `GET` актуального Secret и применяет к списку
1. `TenantConfigWatcher` до мутации разбирает и при необходимости применяет текущую
mounted-проекцию как безопасный baseline; ошибка подготовки прерывает операцию
2. `TenantLifecycleService` создаёт отдельный candidate pool, проверяет соединение и Flyway
3. `KubernetesTenantSecretUpdater` выполняет `GET` актуального Secret и применяет к списку
только upsert запрошенного домена
3. полный объект Secret отправляется условным `PUT` с текущим `metadata.resourceVersion`;
4. полный объект Secret отправляется условным `PUT` с текущим `metadata.resourceVersion`;
при `409 Conflict` выполняются повторное чтение и повторное применение своей мутации
4. `TenantRoutingDataSource` атомарно публикует candidate только после успешной либо
5. `TenantRoutingDataSource` атомарно публикует candidate только после успешной либо
подтверждённой повторным чтением персистенции
5. старый pool перестаёт принимать новые запросы и закрывается после drain/grace timeout
6. `TenantConfigWatcher` на остальных pod видит обновлённый файл и каждые 30 секунд
применяет добавление или удаление домена
6. старый pool перестаёт принимать новые запросы и передаётся на закрытие после
drain/grace timeout
7. lifecycle возвращает `TenantLifecycleMutationResult` с `TenantSecretUpdateReceipt`;
semantic fence регистрируется только для фактически записанного изменения
(`persisted=true`, `changed=true`)
8. `TenantConfigWatcher` на остальных pod видит обновлённый файл и каждые 30 секунд
синхронизирует полный нормализованный `TenantConfig`, включая изменения `name`, `domain`,
`url`, `username` и `password`; новый или изменённый pool проходит проверку соединения, Flyway
и атомарный swap без повторной записи Secret
Удаление использует тот же persistence-first порядок и применяет к актуальному Secret только
remove указанного домена. Число попыток записи ограничено тремя, задержка между повторами
@@ -147,10 +155,17 @@ backend восстанавливает прежний снимок только
Неопределённый сетевой результат сначала сверяется повторным `GET`; совпавшее целевое
состояние принимается без квитанции, допускающей небезопасный автоматический откат.
Watcher пока не заменяет подключение существующего домена при изменении URL или credentials.
Такое изменение сбрасывает readiness, но полное применение на другом pod относится к
проблеме №13. До её исправления нельзя считать watcher механизмом полной синхронизации всех
полей `TenantConfig`.
Watcher подтверждает SHA-256 файловой ревизии только после полного успешного применения
снимка. При ошибке прежний применённый хеш сохраняется, readiness снимается, а синхронизация
повторяется с экспоненциальной задержкой от 30 до 300 секунд.
Защита от задержки kubelet основана не на raw hash, а на полном нормализованном снимке.
Квитанция фактической persisted-мутации задаёт `previousTenants` и `committedTenants`:
previous и промежуточные committed-снимки временно deferred, последний committed ожидается.
Поэтому последовательность H0 → H1 → H2 не откатывается при доставке H0/H1, а H2 применяется.
Неизвестный merged snapshot, не совпадающий с deferred-состояниями, применяется сразу.
Persisted no-op и локальная операция fence не создают. Если sync оборвался после swap на
публикации readiness, retry проверяет уже активное соединение и не выполняет второй swap.
Kubernetes-клиент доверяет только service-account CA и проверяет hostname API server.
Поскольку реализация использует HTTP `GET` и `PUT`, минимальная Role должна разрешать только