Задача Егора
This commit is contained in:
@@ -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-хеши V1–V4 совпадают. 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.
|
||||
|
||||
Reference in New Issue
Block a user