feat(generator): добавлены профили запуска
- Зачем: - запуск генератора должен быть понятным перед будущим DAG-пультом. - Что: - добавлены глаголы запуска backfill, continue и reset. - добавлены профили ci и daily-wave с расчётом длительности истории. - обновлены runbook и документы запуска под профильный интерфейс. - Проверка: - uv run --with-requirements generator/requirements.txt pytest generator/tests -q. - bash -n scripts/run_generator.sh scripts/export_startup_history_artifact.sh scripts/import_startup_history_artifact.sh scripts/run_generated_history_analytics.sh. - PROFILE=daily-wave COMPOSE_BIN=true bash scripts/run_generator.sh backfill.
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
Status: ready-for-agent
|
||||
Status: done
|
||||
|
||||
# Глаголы, длительность и профили запуска генератора
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
reload-monitoring recover-monitoring \
|
||||
superset-init superset-dashboard superset-ui superset-restart \
|
||||
generator-up generator-down generator-logs generator-restart \
|
||||
generator-backfill generator-continue generator-reset \
|
||||
generator-test generator-test-build generator-test-cov
|
||||
|
||||
COMPOSE ?= docker compose
|
||||
@@ -130,6 +131,18 @@ generator-logs:
|
||||
generator-restart:
|
||||
$(COMPOSE) up -d --build --force-recreate generator
|
||||
|
||||
# Промотать стартовую историю через профиль запуска
|
||||
generator-backfill:
|
||||
COMPOSE_BIN="$(COMPOSE)" bash ./scripts/run_generator.sh backfill
|
||||
|
||||
# Продолжить live-поток из существующего state
|
||||
generator-continue:
|
||||
COMPOSE_BIN="$(COMPOSE)" bash ./scripts/run_generator.sh continue
|
||||
|
||||
# Запустить live-поток как новый мир
|
||||
generator-reset:
|
||||
COMPOSE_BIN="$(COMPOSE)" bash ./scripts/run_generator.sh reset
|
||||
|
||||
# Собрать тестовый образ генератора
|
||||
generator-test-build:
|
||||
cd generator && docker build -t generator:test .
|
||||
|
||||
@@ -37,11 +37,17 @@ make generated-history-analytics
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
По умолчанию это быстрый проверочный профиль на 6 часов модельного времени.
|
||||
Суточную историю можно прогнать отдельно:
|
||||
По умолчанию это быстрый профиль `ci`: 6 часов модельного времени. Историю на
|
||||
2 суток с суточной волной можно прогнать одной командой:
|
||||
|
||||
```bash
|
||||
GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 make generated-history-analytics
|
||||
PROFILE=daily-wave make generated-history-analytics
|
||||
```
|
||||
|
||||
Разовую длительность можно задать без ручного расчёта правой границы:
|
||||
|
||||
```bash
|
||||
GEN_HISTORY_DURATION=2d make generated-history-analytics
|
||||
```
|
||||
|
||||
Повторить только техническую проверку после уже выполненного прогона:
|
||||
|
||||
@@ -307,6 +307,7 @@ services:
|
||||
GEN_MODEL_TIMEZONE: ${GEN_MODEL_TIMEZONE:-UTC}
|
||||
GEN_MODEL_TIME_SPEED: ${GEN_MODEL_TIME_SPEED:-1}
|
||||
GEN_RUN_MODE: ${GEN_RUN_MODE:-live}
|
||||
GEN_LAUNCH_PROFILE: ${GEN_LAUNCH_PROFILE:-ci}
|
||||
GEN_DATA_DIR: /data
|
||||
GEN_SEED: ${GEN_SEED:-}
|
||||
GEN_ENABLED: ${GEN_ENABLED:-true}
|
||||
|
||||
+24
-45
@@ -118,6 +118,7 @@ make generator-logs
|
||||
| `GEN_MODEL_TIMEZONE` | Часовой пояс модельных часов для дневного коэффициента | `UTC` |
|
||||
| `GEN_MODEL_TIME_SPEED` | Сколько модельных секунд проходит за одну настенную секунду | `1` |
|
||||
| `GEN_RUN_MODE` | Режим генератора | `live` |
|
||||
| `GEN_LAUNCH_PROFILE` | Имя профиля запуска для логов | `ci` |
|
||||
| `GEN_STARTUP_HISTORY_ARTIFACT` | JSON-файл для экспорта стартовой истории в режиме `backfill` | пусто |
|
||||
| `GEN_STATE_ENABLED` | Сохранять state v2 между рестартами | `true` |
|
||||
| `GEN_STATE_RESET` | Сбросить state при старте | `false` |
|
||||
@@ -141,20 +142,25 @@ make generated-history-analytics
|
||||
```
|
||||
|
||||
Она выполняет полный сброс volumes, поднимает ClickHouse и Kafka, применяет DDL,
|
||||
запускает `GEN_RUN_MODE=backfill`, прогоняет batch STG -> ODS -> DDS -> DM,
|
||||
инициализирует Superset и запускает техническую проверку. Для координатора или CI
|
||||
короткая повторная проверка после уже готового стенда:
|
||||
выполняет глагол `backfill`, прогоняет batch STG -> ODS -> DDS -> DM,
|
||||
инициализирует Superset и запускает техническую проверку. Для координатора или
|
||||
CI короткая повторная проверка после уже готового стенда:
|
||||
|
||||
```bash
|
||||
make generated-history-check
|
||||
```
|
||||
|
||||
По умолчанию команда использует быстрый проверочный профиль: 6 часов модельного
|
||||
времени (`GEN_MODEL_T_END=2026-01-01T06:00:00+00:00`). Суточную историю можно
|
||||
прогнать отдельно, явно задав правую границу:
|
||||
По умолчанию команда использует быстрый профиль `ci`: 6 часов модельного
|
||||
времени. Историю на 2 суток с суточной волной можно получить одной командой:
|
||||
|
||||
```bash
|
||||
GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 make generated-history-analytics
|
||||
PROFILE=daily-wave make generated-history-analytics
|
||||
```
|
||||
|
||||
Разовую длительность можно задать без ручного расчёта `GEN_MODEL_T_END`:
|
||||
|
||||
```bash
|
||||
GEN_HISTORY_DURATION=2d make generated-history-analytics
|
||||
```
|
||||
|
||||
`GEN_RUN_MODE=backfill` быстро проматывает модельное прошлое от `GEN_MODEL_T0`
|
||||
@@ -171,30 +177,16 @@ GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 make generated-history-analytics
|
||||
Kafka-топики данных, state и manifest генератора. `make generated-history-analytics`
|
||||
делает это по умолчанию (`CLEAN_START=1`).
|
||||
|
||||
Ручной backfill без всего аналитического контура:
|
||||
|
||||
```bash
|
||||
make clean
|
||||
docker compose up -d clickhouse kafka
|
||||
make ddl
|
||||
|
||||
GEN_RUN_MODE=backfill \
|
||||
GEN_STATE_RESET=true \
|
||||
GEN_SEED=4242 \
|
||||
GEN_MODEL_T0=2026-01-01T00:00:00+00:00 \
|
||||
GEN_MODEL_T_END=2026-01-01T06:00:00+00:00 \
|
||||
GEN_MODEL_TIMEZONE=UTC \
|
||||
GEN_MODEL_TIME_SPEED=1 \
|
||||
GEN_TICK_SECONDS=60 \
|
||||
GEN_LAMBDA_BASE_PER_MIN=60 \
|
||||
GEN_JITTER_PCT=0 \
|
||||
docker compose run --rm --no-deps generator
|
||||
|
||||
# Materialized View слоя STG читает Kafka сама; даём ей коротко догнать.
|
||||
sleep 10
|
||||
bash scripts/run_batch.sh
|
||||
PROFILE=daily-wave make generator-backfill
|
||||
```
|
||||
|
||||
Ручной сценарий выше нужен для отладки. В обычной проверке используйте
|
||||
`make generated-history-analytics`, чтобы не забыть Superset и итоговый check.
|
||||
Старый способ через `GEN_RUN_MODE`, `GEN_STATE_RESET` и `GEN_MODEL_T_END`
|
||||
остаётся низкоуровневым путём для отладки прямого `docker compose run`. В обычной
|
||||
проверке используйте `make generated-history-analytics`, чтобы не забыть
|
||||
Superset и итоговый check.
|
||||
|
||||
Manifest можно посмотреть так:
|
||||
|
||||
@@ -207,27 +199,14 @@ docker compose exec -T kafka /opt/kafka/bin/kafka-console-consumer.sh \
|
||||
--timeout-ms 5000
|
||||
```
|
||||
|
||||
Live-продолжение стартует с этого state. Используйте те же `GEN_SEED`, `T0`,
|
||||
`T_end`, часовой пояс и настройки генерации. `GEN_STATE_RESET=false` важен: иначе
|
||||
слепок стартовой истории будет проигнорирован.
|
||||
Live-продолжение стартует с этого state. Используйте тот же профиль или ту же
|
||||
длительность, что были у backfill. Команда сама выставит `GEN_STATE_RESET=false`.
|
||||
|
||||
```bash
|
||||
docker compose run -d --name startup-history-live --no-deps \
|
||||
-e GEN_RUN_MODE=live \
|
||||
-e GEN_STATE_RESET=false \
|
||||
-e GEN_SEED=4242 \
|
||||
-e GEN_MODEL_T0=2026-01-01T00:00:00+00:00 \
|
||||
-e GEN_MODEL_T_END=2026-01-01T06:00:00+00:00 \
|
||||
-e GEN_MODEL_TIMEZONE=UTC \
|
||||
-e GEN_MODEL_TIME_SPEED=1 \
|
||||
-e GEN_TICK_SECONDS=60 \
|
||||
-e GEN_LAMBDA_BASE_PER_MIN=60 \
|
||||
-e GEN_JITTER_PCT=0 \
|
||||
generator
|
||||
PROFILE=daily-wave make generator-continue
|
||||
|
||||
sleep 130
|
||||
docker stop startup-history-live
|
||||
docker rm startup-history-live
|
||||
make generator-down
|
||||
sleep 10
|
||||
bash scripts/run_batch.sh
|
||||
```
|
||||
|
||||
@@ -16,12 +16,24 @@
|
||||
|
||||
## Что требует нового артефакта
|
||||
|
||||
- Другая длительность истории (`GEN_MODEL_T_END`).
|
||||
- Другая длительность истории или другой профиль запуска.
|
||||
- Другой `GEN_SEED`, `GEN_MODEL_T0`, часовой пояс, скорость или настройки
|
||||
генерации.
|
||||
- Осознанный новый мир после несовместимого state: сначала сбросьте state через
|
||||
`GEN_STATE_RESET=true` или чистые volumes.
|
||||
|
||||
## Профили запуска
|
||||
|
||||
Основной способ — глагол плюс профиль:
|
||||
|
||||
| Профиль | Для чего | Длительность |
|
||||
|---------|----------|--------------|
|
||||
| `ci` | Быстрая проверка и CI | `6h` |
|
||||
| `daily-wave` | История с видимой суточной волной | `2d` |
|
||||
|
||||
Длительность можно переопределить через `GEN_HISTORY_DURATION`, например `2d`.
|
||||
Команда сама считает `GEN_MODEL_T_END` от `GEN_MODEL_T0`.
|
||||
|
||||
## Экспорт
|
||||
|
||||
По умолчанию создаётся быстрый 6-часовой артефакт:
|
||||
@@ -32,11 +44,11 @@ make startup-history-export
|
||||
|
||||
Файл по умолчанию: `/tmp/clickstream-startup-history.json`.
|
||||
|
||||
Суточная история:
|
||||
История на 2 суток с суточной волной:
|
||||
|
||||
```bash
|
||||
ARTIFACT=/tmp/clickstream-startup-history-1d.json \
|
||||
GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 \
|
||||
ARTIFACT=/tmp/clickstream-startup-history-2d.json \
|
||||
PROFILE=daily-wave \
|
||||
make startup-history-export
|
||||
```
|
||||
|
||||
@@ -62,6 +74,13 @@ make generated-history-check
|
||||
compact-топики в Kafka. ClickHouse читает данные через свои Kafka-таблицы и
|
||||
Materialized View, затем batch строит ODS, DDS и DM.
|
||||
|
||||
Если артефакт создан не профилем `ci`, импорт запускайте с тем же профилем:
|
||||
|
||||
```bash
|
||||
PROFILE=daily-wave ARTIFACT=/tmp/clickstream-startup-history-2d.json \
|
||||
make startup-history-import
|
||||
```
|
||||
|
||||
`make startup-history-check` сверяет контрольные числа DM-витрины с manifest
|
||||
артефакта: события, визиты, пользователей и диапазон `event_timestamp`. Если
|
||||
data-топики Kafka уже непустые, импорт остановится до публикации событий.
|
||||
@@ -74,18 +93,15 @@ ClickHouse уже успел прочитать частичные сообще
|
||||
|
||||
## Live-продолжение
|
||||
|
||||
После импорта запускайте live с теми же настройками, что были в артефакте:
|
||||
После импорта запускайте live с тем же профилем, что был в артефакте:
|
||||
|
||||
```bash
|
||||
GEN_STATE_RESET=false \
|
||||
GEN_SEED=4242 \
|
||||
GEN_MODEL_T0=2026-01-01T00:00:00+00:00 \
|
||||
GEN_MODEL_T_END=2026-01-01T06:00:00+00:00 \
|
||||
GEN_MODEL_TIMEZONE=UTC \
|
||||
GEN_MODEL_TIME_SPEED=1 \
|
||||
docker compose up -d generator
|
||||
PROFILE=ci make generator-continue
|
||||
```
|
||||
|
||||
Если читаемый state есть, но настройки не совпадают, генератор падает с перечнем
|
||||
полей. Это защита от смешения разных миров. Для намеренного нового мира
|
||||
используйте `GEN_STATE_RESET=true` или `make clean`.
|
||||
используйте `make generator-reset` или `make clean`.
|
||||
|
||||
Старые переменные `GEN_RUN_MODE`, `GEN_STATE_RESET` и `GEN_MODEL_T_END` остаются
|
||||
низкоуровневым способом для отладки и прямого `docker compose run`.
|
||||
|
||||
@@ -90,12 +90,25 @@ ADR-0005 решил отвязать время генератора от реа
|
||||
одну настенную секунду. Формат: положительное число, по умолчанию `1`.
|
||||
- `GEN_RUN_MODE` — режим запуска: `live` или `backfill`. Значение по умолчанию —
|
||||
`live`.
|
||||
- `GEN_LAUNCH_PROFILE` — имя профиля запуска для логов. Значение по умолчанию —
|
||||
`ci`; на поток не влияет.
|
||||
- `GEN_STARTUP_HISTORY_ARTIFACT` — путь к JSON-файлу, куда `backfill` дополнительно
|
||||
пишет портативный артефакт стартовой истории. В обычном live-запуске не нужен.
|
||||
- `GEN_SEED`, `GEN_TICK_SECONDS` и остальные настройки генерации остаются частью
|
||||
контракта повторяемости. Если они отличаются, артефакт стартовой истории
|
||||
считается другим.
|
||||
|
||||
Поверх этих переменных есть пользовательский слой запуска:
|
||||
|
||||
- `backfill` — подставляет `GEN_RUN_MODE=backfill` и `GEN_STATE_RESET=true`;
|
||||
- `continue` — подставляет `GEN_RUN_MODE=live` и `GEN_STATE_RESET=false`;
|
||||
- `reset` — подставляет `GEN_RUN_MODE=live` и `GEN_STATE_RESET=true`.
|
||||
|
||||
Профиль `ci` даёт быстрый 6-часовой прогон. Профиль `daily-wave` даёт 2 суток,
|
||||
чтобы была видна суточная волна. `GEN_HISTORY_DURATION` задаёт длительность
|
||||
вида `6h` или `2d`; `GEN_MODEL_T_END` считается от `GEN_MODEL_T0` внутри слоя
|
||||
запуска. Старые переменные остаются низкоуровневым механизмом.
|
||||
|
||||
#### Ход часов
|
||||
|
||||
В живом режиме модельное время идёт фиксированным шагом. На чистом старте оно
|
||||
|
||||
+25
-6
@@ -78,6 +78,7 @@ generator-service -> Kafka topics -> (потребители отдельно)
|
||||
| `GEN_MODEL_TIMEZONE` | Часовой пояс модельных часов для дневного коэффициента | `UTC` |
|
||||
| `GEN_MODEL_TIME_SPEED` | Сколько модельных секунд проходит за одну настенную секунду | `1` |
|
||||
| `GEN_RUN_MODE` | Режим генератора | `live` |
|
||||
| `GEN_LAUNCH_PROFILE` | Имя профиля запуска для логов | `ci` |
|
||||
| `GEN_STARTUP_HISTORY_ARTIFACT` | JSON-файл для экспорта стартовой истории в режиме `backfill` | — |
|
||||
| `GEN_DATA_DIR` | Путь к JSONL файлам | `/data` |
|
||||
| `GEN_SEED` | Сид для воспроизводимости | — |
|
||||
@@ -125,12 +126,17 @@ make generated-history-analytics
|
||||
проверяет Superset metadata. Файлы `data/*.jsonl` при этом не грузятся в Kafka:
|
||||
они пока используются только как фактура для генератора.
|
||||
|
||||
По умолчанию используется быстрый проверочный профиль на 6 часов модельного
|
||||
времени (`GEN_MODEL_T_END=2026-01-01T06:00:00+00:00`). Суточный прогон доступен
|
||||
явно:
|
||||
По умолчанию используется быстрый профиль `ci`: 6 часов модельного времени.
|
||||
Профиль `daily-wave` даёт 2 суток, чтобы была видна суточная волна:
|
||||
|
||||
```bash
|
||||
GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 make generated-history-analytics
|
||||
PROFILE=daily-wave make generated-history-analytics
|
||||
```
|
||||
|
||||
Разовую длительность можно задать без ручного расчёта `GEN_MODEL_T_END`:
|
||||
|
||||
```bash
|
||||
GEN_HISTORY_DURATION=2d make generated-history-analytics
|
||||
```
|
||||
|
||||
### Режим "раз в минуту" (для демо)
|
||||
@@ -162,13 +168,26 @@ make generator-logs
|
||||
# Перезапуск с пересборкой
|
||||
make generator-restart
|
||||
|
||||
# Запуск тестов
|
||||
make generator-test
|
||||
# Промотать стартовую историю
|
||||
make generator-backfill
|
||||
|
||||
# Продолжить live-поток из state
|
||||
make generator-continue
|
||||
|
||||
# Начать live-поток как новый мир
|
||||
make generator-reset
|
||||
|
||||
# Чистый аналитический прогон всего стенда
|
||||
make generated-history-analytics
|
||||
|
||||
# Запуск тестов
|
||||
make generator-test
|
||||
```
|
||||
|
||||
Основной ручной путь запуска — глаголы `generator-backfill`, `generator-continue`
|
||||
и `generator-reset`. Старые `GEN_RUN_MODE`, `GEN_STATE_RESET` и
|
||||
`GEN_MODEL_T_END` остаются низкоуровневым способом для отладки.
|
||||
|
||||
## Метрики Prometheus
|
||||
|
||||
Генератор экспортирует метрики на `:9109/metrics`:
|
||||
|
||||
@@ -97,6 +97,9 @@ class Config:
|
||||
if os.getenv("GEN_STARTUP_HISTORY_ARTIFACT")
|
||||
else None
|
||||
)
|
||||
launch_profile: str = field(
|
||||
default_factory=lambda: os.getenv("GEN_LAUNCH_PROFILE", "ci")
|
||||
)
|
||||
|
||||
def __post_init__(self):
|
||||
if self.tick_seconds < 1:
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
"""Глаголы и профили запуска генератора."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
|
||||
LAUNCH_ENV_KEYS = (
|
||||
"GEN_SEED",
|
||||
"GEN_MODEL_T0",
|
||||
"GEN_MODEL_TIMEZONE",
|
||||
"GEN_MODEL_TIME_SPEED",
|
||||
"GEN_TICK_SECONDS",
|
||||
"GEN_LAMBDA_BASE_PER_MIN",
|
||||
"GEN_JITTER_PCT",
|
||||
"GEN_MIN_EVENTS_PER_TICK",
|
||||
"GEN_MAX_EVENTS_PER_TICK",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LaunchProfile:
|
||||
"""Именованный набор настроек запуска."""
|
||||
|
||||
duration: str
|
||||
env: dict[str, str]
|
||||
|
||||
|
||||
PROFILES = {
|
||||
"ci": LaunchProfile(
|
||||
duration="6h",
|
||||
env={
|
||||
"GEN_SEED": "4242",
|
||||
"GEN_MODEL_T0": "2026-01-01T00:00:00+00:00",
|
||||
"GEN_MODEL_TIMEZONE": "UTC",
|
||||
"GEN_MODEL_TIME_SPEED": "1",
|
||||
"GEN_TICK_SECONDS": "60",
|
||||
"GEN_LAMBDA_BASE_PER_MIN": "60",
|
||||
"GEN_JITTER_PCT": "0",
|
||||
"GEN_MIN_EVENTS_PER_TICK": "1",
|
||||
"GEN_MAX_EVENTS_PER_TICK": "1000",
|
||||
},
|
||||
),
|
||||
"daily-wave": LaunchProfile(
|
||||
duration="2d",
|
||||
env={
|
||||
"GEN_SEED": "4242",
|
||||
"GEN_MODEL_T0": "2026-01-01T00:00:00+00:00",
|
||||
"GEN_MODEL_TIMEZONE": "UTC",
|
||||
"GEN_MODEL_TIME_SPEED": "1",
|
||||
"GEN_TICK_SECONDS": "60",
|
||||
"GEN_LAMBDA_BASE_PER_MIN": "60",
|
||||
"GEN_JITTER_PCT": "0",
|
||||
"GEN_MIN_EVENTS_PER_TICK": "1",
|
||||
"GEN_MAX_EVENTS_PER_TICK": "1000",
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def parse_duration(value: str) -> timedelta:
|
||||
"""Разбирает короткую длительность: 30m, 6h, 2d."""
|
||||
if len(value) < 2:
|
||||
raise ValueError("Duration must look like 6h or 2d")
|
||||
amount_text = value[:-1]
|
||||
unit = value[-1]
|
||||
if not amount_text.isdigit():
|
||||
raise ValueError("Duration amount must be a positive integer")
|
||||
amount = int(amount_text)
|
||||
if amount <= 0:
|
||||
raise ValueError("Duration amount must be > 0")
|
||||
if unit == "s":
|
||||
return timedelta(seconds=amount)
|
||||
if unit == "m":
|
||||
return timedelta(minutes=amount)
|
||||
if unit == "h":
|
||||
return timedelta(hours=amount)
|
||||
if unit == "d":
|
||||
return timedelta(days=amount)
|
||||
raise ValueError("Duration unit must be one of: s, m, h, d")
|
||||
|
||||
|
||||
def build_launch_env(
|
||||
verb: str,
|
||||
*,
|
||||
profile_name: str = "ci",
|
||||
duration: str | None = None,
|
||||
overrides: dict[str, str] | None = None,
|
||||
) -> dict[str, str]:
|
||||
"""Возвращает старые env-переменные для нового глагола запуска."""
|
||||
if verb not in {"backfill", "continue", "reset"}:
|
||||
raise ValueError("verb must be backfill, continue or reset")
|
||||
if profile_name not in PROFILES:
|
||||
known = ", ".join(sorted(PROFILES))
|
||||
raise ValueError(f"Unknown launch profile {profile_name!r}; known: {known}")
|
||||
|
||||
profile = PROFILES[profile_name]
|
||||
overrides = overrides or {}
|
||||
env = dict(profile.env)
|
||||
for key in LAUNCH_ENV_KEYS:
|
||||
if key in overrides and overrides[key] != "":
|
||||
env[key] = overrides[key]
|
||||
|
||||
selected_duration = (
|
||||
duration
|
||||
or overrides.get("GEN_HISTORY_DURATION")
|
||||
or profile.duration
|
||||
)
|
||||
env["GEN_LAUNCH_VERB"] = verb
|
||||
env["GEN_LAUNCH_PROFILE"] = profile_name
|
||||
|
||||
if verb == "backfill":
|
||||
env["GEN_RUN_MODE"] = "backfill"
|
||||
env["GEN_STATE_RESET"] = "true"
|
||||
env["GEN_HISTORY_DURATION"] = selected_duration
|
||||
env["GEN_MODEL_T_END"] = _model_t_end(env["GEN_MODEL_T0"], selected_duration)
|
||||
elif verb == "continue":
|
||||
env["GEN_RUN_MODE"] = "live"
|
||||
env["GEN_STATE_RESET"] = "false"
|
||||
env["GEN_HISTORY_DURATION"] = selected_duration
|
||||
env["GEN_MODEL_T_END"] = _model_t_end(env["GEN_MODEL_T0"], selected_duration)
|
||||
else:
|
||||
env["GEN_RUN_MODE"] = "live"
|
||||
env["GEN_STATE_RESET"] = "true"
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def shell_assignments(env: dict[str, str]) -> str:
|
||||
"""Печатает env в виде, пригодном для eval в Bash."""
|
||||
return "\n".join(
|
||||
f"export {key}={shlex.quote(value)}"
|
||||
for key, value in sorted(env.items())
|
||||
)
|
||||
|
||||
|
||||
def _model_t_end(model_t0: str, duration: str) -> str:
|
||||
t0 = datetime.fromisoformat(model_t0.replace("Z", "+00:00"))
|
||||
if t0.tzinfo is None:
|
||||
raise ValueError("GEN_MODEL_T0 must include timezone")
|
||||
return (t0.astimezone(timezone.utc) + parse_duration(duration)).isoformat()
|
||||
|
||||
|
||||
def _parse_args(argv: list[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Глаголы запуска генератора")
|
||||
parser.add_argument("verb", choices=("backfill", "continue", "reset"))
|
||||
parser.add_argument(
|
||||
"--profile",
|
||||
default=os.getenv("PROFILE") or os.getenv("GEN_LAUNCH_PROFILE") or "ci",
|
||||
choices=sorted(PROFILES),
|
||||
)
|
||||
parser.add_argument("--duration", default=os.getenv("GEN_HISTORY_DURATION"))
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
"""CLI для Bash-скриптов запуска."""
|
||||
args = _parse_args(sys.argv[1:] if argv is None else argv)
|
||||
env = build_launch_env(
|
||||
args.verb,
|
||||
profile_name=args.profile,
|
||||
duration=args.duration,
|
||||
overrides=os.environ,
|
||||
)
|
||||
print(shell_assignments(env))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -70,6 +70,7 @@ class GeneratorService:
|
||||
f"Configuration: tick={self.config.tick_seconds}s, "
|
||||
f"lambda_base={self.config.lambda_base_per_min}/min, "
|
||||
f"jitter={self.config.jitter_pct}%, "
|
||||
f"launch_profile={self.config.launch_profile}, "
|
||||
f"state_enabled={self.config.state_enabled}, "
|
||||
f"state_reset={self.config.state_reset}"
|
||||
)
|
||||
|
||||
@@ -299,6 +299,7 @@ def build_manifest(config: Config, counters: ManifestCounters, state: GeneratorS
|
||||
"model_t_end": config.model_t_end.isoformat(),
|
||||
"model_timezone": config.model_timezone,
|
||||
"run_mode": "backfill",
|
||||
"launch_profile": config.launch_profile,
|
||||
"generation_settings": generation_settings_from_config(config),
|
||||
"state_version": state.version,
|
||||
"state": {
|
||||
|
||||
@@ -118,3 +118,21 @@ class TestConfigDefaults:
|
||||
finally:
|
||||
if orig_value is not None:
|
||||
os.environ["GEN_TICK_SECONDS"] = orig_value
|
||||
|
||||
def test_launch_profile_defaults_to_ci(self, monkeypatch, data_dir):
|
||||
"""По умолчанию выбран быстрый профиль запуска ci."""
|
||||
monkeypatch.setenv("GEN_DATA_DIR", str(data_dir))
|
||||
monkeypatch.delenv("GEN_LAUNCH_PROFILE", raising=False)
|
||||
|
||||
config = Config()
|
||||
|
||||
assert config.launch_profile == "ci"
|
||||
|
||||
def test_launch_profile_can_be_read_from_env(self, monkeypatch, data_dir):
|
||||
"""Выбранный профиль запуска попадает в конфигурацию для логов."""
|
||||
monkeypatch.setenv("GEN_DATA_DIR", str(data_dir))
|
||||
monkeypatch.setenv("GEN_LAUNCH_PROFILE", "daily-wave")
|
||||
|
||||
config = Config()
|
||||
|
||||
assert config.launch_profile == "daily-wave"
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
Тесты профилей и глаголов запуска генератора.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
def test_duration_2d_sets_model_t_end_from_t0():
|
||||
"""Длительность 2d считается от T0 без ручного GEN_MODEL_T_END."""
|
||||
from clickstream_generator.launch import build_launch_env
|
||||
|
||||
env = build_launch_env("backfill", profile_name="ci", duration="2d")
|
||||
|
||||
assert env["GEN_MODEL_T0"] == "2026-01-01T00:00:00+00:00"
|
||||
assert env["GEN_MODEL_T_END"] == "2026-01-03T00:00:00+00:00"
|
||||
|
||||
|
||||
def test_daily_wave_profile_uses_two_days_by_default():
|
||||
"""Профиль daily-wave даёт историю с суточной волной."""
|
||||
from clickstream_generator.launch import build_launch_env
|
||||
|
||||
env = build_launch_env("backfill", profile_name="daily-wave")
|
||||
|
||||
assert env["GEN_LAUNCH_PROFILE"] == "daily-wave"
|
||||
assert env["GEN_HISTORY_DURATION"] == "2d"
|
||||
assert env["GEN_MODEL_T_END"] == "2026-01-03T00:00:00+00:00"
|
||||
|
||||
|
||||
def test_backfill_verb_maps_to_existing_low_level_flags():
|
||||
"""Глагол backfill оставляет прежнюю механику запуска под капотом."""
|
||||
from clickstream_generator.launch import build_launch_env
|
||||
|
||||
env = build_launch_env("backfill", profile_name="ci")
|
||||
|
||||
assert env["GEN_RUN_MODE"] == "backfill"
|
||||
assert env["GEN_STATE_RESET"] == "true"
|
||||
|
||||
|
||||
def test_continue_and_reset_verbs_map_to_existing_low_level_flags():
|
||||
"""Глаголы continue и reset задают только старый контракт state."""
|
||||
from clickstream_generator.launch import build_launch_env
|
||||
|
||||
continue_env = build_launch_env("continue", profile_name="ci")
|
||||
reset_env = build_launch_env("reset", profile_name="ci")
|
||||
|
||||
assert continue_env["GEN_RUN_MODE"] == "live"
|
||||
assert continue_env["GEN_STATE_RESET"] == "false"
|
||||
assert continue_env["GEN_MODEL_T_END"] == "2026-01-01T06:00:00+00:00"
|
||||
assert reset_env["GEN_RUN_MODE"] == "live"
|
||||
assert reset_env["GEN_STATE_RESET"] == "true"
|
||||
assert "GEN_MODEL_T_END" not in reset_env
|
||||
|
||||
|
||||
def test_ci_profile_keeps_previous_default_backfill_window():
|
||||
"""CI-профиль сохраняет прежний 6-часовой проверочный запуск."""
|
||||
from clickstream_generator.launch import build_launch_env
|
||||
|
||||
env = build_launch_env("backfill", profile_name="ci")
|
||||
|
||||
t0 = datetime.fromisoformat(env["GEN_MODEL_T0"])
|
||||
t_end = datetime.fromisoformat(env["GEN_MODEL_T_END"])
|
||||
assert t0 == datetime(2026, 1, 1, 0, 0, tzinfo=timezone.utc)
|
||||
assert (t_end - t0).total_seconds() == 6 * 60 * 60
|
||||
assert env["GEN_TICK_SECONDS"] == "60"
|
||||
assert env["GEN_LAMBDA_BASE_PER_MIN"] == "60"
|
||||
assert env["GEN_JITTER_PCT"] == "0"
|
||||
@@ -130,6 +130,43 @@ def test_artifact_roundtrip_keeps_events_state_and_manifest(base_config):
|
||||
path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def test_manifest_and_artifact_show_launch_profile(base_config):
|
||||
"""Manifest и файл артефакта показывают выбранный профиль запуска."""
|
||||
from clickstream_generator.startup_history_artifact import (
|
||||
StartupHistoryArtifactBuilder,
|
||||
build_manifest,
|
||||
load_startup_history_artifact,
|
||||
write_startup_history_artifact,
|
||||
)
|
||||
|
||||
state = _state()
|
||||
builder = StartupHistoryArtifactBuilder()
|
||||
builder.add_batch(_batch())
|
||||
manifest = build_manifest(
|
||||
config=replace(
|
||||
base_config,
|
||||
model_t0=state.model_t0,
|
||||
model_t_end=state.model_timestamp,
|
||||
model_time_speed=1,
|
||||
model_timezone="UTC",
|
||||
seed=42,
|
||||
launch_profile="daily-wave",
|
||||
),
|
||||
counters=builder.counters,
|
||||
state=state,
|
||||
)
|
||||
artifact = builder.to_artifact(manifest=manifest, state=state)
|
||||
|
||||
path = base_config.data_dir.parent / "tmp-startup-history-profile.json"
|
||||
try:
|
||||
write_startup_history_artifact(path, artifact)
|
||||
loaded = load_startup_history_artifact(path)
|
||||
assert manifest["launch_profile"] == "daily-wave"
|
||||
assert loaded["manifest"]["launch_profile"] == "daily-wave"
|
||||
finally:
|
||||
path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def test_artifact_validation_rejects_mismatched_manifest(base_config):
|
||||
"""Валидация отвергает артефакт, где manifest не совпадает с событиями."""
|
||||
from clickstream_generator.startup_history_artifact import (
|
||||
|
||||
@@ -10,26 +10,31 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
COMPOSE_BIN="${COMPOSE_BIN:-docker compose}"
|
||||
CLEAN_START="${CLEAN_START:-1}"
|
||||
ARTIFACT="${ARTIFACT:-/tmp/clickstream-startup-history.json}"
|
||||
|
||||
GEN_SEED="${GEN_SEED:-4242}"
|
||||
GEN_MODEL_T0="${GEN_MODEL_T0:-2026-01-01T00:00:00+00:00}"
|
||||
GEN_MODEL_T_END="${GEN_MODEL_T_END:-2026-01-01T06:00:00+00:00}"
|
||||
GEN_MODEL_TIMEZONE="${GEN_MODEL_TIMEZONE:-UTC}"
|
||||
GEN_MODEL_TIME_SPEED="${GEN_MODEL_TIME_SPEED:-1}"
|
||||
GEN_TICK_SECONDS="${GEN_TICK_SECONDS:-60}"
|
||||
GEN_LAMBDA_BASE_PER_MIN="${GEN_LAMBDA_BASE_PER_MIN:-60}"
|
||||
GEN_JITTER_PCT="${GEN_JITTER_PCT:-0}"
|
||||
GEN_MIN_EVENTS_PER_TICK="${GEN_MIN_EVENTS_PER_TICK:-1}"
|
||||
GEN_MAX_EVENTS_PER_TICK="${GEN_MAX_EVENTS_PER_TICK:-1000}"
|
||||
PROFILE="${PROFILE:-${GEN_LAUNCH_PROFILE:-ci}}"
|
||||
duration_args=()
|
||||
if [[ -n "${GEN_HISTORY_DURATION:-}" ]]; then
|
||||
duration_args=(--duration "${GEN_HISTORY_DURATION}")
|
||||
fi
|
||||
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
launch_env="$(
|
||||
PYTHONPATH="${REPO_ROOT}/generator/src" \
|
||||
uv run python -m clickstream_generator.launch \
|
||||
backfill \
|
||||
--profile "${PROFILE}" \
|
||||
"${duration_args[@]}"
|
||||
)"
|
||||
eval "${launch_env}"
|
||||
|
||||
ARTIFACT_DIR="$(dirname "${ARTIFACT}")"
|
||||
ARTIFACT_FILE="$(basename "${ARTIFACT}")"
|
||||
mkdir -p "${ARTIFACT_DIR}"
|
||||
|
||||
echo "=== Экспорт стартовой истории ==="
|
||||
echo "ARTIFACT=${ARTIFACT}"
|
||||
echo "GEN_LAUNCH_PROFILE=${GEN_LAUNCH_PROFILE}"
|
||||
echo "GEN_HISTORY_DURATION=${GEN_HISTORY_DURATION}"
|
||||
echo "GEN_SEED=${GEN_SEED}"
|
||||
echo "GEN_MODEL_T0=${GEN_MODEL_T0}"
|
||||
echo "GEN_MODEL_T_END=${GEN_MODEL_T_END}"
|
||||
@@ -53,6 +58,7 @@ ${COMPOSE_BIN} run --rm --no-deps \
|
||||
-v "${ARTIFACT_DIR}:/startup-artifacts" \
|
||||
-e GEN_RUN_MODE=backfill \
|
||||
-e GEN_STATE_RESET=true \
|
||||
-e GEN_LAUNCH_PROFILE="${GEN_LAUNCH_PROFILE}" \
|
||||
-e GEN_STARTUP_HISTORY_ARTIFACT="/startup-artifacts/${ARTIFACT_FILE}" \
|
||||
-e GEN_SEED="${GEN_SEED}" \
|
||||
-e GEN_MODEL_T0="${GEN_MODEL_T0}" \
|
||||
|
||||
@@ -9,20 +9,23 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
COMPOSE_BIN="${COMPOSE_BIN:-docker compose}"
|
||||
ARTIFACT="${ARTIFACT:-/tmp/clickstream-startup-history.json}"
|
||||
|
||||
GEN_SEED="${GEN_SEED:-4242}"
|
||||
GEN_MODEL_T0="${GEN_MODEL_T0:-2026-01-01T00:00:00+00:00}"
|
||||
GEN_MODEL_T_END="${GEN_MODEL_T_END:-2026-01-01T06:00:00+00:00}"
|
||||
GEN_MODEL_TIMEZONE="${GEN_MODEL_TIMEZONE:-UTC}"
|
||||
GEN_MODEL_TIME_SPEED="${GEN_MODEL_TIME_SPEED:-1}"
|
||||
GEN_TICK_SECONDS="${GEN_TICK_SECONDS:-60}"
|
||||
GEN_LAMBDA_BASE_PER_MIN="${GEN_LAMBDA_BASE_PER_MIN:-60}"
|
||||
GEN_JITTER_PCT="${GEN_JITTER_PCT:-0}"
|
||||
GEN_MIN_EVENTS_PER_TICK="${GEN_MIN_EVENTS_PER_TICK:-1}"
|
||||
GEN_MAX_EVENTS_PER_TICK="${GEN_MAX_EVENTS_PER_TICK:-1000}"
|
||||
PROFILE="${PROFILE:-${GEN_LAUNCH_PROFILE:-ci}}"
|
||||
duration_args=()
|
||||
if [[ -n "${GEN_HISTORY_DURATION:-}" ]]; then
|
||||
duration_args=(--duration "${GEN_HISTORY_DURATION}")
|
||||
fi
|
||||
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
launch_env="$(
|
||||
PYTHONPATH="${REPO_ROOT}/generator/src" \
|
||||
uv run python -m clickstream_generator.launch \
|
||||
continue \
|
||||
--profile "${PROFILE}" \
|
||||
"${duration_args[@]}"
|
||||
)"
|
||||
eval "${launch_env}"
|
||||
|
||||
if [[ ! -s "${ARTIFACT}" ]]; then
|
||||
echo "Ошибка: артефакт не найден или пуст: ${ARTIFACT}" >&2
|
||||
exit 1
|
||||
@@ -33,6 +36,8 @@ ARTIFACT_FILE="$(basename "${ARTIFACT}")"
|
||||
|
||||
echo "=== Импорт стартовой истории ==="
|
||||
echo "ARTIFACT=${ARTIFACT}"
|
||||
echo "GEN_LAUNCH_PROFILE=${GEN_LAUNCH_PROFILE}"
|
||||
echo "GEN_HISTORY_DURATION=${GEN_HISTORY_DURATION}"
|
||||
echo "GEN_SEED=${GEN_SEED}"
|
||||
echo "GEN_MODEL_T0=${GEN_MODEL_T0}"
|
||||
echo "GEN_MODEL_T_END=${GEN_MODEL_T_END}"
|
||||
@@ -47,6 +52,7 @@ ${COMPOSE_BIN} build generator
|
||||
echo "Шаг 3: воспроизведение артефакта в Kafka"
|
||||
${COMPOSE_BIN} run --rm --no-deps \
|
||||
-v "${ARTIFACT_DIR}:/startup-artifacts:ro" \
|
||||
-e GEN_LAUNCH_PROFILE="${GEN_LAUNCH_PROFILE}" \
|
||||
-e GEN_SEED="${GEN_SEED}" \
|
||||
-e GEN_MODEL_T0="${GEN_MODEL_T0}" \
|
||||
-e GEN_MODEL_T_END="${GEN_MODEL_T_END}" \
|
||||
|
||||
@@ -15,20 +15,23 @@ COMPOSE_BIN="${COMPOSE_BIN:-docker compose}"
|
||||
CLEAN_START="${CLEAN_START:-1}"
|
||||
WAIT_CLICKHOUSE_SECONDS="${WAIT_CLICKHOUSE_SECONDS:-60}"
|
||||
WAIT_STG_SECONDS="${WAIT_STG_SECONDS:-10}"
|
||||
|
||||
GEN_SEED="${GEN_SEED:-4242}"
|
||||
GEN_MODEL_T0="${GEN_MODEL_T0:-2026-01-01T00:00:00+00:00}"
|
||||
GEN_MODEL_T_END="${GEN_MODEL_T_END:-2026-01-01T06:00:00+00:00}"
|
||||
GEN_MODEL_TIMEZONE="${GEN_MODEL_TIMEZONE:-UTC}"
|
||||
GEN_MODEL_TIME_SPEED="${GEN_MODEL_TIME_SPEED:-1}"
|
||||
GEN_TICK_SECONDS="${GEN_TICK_SECONDS:-60}"
|
||||
GEN_LAMBDA_BASE_PER_MIN="${GEN_LAMBDA_BASE_PER_MIN:-60}"
|
||||
GEN_JITTER_PCT="${GEN_JITTER_PCT:-0}"
|
||||
GEN_MIN_EVENTS_PER_TICK="${GEN_MIN_EVENTS_PER_TICK:-1}"
|
||||
GEN_MAX_EVENTS_PER_TICK="${GEN_MAX_EVENTS_PER_TICK:-1000}"
|
||||
PROFILE="${PROFILE:-${GEN_LAUNCH_PROFILE:-ci}}"
|
||||
duration_args=()
|
||||
if [[ -n "${GEN_HISTORY_DURATION:-}" ]]; then
|
||||
duration_args=(--duration "${GEN_HISTORY_DURATION}")
|
||||
fi
|
||||
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
launch_env="$(
|
||||
PYTHONPATH="${REPO_ROOT}/generator/src" \
|
||||
uv run python -m clickstream_generator.launch \
|
||||
backfill \
|
||||
--profile "${PROFILE}" \
|
||||
"${duration_args[@]}"
|
||||
)"
|
||||
eval "${launch_env}"
|
||||
|
||||
wait_for_clickhouse() {
|
||||
local deadline
|
||||
deadline=$((SECONDS + WAIT_CLICKHOUSE_SECONDS))
|
||||
@@ -46,6 +49,8 @@ wait_for_clickhouse() {
|
||||
}
|
||||
|
||||
echo "=== Чистый прогон стартовой истории как источника аналитики ==="
|
||||
echo "GEN_LAUNCH_PROFILE=${GEN_LAUNCH_PROFILE}"
|
||||
echo "GEN_HISTORY_DURATION=${GEN_HISTORY_DURATION}"
|
||||
echo "GEN_SEED=${GEN_SEED}"
|
||||
echo "GEN_MODEL_T0=${GEN_MODEL_T0}"
|
||||
echo "GEN_MODEL_T_END=${GEN_MODEL_T_END}"
|
||||
@@ -73,6 +78,7 @@ echo "Шаг 4: backfill стартовой истории в Kafka"
|
||||
${COMPOSE_BIN} run --rm --no-deps \
|
||||
-e GEN_RUN_MODE=backfill \
|
||||
-e GEN_STATE_RESET=true \
|
||||
-e GEN_LAUNCH_PROFILE="${GEN_LAUNCH_PROFILE}" \
|
||||
-e GEN_SEED="${GEN_SEED}" \
|
||||
-e GEN_MODEL_T0="${GEN_MODEL_T0}" \
|
||||
-e GEN_MODEL_T_END="${GEN_MODEL_T_END}" \
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Запускает генератор через понятные глаголы поверх старых env-переменных.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "Использование: $0 backfill|continue|reset" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
VERB="$1"
|
||||
case "${VERB}" in
|
||||
backfill|continue|reset) ;;
|
||||
*)
|
||||
echo "Ошибка: неизвестный глагол запуска: ${VERB}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
COMPOSE_BIN="${COMPOSE_BIN:-docker compose}"
|
||||
PROFILE="${PROFILE:-${GEN_LAUNCH_PROFILE:-ci}}"
|
||||
duration_args=()
|
||||
if [[ -n "${GEN_HISTORY_DURATION:-}" ]]; then
|
||||
duration_args=(--duration "${GEN_HISTORY_DURATION}")
|
||||
fi
|
||||
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
launch_env="$(
|
||||
PYTHONPATH="${REPO_ROOT}/generator/src" \
|
||||
uv run python -m clickstream_generator.launch \
|
||||
"${VERB}" \
|
||||
--profile "${PROFILE}" \
|
||||
"${duration_args[@]}"
|
||||
)"
|
||||
eval "${launch_env}"
|
||||
|
||||
echo "=== Запуск генератора: ${GEN_LAUNCH_VERB} ==="
|
||||
echo "GEN_LAUNCH_PROFILE=${GEN_LAUNCH_PROFILE}"
|
||||
if [[ -n "${GEN_HISTORY_DURATION:-}" ]]; then
|
||||
echo "GEN_HISTORY_DURATION=${GEN_HISTORY_DURATION}"
|
||||
fi
|
||||
echo "GEN_RUN_MODE=${GEN_RUN_MODE}"
|
||||
echo "GEN_STATE_RESET=${GEN_STATE_RESET}"
|
||||
if [[ -n "${GEN_MODEL_T_END:-}" ]]; then
|
||||
echo "GEN_MODEL_T_END=${GEN_MODEL_T_END}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "${VERB}" == "backfill" ]]; then
|
||||
echo "Шаг 1: запуск Kafka"
|
||||
${COMPOSE_BIN} up -d kafka
|
||||
|
||||
echo "Шаг 2: сборка образа генератора"
|
||||
${COMPOSE_BIN} build generator
|
||||
|
||||
echo "Шаг 3: backfill стартовой истории"
|
||||
${COMPOSE_BIN} run --rm --no-deps \
|
||||
-e GEN_RUN_MODE="${GEN_RUN_MODE}" \
|
||||
-e GEN_STATE_RESET="${GEN_STATE_RESET}" \
|
||||
-e GEN_LAUNCH_PROFILE="${GEN_LAUNCH_PROFILE}" \
|
||||
-e GEN_SEED="${GEN_SEED}" \
|
||||
-e GEN_MODEL_T0="${GEN_MODEL_T0}" \
|
||||
-e GEN_MODEL_T_END="${GEN_MODEL_T_END}" \
|
||||
-e GEN_MODEL_TIMEZONE="${GEN_MODEL_TIMEZONE}" \
|
||||
-e GEN_MODEL_TIME_SPEED="${GEN_MODEL_TIME_SPEED}" \
|
||||
-e GEN_TICK_SECONDS="${GEN_TICK_SECONDS}" \
|
||||
-e GEN_LAMBDA_BASE_PER_MIN="${GEN_LAMBDA_BASE_PER_MIN}" \
|
||||
-e GEN_JITTER_PCT="${GEN_JITTER_PCT}" \
|
||||
-e GEN_MIN_EVENTS_PER_TICK="${GEN_MIN_EVENTS_PER_TICK}" \
|
||||
-e GEN_MAX_EVENTS_PER_TICK="${GEN_MAX_EVENTS_PER_TICK}" \
|
||||
generator
|
||||
else
|
||||
echo "Шаг 1: запуск live-генератора"
|
||||
${COMPOSE_BIN} up -d --build generator
|
||||
fi
|
||||
Reference in New Issue
Block a user