Подготовить skeleton лабораторной 4 по мессенджеру
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Лабораторная 4. S3-хранилище и вложения
|
||||
|
||||
Разделить метаданные и бинарное содержимое, добавить изображения в сообщения и сохранить доступ к ним при переключении API-реплики.
|
||||
|
||||
Это **skeleton**, а не готовый сервер. Здесь есть задание, контракт и публичные проверки. Реализацию приложения, Dockerfile и требуемую инфраструктуру пишет студент. Язык и framework свободные; UI не обязателен.
|
||||
|
||||
[Карта курса](COURSE.md) · [Процесс fork/PR](CONTRIBUTING.md) · [OpenAPI](contracts/openapi.json) · [Семантика API](contracts/README.md) · [Проверки](tests/README.md) · [Отчёт](REPORT.md) · [Примеры JSON](contracts/examples.json) · [Памятка преподавателю](MAINTAINERS.md)
|
||||
|
||||
## Откуда начинаем
|
||||
|
||||
Перенесите минимум lab3. Дополняются поля сообщения и endpoint вложений; прежние текстовые POST остаются валидными.
|
||||
|
||||
**Архитектура:** Клиент → LB → API × 2+ → PostgreSQL (метаданные) + приватное S3-хранилище (байты).
|
||||
|
||||
## Что сделать
|
||||
|
||||
1. Выберите один S3-совместимый сервер: MinIO, SeaweedFS или RustFS. Закрепите проверенную версию образа и опишите создание приватного bucket.
|
||||
2. Спроектируйте Attachment и связь с чатом/сообщением. S3 object key назначает сервер, filename используется только для отображения.
|
||||
3. Реализуйте синхронный binary upload PNG/JPEG с ограничениями, сохранение метаданных и авторизованный download через API.
|
||||
4. Включите S3 и persistent volumes в общий Compose/Swarm. Проверьте, что другая API-реплика скачивает файл после остановки загрузившей.
|
||||
5. Проверьте недоступность S3, чужой чат и restart всего стенда.
|
||||
|
||||
## Оценка «3»: работающий минимум
|
||||
|
||||
- Все обязательные возможности lab3 сохранены. S3-сервер находится в общем стенде; метаданные вложений живут в PostgreSQL, байты — в приватном bucket, не в БД или локальном каталоге API.
|
||||
- POST вложения возвращает 201 ready после сохранения; оригинал скачивается побайтно. Message с attachment_ids читается участниками чата. Публичный smoke проверяет checksum и отсутствие доступа у постороннего.
|
||||
- Принимаются реальные PNG/JPEG ≤10 MiB и ≤40 млн пикселей; MIME проверяется по содержимому, опасные filename отклоняются, лимиты применяются до неограниченного чтения/декодирования.
|
||||
- Upload → остановка обслужившей API-реплики → download через другую возвращает тот же файл. Restart с сохранением volumes сохраняет метаданные и оригинал.
|
||||
|
||||
## Оценка «4»: уровень стажёра/джуна
|
||||
|
||||
Весь уровень 3 **и все** пункты ниже:
|
||||
|
||||
- Тесты покрывают слишком большой файл, повреждённое изображение, подмену MIME, чужое attachment_id и привязку файла другого чата. Пользовательская ошибка не превращается в 500.
|
||||
- Прерывание загрузки и отказ между записью S3/БД не оставляют вечных неучтённых объектов: есть документированный механизм компенсации/сверки/очистки с безопасным grace period.
|
||||
- Лимиты/таймауты и потоковая обработка не позволяют одной загрузке занять неограниченную RAM. Проведён эксперимент ≥10 одновременных загрузок с измерением пика памяти.
|
||||
- Bucket bootstrap идемпотентен; API использует отдельные S3 credentials с ограниченными правами. Административная консоль не открыта в общей сети. Есть проверка private bucket вне API.
|
||||
|
||||
## Оценка «5»: исследование и доказанный результат
|
||||
|
||||
Весь уровень 4 **и один законченный трек на выбор**. Приведите гипотезу, повторяемую методику, результаты и ограничения.
|
||||
|
||||
**Трек 1. Прямой upload.** Добавьте отдельный безопасный presigned-upload flow: короткая подпись, уникальный ключ, проверка принадлежности, размера/checksum при complete, обработка незавершённой загрузки. Сохраните обязательный proxy endpoint. Сравните трафик и RAM API при ≥2 размерах файлов, покажите негативные сценарии.
|
||||
|
||||
**Трек 2. Согласованность двух хранилищ.** Разработайте повторяемый reconciler для orphan/missing объектов и метаданных. Внедрите ошибки на обеих сторонах, покажите обнаружение и безопасное исправление с dry-run/повторным запуском. Докажите, что свежие и используемые вложения не удаляются; измерьте стоимость обхода.
|
||||
|
||||
## Как начать и проверить
|
||||
|
||||
```sh
|
||||
# Работает прямо в skeleton, Python 3.9+; приложение не запускается:
|
||||
make check
|
||||
|
||||
# После реализации запустите свой сервер/стенд по REPORT.md.
|
||||
# По умолчанию тест использует http://localhost:8080.
|
||||
make test
|
||||
```
|
||||
|
||||
`.env.example`, `compose.example.yaml` и `contracts/infrastructure.json` описывают настройки и роли компонентов. **compose.example.yaml — список ролей с пустым services, не запускаемый стенд.** Реализуйте свой `compose.yaml`/`stack.yaml`, закрепите версии образов, заполните локальный `.env` и опишите bootstrap/миграции. Все зависимости поднимаются из этого репозитория; ссылка «у меня БД уже установлена» не заменяет воспроизводимость.
|
||||
|
||||
```sh
|
||||
# Compose-вариант после реализации:
|
||||
docker compose --env-file .env config --quiet
|
||||
docker compose --env-file .env up -d --build
|
||||
# Дождитесь /health/ready; затем make test.
|
||||
# Для Swarm укажите эквивалентные build/push/deploy-команды в REPORT.md.
|
||||
```
|
||||
|
||||
|
||||
Примеры запросов доступны в `examples/requests.http`, примеры JSON — в `contracts/examples.json`. Для полной проверки схемы (по желанию локально; CI делает её автоматически):
|
||||
|
||||
```sh
|
||||
python3 -m venv .venv
|
||||
.venv/bin/python -m pip install -r requirements-dev.txt
|
||||
make validate PYTHON=.venv/bin/python
|
||||
```
|
||||
|
||||
Smoke не выставляет оценку автоматически. Проверку сохранности/отказов запускайте явно по [tests/README.md](tests/README.md), остальные доказательства приведите в отчёте.
|
||||
|
||||
## Сценарий защиты
|
||||
|
||||
1. Загрузить fixture, отправить message с attachment_ids, скачать как второй участник и сравнить SHA-256.
|
||||
2. С третьим пользователем получить 404 для метаданных и bytes; direct unauthenticated S3 GET не отдаёт объект.
|
||||
3. Выполнить persistence и failover: общий тест начиная с lab4 сохраняет и повторно читает оригинал через API.
|
||||
4. Показать, что на API нет обязательного локального файла оригинала. Для 4/5 — отказы и эксперименты.
|
||||
|
||||
## Что должно быть в PR
|
||||
|
||||
Дополненный стенд, настройка bucket/политики, миграции Attachment, тесты файлов, описание согласованности БД/S3 и результаты.
|
||||
|
||||
Заполните `REPORT.md` и [шаблон PR](.github/pull_request_template.md), укажите целевую оценку/трек. Не меняйте обязательный контракт и публичные tests ради зелёного результата. Сохраняйте возможности предыдущих лабораторных в пределах [объявленных переходов](COURSE.md).
|
||||
|
||||
## Вопросы на защите
|
||||
|
||||
- Почему файл в Docker volume одной API-реплики не равен объектному хранилищу?
|
||||
- Когда можно честно вернуть 201?
|
||||
- Что произойдёт, если S3 PUT прошёл, а COMMIT в PostgreSQL — нет?
|
||||
- Почему случайный object key и приватный bucket не заменяют ACL приложения?
|
||||
|
||||
## Первичные материалы
|
||||
|
||||
- [S3 object uploads](https://docs.aws.amazon.com/AmazonS3/latest/userguide/upload-objects.html)
|
||||
- [Presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
|
||||
- [MinIO](https://github.com/minio/minio)
|
||||
- [SeaweedFS](https://github.com/seaweedfs/seaweedfs)
|
||||
- [RustFS](https://docs.rustfs.com/)
|
||||
Reference in New Issue
Block a user