6.6 KiB
Context
См. мотивацию в proposal.md и изменяемое поведение в delta specs. Backend
использует Nest 11, Prisma 7 с SQLite adapter, AuthService и локальные
Map-кэши. Публичные auth routes, cookie attributes и success statuses не
меняются; backend e2e migration gate остаётся отложенным по WL-P2-05.
Goals / Non-Goals
Goals:
- Сделать cache страниц журнала ограниченным и полезным для семантически равных запросов.
- Устранить race между проверкой уникальности email и его созданием.
- Гарантировать единственного победителя refresh rotation без случайного revoke его Session.
- Оставить воспроизводимые service-level tests, пока canonical SQLite e2e path недоступен.
Non-Goals:
- Изменять auth response statuses, OpenAPI snapshot или frontend client resilience (WL-P2-04, WL-P2-07 и WL-P2-08).
- Добавлять byte-weight cache budget, распределённый cache, cache invalidation или cross-instance coherence.
- Добавлять auth framework, новую схему Session, grace window либо отдельную историю использованных refresh token.
- Объявлять
test:e2eили общий workspace verdict восстановленными.
Decisions
Bounded cache остаётся domain-local
BrokerageOperationsService получает прямую production dependency
lru-cache и хранит в ней максимум 100 response pages с TTL 30 секунд.
Это использует готовую, тестируемую LRU policy без глобального Nest cache
layer. Cache identity строится отдельно из user/account и нормализованных
effective query parameters: categories сортируются и deduplicate-ятся,
cursor остаётся opaque, а отсутствующий to привязывается к текущему
30-секундному bucket. Cache никогда не заменяет существующую проверку
connection и account ownership.
Альтернативы: Map с ручной eviction повторяет сложную policy; Nest
CacheModule потребует глобального HTTP keying и не подходит user/account
domain cache; byte budget не нужен без подтверждённого memory pressure.
Database constraint определяет регистрацию
AuthService.register сохраняет предварительную проверку как UX
оптимизацию, но считает user.create authoritative. Только ожидаемый Prisma
unique-constraint error P2002 от этой операции преобразуется в
ConflictException с существующим code EMAIL_ALREADY_REGISTERED; прочие
ошибки остаются внутренними. Это устраняет check-then-create race, не меняя
HTTP filter или API surface.
Refresh rotation использует compare-and-swap
После проверки текущей Session сервис формирует новую token pair и обновляет
Session через updateMany с predicate { id, refreshTokenHash: oldHash }.
count === 1 означает победителя и разрешает вернуть новую пару. count === 0 означает superseded token: сервис очищает cookie через существующий
controller path и отвечает 401 UNAUTHORIZED, не вызывая revoke Session.
Схема не меняется: current hash уже является достаточным CAS predicate.
Альтернативы: transaction добавляет SQLite locking/retry semantics без дополнительной пользы; version column или token history нужны только для отдельного различения late replay и concurrent loser. ADR-009 фиксирует выбранный trade-off.
Tests моделируют arbitration, не подменяют e2e
Новый AuthService spec использует stateful Prisma fake и barrier, чтобы
две регистрации одновременно дошли до create, а два refresh — до CAS.
Тесты проверяют наблюдаемые service outcomes и predicate CAS, включая
отсутствие revoke у loser. BrokerageOperationsService spec использует fake
upstream для проверки canonical hit, ownership checks и max-100 eviction;
TTL expiry обеспечивается и тестируется библиотекой lru-cache, поэтому
change не добавляет test-only clock seam. Это детерминированные regression
tests; они не выдают себя за HTTP/SQLite e2e и не меняют deferred status
WL-P2-05.
Risks / Trade-offs
- [Superseded token может быть поздним replay, а не concurrent loser] →
401не отзывает Session по ADR-009; более строгая дифференциация требует отдельного security change. - [Одна cached page может быть большой] → максимальное число страниц ограничивает cardinality; byte budget остаётся отдельным будущим решением.
- [TTL не создаёт distributed coherence] → cache остаётся process-local, как и текущий local MVP policy.
- [Unit tests не доказывают SQLite migration/HTTP wiring] → они покрывают сервисную логику до recovery trigger для WL-P2-05.
Migration Plan
- Добавить прямую зависимость
lru-cacheи service-level tests. - Внести cache, registration и CAS изменения без Prisma migration.
- Обновить фактические feature docs и ADR-009; сохранить WL-P2-05/P2-13 deferred в remediation registry до их отдельных trigger.
- Выполнить backend non-mutating gates, OpenSpec verification и independent review до archive.
Rollback заключается в откате приложения и dependency lockfile; schema и публичные route shapes не меняются.