Задача Егора

This commit is contained in:
dipatrik10
2026-07-16 22:56:43 +03:00
parent 85f61436b6
commit 3f685d3e22
33 changed files with 3777 additions and 279 deletions

View File

@@ -1,6 +1,6 @@
# Журнал исправления проблем
Обновлено: 2026-07-13 (Europe/Moscow).
Обновлено: 2026-07-15 (Europe/Moscow).
## Область и неизменяемые данные
@@ -54,8 +54,8 @@
| 6 | исправлено и проверено | Teacher-only поиск загружал только базовые правила исходного преподавателя до применения override. | Один snapshot overrides; релевантные `newTeacher`-замены достраивают exact base occurrences по уникальным датам, затем применяются overrides, финальный teacher-фильтр и deduplicate. | 5 новых A→B/phantom/cache/dedup/regression тестов; узкий прогон 10/0/0/0, полный backend 79/0/0/0. |
| 7 | исправлено и проверено | Контроллер сравнивал новые слоты только с сохранёнными правилами, не проверял роль преподавателя и enum формата, а нечётные часы приводили к перерасходу генератора. | `ScheduleRuleService` валидирует весь кандидат до мутации, попарно проверяет дубли/ресурсы, требует `TEACHER`, `Очно`/`Онлайн` и чётные часы; строка семестра сериализует запись. V4 закрепляет чётность и точный дубль в БД. | 23 service/MockMvc/Flyway/PostgreSQL tests; полный backend: 102/0/0/0. |
| 8 | исправлено и проверено | `saveGrid` удалял данные до полной валидации и ловил исключение внутри transactional controller-метода, поэтому Spring коммитил delete и уже обработанные строки. | `AcademicCalendarGridService` полностью валидирует и строит replacement до delete, атомарно заменяет строки и очищает кэш через `afterCommit`; контроллер стал HTTP-адаптером. | 8 unit + 1 PostgreSQL proxy test; fingerprint старой сетки после отказа неизменен; полный backend 111/0/0/0. |
| 9 | в работе | При update старый pool закрывается до создания нового; Hikari допускает ленивый нерабочий pool, Flyway проглатывает ошибку, а отказ Secret-персистенции оставляет локальное состояние изменённым. | Реализуются prepare/validate/migrate кандидата, persistence до atomic swap и компенсация Secret при ошибке активации. | Готовятся unit и PostgreSQL lifecycle tests для credentials/migration/persistence/swap. |
| 10 | не начато | Два pod перезаписывают целый tenant-документ без `resourceVersion`; TCP probes не отражают готовность tenant-БД. | Optimistic locking с retry; отдельные liveness/readiness semantics. | Конкурентные PATCH и health tests; Kustomize-проверка probes. |
| 9 | исправлено и проверено | При update старый pool закрывался до создания нового; Hikari допускал ленивый нерабочий pool, Flyway проглатывал ошибку, а отказ persistence мог сочетаться с изменённым локальным состоянием. | `TenantLifecycleService` выполняет prepare → connection validation → Flyway → Secret persistence atomic snapshot swap; candidate закрывается при отказе, прежний route сохраняется, Secret компенсируется, старый pool закрывается после drain. | 18 целевых unit/MockMvc/PostgreSQL tests; create/update, credentials, invalid connection, Flyway, persistence, swap, HTTP `503` и in-flight connection. Полный backend: 132/0/0/0. |
| 10 | требуется внешнее действие | Два pod перезаписывали целый tenant-документ без `resourceVersion`; TCP probes не отражали готовность tenant-БД. | Backend применяет доменные upsert/remove через GET → условный PUT по `resourceVersion`, ограниченный retry и безопасную компенсацию; Actuator разделяет process-only liveness и readiness обязательных tenant-БД. Оператору остаются RBAC `get/update`, mount без `subPath`, `TENANTS_CONFIG_REQUIRED=true` и HTTP probes в отсутствующем `../k8s`. | 64 целевых теста; полный backend 169/0/0/0; LF-хеши V1V4 совпадают. Kustomize не выполнен: `../k8s` отсутствует. |
| 11 | не начато | Sunday вычисляется повторным мутированием даты; ошибки запросов превращаются в пустые результаты и зелёный статус. | Чистая local-date утилита и три явных состояния проверки. | 2026-07-02, переход года, частичная/полная ошибка API. |
| 12 | не начато | Access JWT хранится в Web Storage; origin исполняет удалённые модули; CSP допускает несовместимые сценарии. | Access JWT только в памяти, восстановление через HttpOnly refresh-cookie; локальные pinned assets; строгий CSP и удаление inline JS. | Login/refresh/logout/reload tests; статический аудит CSP/storage/imports. |
| 13 | не начато | Watcher сравнивает только домены и фиксирует hash до успешной полной синхронизации. | Нормализованное сравнение полного `TenantConfig`, hash после успеха, bounded backoff. | Изменение URL/credentials и retry после ошибки. |
@@ -261,18 +261,100 @@
| Контейнерный полный `mvn test` | 111 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
### № 9 — безопасная замена tenant DataSource
- `DatabaseController` делегирует create/update/delete в единый `TenantLifecycleService` и
возвращает lifecycle-ошибки как русский безопасный `503`, а не ложный `200` или raw
JDBC/Flyway message.
- Новый Hikari candidate не публикуется в маршрутизации до явного `getConnection()` /
`isValid(5)`, успешного Flyway и подтверждённой доменной мутации в актуальном
`tenants-secret`.
- `TenantRoutingDataSource` одной volatile-публикацией заменяет неизменяемый снимок
`TenantConfig + DataSource`; прежняя конфигурация и pool остаются рабочими при ошибке
credentials, миграции, persistence или доатомарной активации.
- Неуспешная persistence не меняет локальный route. Неопределённый результат записи сначала
сверяется повторным чтением и не даёт права на опасный откат. Если swap/remove отказывает
после подтверждённой записи, прежний снимок восстанавливается только при точном совпадении
`resourceVersion`; более новое изменение другого pod не перезаписывается.
- После успешного swap старый pool не закрывается синхронно: `RetiredTenantPoolService`
прекращает новые маршруты, сохраняет уже выданные соединения и закрывает Hikari после
drain либо grace timeout.
- Unit-тесты дополнены отдельной create-веткой, восстановлением снимка без нового domain,
`Connection.isValid(false)` и MockMvc-проверкой `503` без технических деталей.
- PostgreSQL 16.3/Testcontainers-тест подтверждает сохранность старого route при неверном
пароле, повреждённом Flyway checksum и отказе persistence, работоспособность in-flight
соединения во время swap и последующий drain старого pool.
- `docs/API.md`, `docs/ARCHITECTURE.md` и `docs/INFRASTRUCTURE.md` синхронизированы через
AutoUpdateDocs; исправлено устаревшее описание порядка «in-memory до Secret».
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Контейнерный целевой `mvn -Dtest=TenantLifecycleServiceTest,TenantLifecyclePostgreSqlIntegrationTest,TenantRoutingDataSourceTest,DatabaseControllerTest test` | 18 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| Контейнерный полный `mvn test` | 132 теста, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| Повторные LF-нормализованные SHA-256 V1/V2/V3/V4 | Совпадают с ранее зафиксированными значениями. |
### № 10 — межподовая координация tenant-конфигурации и health probes
- Введён `TenantConfigStore` с явными операциями upsert/remove. Kubernetes-реализация перед
каждой мутацией читает актуальный `tenants-secret`, нормализует домены и отправляет полный
объект условным `PUT` с прочитанным `metadata.resourceVersion`.
- `409`, `408`, `429`, `5xx` и транспортные ошибки обрабатываются ограниченно: максимум три
попытки с возрастающей задержкой и повторным применением только собственной доменной
мутации к свежему состоянию. Идемпотентная операция не выполняет `PUT`.
- Успех записи и компенсации больше не определяется одним HTTP 2xx: backend разбирает
возвращённый Secret, сравнивает `tenants.json` с ожидаемым и использует `resourceVersion`
только проверенного объекта. Пустой, неполный или неоднозначный ответ сверяется повторным
`GET` без выдачи квитанции, разрешающей небезопасный откат.
- Компенсация выполняется только для подтверждённой собственной версии. Если текущий Secret
уже получил другой `resourceVersion`, откат прекращается и не затирает изменение другого
pod. Отсутствующий `data.tenants.json`, повторяющиеся домены и Kubernetes mode без
ServiceAccount token обрабатываются fail-closed.
- Добавлен Spring Boot Actuator. `/actuator/health/liveness` зависит только от жизнеспособности
процесса; `/actuator/health/readiness` требует непустой набор обязательных tenant, успешную
миграцию и свежую успешную проверку каждого соединения. H2-заглушка, ошибка обязательного
конфига, migration failure, timeout, недоступность или просроченная проверка дают `503`.
- `TenantDatabaseHealthMonitor` проверяет tenant-БД ограниченно-параллельно в фоне; endpoint
читает только атомарный кэш. Общий deadline отменяет лишь незавершённые задачи, уже
завершившиеся результаты не теряются и получают фактическое время проверки. Scheduler
имеет два потока, поэтому долгий health-pass не блокирует watcher.
- `TenantDataSourceConfig` и watcher поддерживают `TENANTS_CONFIG_REQUIRED=true`: отсутствие,
пустой список или ошибка чтения production-файла фиксируют configuration failure, а H2 не
маскирует отказ. Actuator endpoints исключены из tenant-interceptor и не раскрывают
components, домены, JDBC URL или credentials.
- AutoUpdateDocs синхронизировал `docs/API.md`, `docs/ARCHITECTURE.md` и
`docs/INFRASTRUCTURE.md`. Ограничение watcher по обновлению URL/credentials существующего
домена явно оставлено проблеме № 13.
- В текущей рабочей копии отсутствует внешний каталог `../k8s`, поэтому манифесты не менялись
и Kustomize не запускался. Для production обязательны directory mount без `subPath`,
`TENANTS_CONFIG_REQUIRED=true`, Role `get/update` для `tenants-secret` и HTTP liveness /
readiness probes; применение и rollout остаются внешними действиями оператора.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| `KubernetesTenantSecretUpdaterTest` | 21 тест, 0 failures, 0 errors, 0 skipped; два конкурентных updater, CAS conflict/retry, 408/5xx reconciliation, проверка 2xx и безопасная компенсация. |
| Health/startup target (`TenantDataSourceConfigTest`, registry/monitor/indicator, Actuator, MVC exclusion) | 19 тестов, 0 failures, 0 errors, 0 skipped. |
| Lifecycle/controller/watcher target | 24 теста, 0 failures, 0 errors, 0 skipped; PostgreSQL lifecycle отдельно повторён успешно после единичной Docker-сетевой флуктуации. |
| Контейнерный полный `mvn test` | 169 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS; 40 suites. |
| `git diff --check` | Успешно. |
| Повторные LF-нормализованные SHA-256 V1/V2/V3/V4 | Совпадают с ранее зафиксированными значениями. |
| `kubectl kustomize ../k8s` | Не выполнено: внешний каталог `../k8s` отсутствует в текущем workspace. |
## Точка продолжения
Текущий этап: **9безопасная замена tenant DataSource**.
Текущий этап: **13полная синхронизация изменённых tenant credentials/URL watcher-ом**.
Следующая операция:
1. проверить актуальные create/update потоки `DatabaseController`, swap/remove API
`TenantRoutingDataSource`, Flyway и персистенцию tenant-конфигурации;
2. определить порядок prepare → connection validation → Flyway → persistence → atomic swap,
не закрывающий рабочий pool до полной готовности кандидата;
3. добавить компенсирующий rollback для ошибок персистенции/активации и явные русские
ошибки без ложного `200`;
4. покрыть неверные credentials, ошибку миграции и ошибку персистенции тестами, доказывающими
сохранность старого подключения;
5. выполнить полный прогон и AutoUpdateDocs перед изменением статуса № 9.
1. сравнивать полную нормализованную активную конфигурацию с persisted snapshot, а не только
набор доменов;
2. при изменении URL/username/password выполнять prepare → verify → Flyway → atomic swap без
повторной записи Secret;
3. считать hash применённым только после полной успешной синхронизации и не подтверждать
произвольное содержимое mount через `refreshHash()` после API-мутации;
4. добавить bounded retry/backoff и тесты изменения credentials, восстановления прежнего
файла после ошибки и межподового merged snapshot;
5. выполнить полный прогон и AutoUpdateDocs перед изменением статуса № 13.