docs(glossary): четыре термина переименованы, world.md и stand.md вычитаны
- Зачем:
- справочники читает человек со стороны, а «пульт мира», «опись мира»,
«работник» и «сырьё» звучали шуткой или калькой времён разработки
(тикет #214, выбор владельца).
- Что:
- CONTEXT.md: статьи «Паспорт мира», «Даги модельного времени»,
«Шаговый даг», «Сырые данные», старые слова в строках «Избегать»;
пометки в ADR 0005, 0008, 0009 без переписывания решений.
- замены по README, справочникам, путеводителю, лабам, спекам,
комментариям дагов и SQL, тегу дага и панели Grafana; имена в коде
не тронуты.
- world.md и stand.md: 35 находок вычитки внесены, регистр выровнен.
- Проверка:
- make config-test; make lint в корне и в generator/; make test в
generator/ (437 passed); скрипт ссылок из #212 пуст; grep старых
слов вне ADR, research и CONTEXT.md пуст.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QBCtU38qSHg5DvgtnKXcW
This commit is contained in:
1 parent
86ad8afaf2
commit
59d8c83b17
54 files changed
+794
-776
No files matched your search
@@ -46,8 +46,8 @@ HITS_PARTITIONS = (0, 1)
|
||||
READINESS_TIMEOUT_SECONDS = 300
|
||||
READINESS_POLL_SECONDS = 3
|
||||
|
||||
# Позиция на оси модельного времени: номер первого несыгранного дня. Её ставит
|
||||
# пульт мира, и только она отличает прожитый день от живого — зачем это
|
||||
# Позиция на оси модельного времени: номер первого несыгранного дня. Её ставят
|
||||
# даги модельного времени, и только она отличает прожитый день от живого — зачем это
|
||||
# сессиям, разобрано в sql/dds/session_scope.sql.
|
||||
WORLD_POSITION = "world_position"
|
||||
|
||||
@@ -224,8 +224,8 @@ def _wait_for_phase(
|
||||
def _world_position() -> int:
|
||||
"""Позиция мира числом, с внятным отказом на непригодном значении.
|
||||
|
||||
Ту же переменную читает пульт, но общего модуля у дагов нет намеренно:
|
||||
связь между пультом и трансформацией держит сама переменная, а не импорт.
|
||||
Ту же переменную читают даги времени, но общего модуля у дагов нет намеренно:
|
||||
связь между дагами времени и трансформацией держит сама переменная, а не импорт.
|
||||
"""
|
||||
raw_position = Variable.get(WORLD_POSITION, default=None)
|
||||
if raw_position is None:
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ def build_classes_vs_inventory() -> list[str]:
|
||||
for mismatch_class, count in day["orders"].items()
|
||||
}
|
||||
if not expected:
|
||||
raise AirflowException("classes_vs_inventory: в описи нет закрытых дней")
|
||||
raise AirflowException("classes_vs_inventory: в паспорте нет закрытых дней")
|
||||
|
||||
query = (SQL_ROOT / "dq" / "classes_vs_inventory.sql").read_text(encoding="utf-8")
|
||||
# GROUP BY закономерно не возвращает группу из нуля строк.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Приём заказов: пакетный забор слепка из Kafka в хранилище.
|
||||
|
||||
Путь Kafka → STG → ODS у заказов один и живёт одним дагом: сначала забор
|
||||
порции в сырьё, затем разбор того же среза в типизированный ODS и в таблицу
|
||||
порции в `stg`, затем разбор того же среза в типизированный ODS и в таблицу
|
||||
брака. Расписания у дага нет: его дёргает тот, кто положил слепок в топик, —
|
||||
работник пульта мира после проигрыша дня, — и ждёт конца.
|
||||
шаговый даг модельного времени после проигрыша дня, — и ждёт конца.
|
||||
|
||||
Контраст с приёмом событий и есть урок. События тянет матвью, навсегда
|
||||
подписанная на чтеца: приём идёт сам, пока идёт поток. Слепок заказов
|
||||
|
||||
+15
-15
@@ -1,25 +1,25 @@
|
||||
"""Пульт мира: даги, которыми двигают ось модельного времени.
|
||||
"""Даги модельного времени: ими двигают ось мира.
|
||||
|
||||
Их три, и они двух родов. **Работники** играют день и запускаются руками:
|
||||
Их три, и они двух родов. **Шаговые даги** играют день и запускаются руками:
|
||||
`world_next_day` — пачкой, без пауз, сколько дней попросили; `world_live_day` —
|
||||
один день в темпе модельного времени. **Выключатель** `world_live` своей работы
|
||||
не делает: он тикает по расписанию и дёргает работника живого дня, дожидаясь
|
||||
не делает: он тикает по расписанию и дёргает шаговый даг живого дня, дожидаясь
|
||||
конца. Снят с паузы — мир едет день за днём; поставлен на паузу — встал на
|
||||
границе модельных суток.
|
||||
|
||||
Сыграть день — половина работы. Вторая половина: отправить слепок заказов и
|
||||
дождаться, пока хранилище его заберёт. Поэтому у обоих работников за проигрышем
|
||||
дождаться, пока хранилище его заберёт. Поэтому у обоих шаговых дагов за проигрышем
|
||||
идут отправка слепка тем же контейнером генератора и ждущий триггер дага приёма
|
||||
`orders_ingest`.
|
||||
|
||||
Живой день после установки позиции дожидается еще и `etl_pipeline`. Пачка
|
||||
только разгоняет источники: слои после нее менти догоняет ручным запуском.
|
||||
|
||||
Разделение не косметическое. Расписание на самом работнике заставило бы кнопку
|
||||
паузы значить две вещи разом — «мир не едет сам» и «даг выключен», — а работник
|
||||
Разделение не косметическое. Расписание на самом шаговом даге заставило бы кнопку
|
||||
паузы значить две вещи разом — «мир не едет сам» и «даг выключен», — а шаговый даг
|
||||
при этом выглядел бы в списке выключенным, хотя нажимают его каждый день.
|
||||
|
||||
Календарь Airflow к оси мира отношения не имеет: какой день играть, работник
|
||||
Календарь Airflow к оси мира отношения не имеет: какой день играть, шаговый даг
|
||||
спрашивает у переменной, а не у логической даты прогона.
|
||||
|
||||
Решения и доводы целиком — ADR 0009.
|
||||
@@ -51,7 +51,7 @@ ORDERS_TOPIC = os.environ["KAFKA_ORDERS_TOPIC"]
|
||||
STARTING_DAYS = int(os.environ["WORLD_STARTING_DAYS"])
|
||||
|
||||
# Позиция на оси: номер первого несыгранного дня. До конца инициализации
|
||||
# работники не имеют права угадывать её — состояние объяснено в ADR 0013.
|
||||
# шаговые даги не имеют права угадывать её — состояние объяснено в ADR 0013.
|
||||
#
|
||||
# Позиция ставится, а не увеличивается. Наложись один прогон на другой, худшее
|
||||
# при таком правиле — сыгранный дважды день, а повтор схлопнет
|
||||
@@ -63,12 +63,12 @@ WORLD_POSITION = "world_position"
|
||||
# двадцати четырёх минут: тик только спрашивает «не пора ли снова».
|
||||
LIVE_TICK = datetime.timedelta(minutes=25)
|
||||
|
||||
# Какой день играть, работник узнаёт у первой задачи. Спрашивают её все
|
||||
# Какой день играть, шаговый даг узнаёт у первой задачи. Спрашивают её все
|
||||
# генераторные шаги: слепок снимается с той же позиции, что и проигрыш.
|
||||
PLAYED_DAY = "{{ ti.xcom_pull(task_ids='first_unplayed_day') }}"
|
||||
|
||||
START_DATE = datetime.datetime(2026, 1, 1, tzinfo=datetime.UTC)
|
||||
TAGS = ["пульт мира"]
|
||||
TAGS = ["модельное время"]
|
||||
|
||||
|
||||
@task
|
||||
@@ -120,7 +120,7 @@ def _generator(task_id: str, command: list[str]) -> DockerOperator:
|
||||
def _ingest_orders() -> TriggerDagRunOperator:
|
||||
"""Задача забора: дёрнуть даг приёма и дождаться, чем он кончился.
|
||||
|
||||
Ожидание здесь несущее. Без него работник позеленел бы, не узнав, доехал
|
||||
Ожидание здесь несущее. Без него шаговый даг позеленел бы, не узнав, доехал
|
||||
ли слепок, и позиция мира ушла бы вперёд хранилища — а зелёный конец графа
|
||||
не должен переживать отказ выше (ADR 0003).
|
||||
"""
|
||||
@@ -128,7 +128,7 @@ def _ingest_orders() -> TriggerDagRunOperator:
|
||||
task_id="trigger_orders_ingest",
|
||||
trigger_dag_id="orders_ingest",
|
||||
wait_for_completion=True,
|
||||
# Умолчание — минута опроса, а весь прогон работника пачкой короче.
|
||||
# Умолчание — минута опроса, а весь прогон шагового дага пачкой короче.
|
||||
poke_interval=10,
|
||||
)
|
||||
|
||||
@@ -151,7 +151,7 @@ def _ingest_orders() -> TriggerDagRunOperator:
|
||||
# (params.rst, Airflow 3.3), чтобы правило было видно сразу.
|
||||
description_md=(
|
||||
"Форма задает только число дней. Номер первого несыгранного "
|
||||
"дня работник берет из переменной `world_position` (Admin → "
|
||||
"дня шаговый даг берёт из переменной `world_position` (Admin → "
|
||||
"Variables). Поля Logical date и Run ID нужны самому Airflow "
|
||||
"и на выбор дня не влияют."
|
||||
),
|
||||
@@ -163,7 +163,7 @@ def world_next_day():
|
||||
|
||||
Запускается руками. День по умолчанию один, но разгон вперёд идёт одним
|
||||
нажимом, а не десятью: сколько дней играть — параметр запуска. Первый
|
||||
день работник берет из `world_position`. Логическая дата прогона на выбор
|
||||
день шаговый даг берёт из `world_position`. Логическая дата прогона на выбор
|
||||
дня не влияет.
|
||||
"""
|
||||
|
||||
@@ -215,7 +215,7 @@ def world_live_day():
|
||||
Ускорение ×60: модельные сутки укладываются примерно в двадцать четыре
|
||||
реальные минуты, и суточная волна разворачивается на глазах. Запускается
|
||||
руками; чтобы мир жил так день за днём сам, есть выключатель `world_live`.
|
||||
Следующий день работник берет из `world_position`. Логическая дата прогона
|
||||
Следующий день шаговый даг берёт из `world_position`. Логическая дата прогона
|
||||
на выбор дня не влияет.
|
||||
"""
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ def world_initialize():
|
||||
|
||||
@task
|
||||
def wait_for_events() -> None:
|
||||
"""Дождаться приёма всех событий описи, включая строки брака."""
|
||||
"""Дождаться приёма всех событий паспорта, включая строки брака."""
|
||||
inventory = json.loads(INVENTORY.read_text(encoding="utf-8"))
|
||||
expected = sum(day["events"] for day in inventory["days"])
|
||||
first_date = inventory["days"][0]["date"]
|
||||
|
||||
Reference in new issue
Block a user