- Зачем:
- до сих пор генератор умел собирать день, но не умел его отдать: топик
hits наполнялся пробником, а не настоящими данными. Тикет #41 доводит
события до стенда и закрывает форму на проводе, на которую обопрётся
типизированный ODS (#43).
- сериализатор один по решению спеки: второе место, печатающее событие в
JSON, разошлось бы с первым молча.
- Что:
- serialize.py — канонический сериализатор на orjson: единственное место,
где событие целиком становится JSON; 47 ключей всегда, «пусто» это
пустое значение, даты ISO-8601, ecommerce строкой. Вложенный блок
ecommerce в commerce.py вторым сериализатором не считается — правило
про событие, а не про блок внутри него.
- sinks.py — приёмники: файл (одно событие — одна строка) и Kafka (одно
событие — одно сообщение). Ключа у сообщения нет: WatchID уникален,
ключом он был бы ключом лишь на вид.
- player.py, cli.py — проигрыватель и интерфейс запуска: режимы batch и
live (темп ×60), несколько дней одним запуском, ограниченная пачка,
раздельные тайминги генерации и доставки, лаг в логе.
- день на оси и имя топика умолчаний не имеют: параметр, описывающий
среду или позицию, приходит от зовущего, иначе отказ до генерации.
Умолчания зерна, числа дней и темпа остаются — они описывают мир.
- generator/Dockerfile — свой образ: зависимости из uv.lock, база
закреплена до патча, раскладка репозитория сохранена ради каталога
товаров. Образ Airflow не тронут.
- разовая служба compose под профилем, цели generate-batch и
generate-live, .dockerignore, tmp/ в .gitignore.
- решения внесены в спеку (разделы 4, 8, 9), быстрый старт — в README.
- Проверка:
- make test 406 passed, make lint, make typecheck, make config-test.
- побайтовый детерминизм: два прогона дня в независимых процессах дают
один sha256; день в контейнере совпадает с днём на машине.
- на стенде: пакетный день доехал до stg.hits_raw_dist, счёт по
Distributed сошёлся — отправлено 50626, в таблице 50626.
- топик прочитан обеими нодами: clickhouse-01 раздел 0 (26368),
clickhouse-02 раздел 1 (24258).
- живой день: модельное время 01:00 на 60-й секунде, 02:00 на 120-й —
темп ×60, лаг печатается.
- форма на проводе в колонке raw: даты читаются глазами, ecommerce лежит
строкой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
40 lines
1.4 KiB
Bash
40 lines
1.4 KiB
Bash
COMPOSE_PROJECT_NAME=clickstream-data-platform
|
|
CLICKHOUSE_IMAGE=clickhouse/clickhouse-server:26.3.17.56
|
|
CLICKHOUSE_01_HTTP_PORT=28123
|
|
CLICKHOUSE_01_TCP_PORT=29000
|
|
CLICKHOUSE_02_HTTP_PORT=28124
|
|
CLICKHOUSE_02_TCP_PORT=29001
|
|
KAFKA_IMAGE=apache/kafka:4.3.1
|
|
KAFKA_EXTERNAL_PORT=29092
|
|
|
|
# Разовая служба генератора. Адрес брокера здесь внутренний: команда идёт
|
|
# из сети Compose.
|
|
KAFKA_BOOTSTRAP_SERVERS=kafka:9092
|
|
KAFKA_TOPIC=hits
|
|
|
|
PROMETHEUS_IMAGE=prom/prometheus:v3.13.2
|
|
PROMETHEUS_PORT=29090
|
|
GRAFANA_IMAGE=grafana/grafana:13.1.1
|
|
GRAFANA_PORT=23000
|
|
GRAFANA_ADMIN_USER=admin
|
|
GRAFANA_ADMIN_PASSWORD=admin
|
|
POSTGRES_IMAGE=postgres:16-alpine
|
|
POSTGRES_ADMIN_USER=postgres
|
|
POSTGRES_ADMIN_PASSWORD=postgres-local
|
|
AIRFLOW_METADATA_USER=airflow
|
|
AIRFLOW_METADATA_PASSWORD=airflow-local
|
|
SUPERSET_METADATA_USER=superset
|
|
SUPERSET_METADATA_PASSWORD=superset-local
|
|
AIRFLOW_IMAGE=apache/airflow:3.3.0
|
|
AIRFLOW_PORT=28080
|
|
AIRFLOW_ADMIN_USER=admin
|
|
AIRFLOW_ADMIN_PASSWORD=airflow
|
|
AIRFLOW_JWT_SECRET=local-only-airflow-jwt-secret-do-not-use
|
|
AIRFLOW_API_SECRET_KEY=local-only-airflow-api-secret-do-not-use
|
|
AIRFLOW_FERNET_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
SUPERSET_IMAGE=apache/superset:6.1.0
|
|
SUPERSET_PORT=28088
|
|
SUPERSET_ADMIN_USER=admin
|
|
SUPERSET_ADMIN_PASSWORD=superset
|
|
SUPERSET_SECRET_KEY=local-only-superset-secret-do-not-use-outside-this-stand
|