hyperion/00-work/issues.md
2025-12-28 19:00:03 +03:00

253 lines
No EOL
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

почему в качестве валидации email - регулярка, когда можно использовать общепринятую функцию
tls конфликт
```
sea-orm: runtime-tokio-native-tls
sqlx: runtime-tokio-rustls
```
Причина долгого исполнения тестов: компиляция сервера каждый раз.
```rust
Command::new("cargo")
.args(["r", "--bin", "client"])
```
1. решение: предскомпилировать сервер, и переиспользовать.
2. Распараллелить тесты (может быть сложно)
3. **Проблема**: `DELETE FROM table;`**медленная** операция (проверяет constraints, пишет в WAL). **TRUNCATE** — мгновенно.
```rust
async fn clear_db(db: &DbConn) -> Result<(), DbErr> {
db.execute_unprepared("
TRUNCATE abuse, abuse_reason, admin, \"like\", mail, refreshtoken,
thread, trail, trails_visit_history, \"user\"
RESTART IDENTITY CASCADE;
").await?;
Ok(())
}
```
## 1. **Токио
```toml
tokio = { version = "1,<1.48", features = ["full"] }
```
---
## 2. **SeaORM**
```toml
sea-orm = { version = "=1.1.17", ... }
```
---
## 3. **SQLx + SeaORM**
**Почему это плохо**:
- `sea-orm` использует `sqlx` под капотом, но с другими runtime features (`native-tls` vs `rustls`)
- У вас **два пула соединений** с БД (один от SeaORM, второй от sqlx)
- Миграции на SeaORM, а тесты могут использовать sqlx напрямую → **schema drift** (расхождение схемы)
**Конкретный риск**: В `tests/Cargo.toml` вы подключаете оба. Если напишете тест, который использует `sqlx::query!` и `sea_orm::Entity::insert`, у вас будет **race condition** на уровне транзакций. PostgreSQL не увидит изменения из одного пула в другом до коммита, и ваши тесты будут flaky (нестабильными).
**Техдолг**: Вы платите за **complexity tax** (налог на сложность) дважды: за абстракцию ORM и за прямой доступ.
---
## 4. **Storage Service: Premature abstraction, которая убьет вас**
Ваша абстракция хранилища с `mirror`, `backup`, `single` стратегиями — это **over-engineering без причины**.
**Почему это ошибка**:
- У вас нет SLA на durability (надежность хранения)
- У вас нет метрик на RTO/RPO (Recovery Time/Point Objectives)
- У вас нет **idempotency** в storage operations
**Конкретный баг**: В `mirror.rs` если первый write успешен, а второй — нет, у вас **silent data corruption** (тихая порча данных). У вас нет **transaction log** для отката первого write. Это **eventual consistency without reconciliation** (согласование в конечном счете без сверки).
**Техдолг**: Вы построили **distributed system** там, где был достаточен `std::fs::write`. Через год, когда storage заполнится, вы не поймете, почему `mirror` стратегия оставляет мусор в старых бакетах — потому что нет garbage collection.
---
## 5. **DTO Hell: Вы создаете 3-4 структуры на каждую сущность**
Посмотрите на `domain/src/client/profiles/requests.rs` и `responses.rs`. У вас:
- `CreateUserRequest`
- `UpdateUserRequest`
- `PublicUserResponse`
- `PrivateUserResponse`
- `UserResponse`
**Это не separation of concerns, это duplication of concerns**. Каждое изменение поля `username` — это изменение в 5 файлах и 2 миграциях.
**Что вы потеряли**: **Single source of truth**. У вас нет `User` entity из домена, который бы гарантировал инварианты. Вместо этого вы полагаетесь на валидацию в каждом DTO отдельно.
**Техдолг**: **Schema spread**. Когда добавите поле `last_login_at`, вы забудете обновить один из Response DTO, и клиент получит panicking deserialization.
---
## 6. **Abuse System: Security theater (имитация безопасности)**
У вас есть `CreateAbuseRequest`, `abuse.rs`, `abuse_reason.rs`, но нет **rate limiting** на создание abuse-репортов. Я могу написать скрипт, который заспамит 1M репортов на один trail и **сложит вашу БД**.
**Что не так**:
- Нет `abuse_reports_per_user_per_hour` констрейнта
- Нет `abuse_score` алгоритма (например, если 10 репортов от разных юзеров — автоматический бан)
- Нет `abuse_report_fingerprint` (я могу создавать репорты с разных аккаунтов, но с одного IP)
**Техдолг**: У вас есть **data model для безопасности, но нет enforcement**. Это как замок на двери без стен.
---
## 7. **Transactions: Невидимый кошмар**
В `repository` трейтах у вас методы типа `create_trail`, `toggle_trail_like` — каждый **своя собственная транзакция**.
**Почему это баг**: Если в use-case нужно создать trail, создать thread, поставить лайк — у вас **три отдельные транзакции**. Если последняя упадет, первые две останутся в БД. У вас **inconsistent state** без `ROLLBACK`.
**Где посмотреть**: `api-core` должен принимать `&DatabaseTransaction` и выполнять все операции в ней. Сейчас у вас **autocommit hell** — каждый `repo.insert()` — это `COMMIT`.
**Техдолг**: У вас нет **transactional boundaries** на уровне use-case. Это значит, ваши данные **не консистентны** в границах бизнес-операции.
---
## 8. **API Keys Middleware: Тёмная материя без тестов**
В `api/src/middleware/api_key_middleware.rs` (который вы не показали, но он подключен) — вероятно, нет **integration tests**.
**Что это значит**: Вы не знаете, работает ли rate limiting. Я могу brute-force API keys со скоростью 10k req/s, и ваша middleware может **не выдержать нагрузку**.
**Техдолг**: У вас **нет нагрузочных тестов** на middleware. Когда ваше приложение ляжет от DDoS, вы не поймете, виноват ли `axum`, `tower`, или ваш кастомный код.
---
## 9. **Thread Model: Race condition при append**
`append_trail_to_thread` — это **concurrency nightmare**.
**Сценарий**:
1. User A читает thread (id=1, last_trail_id=5)
2. User B читает thread (id=1, last_trail_id=5)
3. Оба одновременно `append_trail_to_thread` с parent_id=5
4. У вас два trail'а с parent_id=5, но thread не знает, кто "победил"
У вас нет **optimistic locking** (`version` поле) или `SELECT FOR UPDATE`. Это **lost update problem** на уровне приложения.
**Техдолг**: У вас **нет SERIALIZABLE isolation** для критичных операций. Ваши треды могут разветвляться в невалидные структуры.
---
## 10. **Observability: Вы слепы в production**
У вас есть `tracing`, но нет:
- **Metrics** (`prometheus`, `metrics-rs`)
- **Structured logging** (JSON logs для ELK/Loki)
- **Distributed tracing** (OpenTelemetry для Jaeger/Tempo)
**Что это значит**: Когда в продакшене упадет запрос, вы увидите:
```
INFO: request started
INFO: request failed
```
Но не увидите: `user_id`, `trace_id`, `duration_ms`, `db_query_count`. Вы **не сможете отладить performance regression** после вашего PR.
**Техдолг**: У вас **нет production-grade observability**. Это значит, вы **не сможете понять, что сломали**, когда добавите ML-фичу.
---
## 11. **Release Profile: Thin LTO — это медленный production**
```toml
[profile.release]
lto = "thin"
```
Для вашего уровня performance-чувствительности (RTX 3050, 24GB RAM) это **сознательный выбор скорости компиляции в ущерб runtime**.
**Что это значит**: Ваш бинарник будет на 15-30% медленнее, чем мог бы быть. Для "тропиночного" приложения это не критично, но если вы планируете **batch ML inference** на GPU, вы **теряете performance** из-за неинлайненных вызовов.
**Техдолг**: У вас **нет performance budget**. Вы не знаете, сколько RPS может выдержать ваш сервер, потому что не тестировали под нагрузкой с `lto = "fat"`.
---
## 12. **Migrations: Append-only log без checksums**
Ваш `Migrator` — это **poor man's Flyway**. Но нет:
- **Checksum** каждой миграции (если кто-то изменит старую миграцию, вы не узнаете)
- **Baseline** миграции (невозможно подключиться к существующей БД)
- **Repair** mode (если миграция упала на production)
**Что это значит**: Если ваш DevOps-engineer случайно отредактирует `m20241005_112021_create_like.rs` на проде-сервере, `cargo run --bin migrate` **не заметит изменения** и накатит кривую схему.
**Техдолг**: У вас **нет migration integrity checks**. Это значит, схема БД — **mutable history**, а не immutable log.
---
## 13. **Dependencies: 150+ crate'ов, и вы не знаете, зачем**
Запустите `cargo tree | wc -l`. У вас больше 150 зависимостей для CRUD приложения.
**Конкретные лишние**:
- `rust-argon2` **и** `bcrypt` — два password hashing алгоритма. Выбери один.
- `lazy_static` **и** `once_cell` — дублирование (once_cell в std с 1.70)
- `utoipa` **и** `utoipa-swagger-ui` — генерируете OpenAPI, но нет тестов, что спецификация валидна
- `opendal` — вы используете только `memory` и `fs`, но это 50+ transitive dependencies
**Техдолг**: У вас **dependency bloat**. Каждый `cargo build` тянет мир, а security audit (cargo audit) будет плакать каждую неделю.
---
## 14. **Test Coverage: Hurl — это не enough**
У вас 50+ hurl файлов, но **нет unit тестов** на `api-core`. Это значит, вы тестируете **только happy path** через HTTP.
**Что вы не тестируете**:
- **Error conditions**: Что если `TrailRepository::create` вернет `DbErr::ConnectionAcquire`?
- **Edge cases**: Что если `user_id` отрицательный? (SeaORM принимает i64, но должна валидировать)
- **Concurrency**: Что если 1000 потоков вызывают `toggle_trail_like` одновременно?
**Техдолг**: У вас **нет property-based testing** (proptest) и **нет fuzzing** (cargo-fuzz). Вы **не нашли race conditions и integer overflow bugs**, которые 100% есть в коде.
---
## 15. **DTO Validation: Validate на уровне HTTP, а не домена**
Ваша валидация в `domain` DTO — это **security theater**. Почему?
```rust
#[derive(Validate)]
pub struct CreateTrailRequest {
#[validate(length(min = 3, max = 200))]
pub title: String,
}
```
Эта валидация **не вызывается автоматически**. Она вызывается в **extractor**. Если кто-то создаст `Trail` напрямую через `api-core`, минуя HTTP — валидация **пропущена**.
**Правильно**: Валидация должна быть в `NewTrail` **domain entity**, и она должна быть **infallible constructor** (конструктор, который не позволит создать невалидный объект).
**Техдолг**: У вас **type safety holes**. Невалидные данные могут просочиться в БД через прямой вызов api-core.
---
## Финальная правда: Это хороший прототип, но не production system
Ваш проект — **MVP с правильной архитектурной формой, но без production-grade деталей**. Он выживет до первого серьезного инцидента:
- **До первого DDoS** — упадет, потому что нет rate limiting
- **До первого data corruption** — потеряете данные, потому что нет транзакций
- **До первого security breach** — скомпрометируете API keys, потому что нет audit log
- **До первого performance regression** — не поймете, что сломали, потому что нет метрик
**Что делать сейчас** (порядок приоритета):
1. **Удалите `sqlx` из workspace** — используйте только SeaORM
2. **Добавьте `TransactionTrait`** во все use-кейсы в `api-core`
3. **Напишите 10 unit тестов** на `TrailService` с `MockTrailRepository` — вы поймете, где у вас **logic gaps**
4. **Запустите `cargo audit`** и пофиксите critical уязвимости (их будет 5-10)
5. **Добавьте один rate limiter** в `api_key_middleware` — поймете, насколько ваш design **fragile**
Это не "у вас всё плохо". Это "у вас всё **правильно начато, но не закончено**".