← Все проекты уровня 12
Уровень 12 · Поток данныхВариант B

Посещения сайта

Сбор обезличенных событий посещения и пересчитываемая аналитика скользящих периодов.

Техническое задание

События и приватность

Реализуйте REST-сервис посещений сайтов. Событие содержит site_id, event_id, path, event_time и event_type. Разрешены типы page_view и conversion. Принимаются только поля схемы; неизвестные поля дают validation_error и не сохраняются. IP-адреса, query string, fragment, email, имя, cookie, user agent и произвольные свойства запрещены. Path должен начинаться с одного слеша, иметь не более 512 символов и 16 сегментов; повторные слеши схлопываются, percent-encoding декодируется один раз, управляющие символы отвергаются. Query и fragment удаляются до записи. Время — RFC 3339; event_time не может быть старше 30 дней или опережать сервер более чем на 5 минут. Пакет ограничен 1000 событиями и 1 MiB.

REST-контракт и подсчёт

POST /api/v1/sites/{site_id}/events принимает пакет и возвращает accepted, duplicate или rejected для каждого event_id. Уникальна пара site_id/event_id; точный повтор — duplicate, изменившийся payload — conflict без изменения хранимой строки. GET /api/v1/sites/{site_id}/analytics?path=...&from=...&to=...&bucket=... возвращает число page_view и conversion в UTC-корзинах. Диапазон полуоткрытый, максимум 90 дней; bucket равен hour или day. Уникальные посетители не рассчитываются, поскольку устойчивого идентификатора пользователя нет. path фильтруется по уже нормализованному пути. Неуказанный path означает все пути. Страница ограничена 500 корзинами; большие периоды требуют меньшего bucket или нескольких запросов.

Буферизация и восстановление

Используйте PostgreSQL с ручным SQL и миграциями. Событие подтверждается только после транзакционного сохранения. Исполнитель (worker) обрабатывает партии (batch) по 500 событий и фиксирует агрегат с контрольной позицией в одной транзакции. Очередь ожидающей обработки ограничена 20 000 событий; если заполнена, endpoint возвращает 503 с Retry-After=5 и не подтверждает новые события. После перезапуска исполнитель повторно обрабатывает незавершённую партию по уникальным event ID и контрольной позиции, поэтому счётчик не удваивается. Retention исходных событий — 90 дней; ежедневная задача удаляет более старые строки, агрегаты за завершённые даты сохраняются. Коррекция принятых событий доступна только роли analytics_admin с локальным seed-токеном в Authorization header через POST /api/v1/sites/{site_id}/events/{event_id}/corrections с причиной длиной 10–500 символов; коррекция создаёт аудит и пересчитывает затронутые корзины. Автоматический replay не является коррекцией.

Структура программы

Приём нормализует путь и проверяет приватность; предметная логика считает корзины и коррекции; SQL-хранилище и фоновый агрегатор разделены. main.go только загружает конфигурацию, связывает зависимости и управляет запуском. Пакеты без циклов, общих utils и интерфейсов без потребителя. README показывает зависимости; тесты ядра без HTTP/БД, интеграционные отдельно.

Приёмка

Офлайн-набор содержит повторы после потерянного ответа, query и fragment, запрещённые поля, границы времени, одинаковые пути с разными query, replay партии и коррекцию. Проверки читают БД напрямую и убеждаются, что запрещённых полей там нет. Фиктивные часы задают точные границы. Остановка происходит после сохранения входа и до агрегации, затем приложение запускается снова . Метрики показывают queue depth, возраст старейшего события, rejected, duplicate и время сохранения контрольной позиции. Критерии готовности: replay не увеличивает агрегаты, отказ заполненной очереди не теряет ни одного ранее подтверждённого события, коррекция воспроизводима и аудируется, а лимит 90-дневного retention действует по UTC.

Все запросы используют context.Context и SQL timeout 3 секунды. Возвращайте 400 при ошибке формата и 404 для неизвестного site; внутренние ошибки базы отвечают 500 с безопасным кодом.

Критерии готовности

Ожидаемый результат

Проверяемый результат

  • README показывает границы и направление зависимостей; main.go только связывает конфигурацию и запуск; модульные тесты ядра обходятся без HTTP и БД, интеграционные тесты отделены.
  • API принимает до 1000 событий и сохраняет только site_id, event_id, нормализованный path, времена и разрешённый тип.
  • Повторный пакет не меняет агрегаты; тот же ID с другим содержимым получает конфликт.
  • Аналитика выдаёт page_view и conversion по часу или дню в UTC, без фиктивного подсчёта уникальных людей.
  • Офлайн-тесты подтверждают, что запрещённые поля не попадают в БД, query/fragment отсутствуют, а диапазон корректен.
  • Заполнение 20 000 записей приводит к 503 до приёма новых данных; сохранённые до перезапуска события затем агрегируются один раз.
  • Коррекция требует причину и создаёт аудируемую запись; retention сохраняет агрегаты и удаляет исходные события после 90 дней.
  • README описывает маршруты, приватность, лимиты, восстановление и наблюдаемые метрики.