docs(course): маркеры «сверить-на-стенде» заменены живыми числами (#23)

Зачем: тикет #23 — каждое цитируемое число курса сверено с живым
стендом; до этого в уроках стояли заглушки.

Что:
- уроки 00–04: 18 маркеров заполнены числами свежего импорта
  (280 437 событий, нули в таблицах ошибок, счётчики ODS/DDS);
- лаба 07: таблица manifest после дня 4 (374 092 / 34 801 / 5 388),
  переходящие визиты по стыкам (35/22/26), числа после дня 5;
- лаба 08: каноническая граница трёх дней, пример замера свежести
  (лаг 3:45 модельного времени до догона, ETL ~29 с) и вернувшегося
  пользователя; две живые поправки разбора времени: убран
  принудительный UTC в разборе STG и суффикс +00:00 в сравнении
  границы (ловились только на живом стенде).

Проверка: детерминизм подтверждён двумя независимыми циклами
сброс→импорт→инкремент (числа manifest и checksum_sha256 дней 4 и 5
совпали бит в бит); chain-check зелёный; сценарий лабы 08 прогнан
вживую, включая стоп/продолжение и красный full_refresh=false из
урока 4; grep «сверить-на-стенде» пуст; ссылки и якоря целы;
make test (219+31) и make lint зелёные; /ai-text-lint по лабам — без
существенных находок.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-23 18:15:46 +03:00
co-authored by Claude Opus 4.8
parent cbf8b2d770
commit 8f03d8b347
6 changed files with 70 additions and 70 deletions
+11 -5
View File
@@ -55,7 +55,7 @@ Materialized View сразу складывает сообщения в STG. Д
SELECT
'STG' AS layer,
max(parseDateTime64BestEffortOrNull(
JSONExtractString(raw, 'event_timestamp'), 6, 'UTC'
JSONExtractString(raw, 'event_timestamp'), 6
)) AS max_event_ts
FROM stg.browser_raw
@@ -76,7 +76,8 @@ FROM dm.v_events_enriched;
```
На эталонном мире правые границы слоёв должны совпадать.
<!-- сверить-на-стенде -->
После импорта трёх эталонных дней все четыре слоя заканчиваются на
`2026-01-03 23:59:59.066766`.
### Запускаем живое продолжение
@@ -217,7 +218,9 @@ make generator-continue
Запусти `etl_pipeline` с пустой формой. После зелёного прогона выполни:
```sql
WITH parseDateTime64BestEffort('<model_t_end>', 6, 'UTC') AS boundary
WITH parseDateTime64BestEffort(
substring('<model_t_end>', 1, 19), 6
) AS boundary
SELECT
user_domain_id,
countIf(visit_start < boundary) AS visits_before,
@@ -233,7 +236,6 @@ GROUP BY user_domain_id
HAVING min(visit_start) < boundary AND max(visit_start) >= boundary
LIMIT 10;
```
<!-- сверить-на-стенде -->
Подставь вместо `<model_t_end>` границу из манифеста. Успех — хотя бы одна
строка. Иначе подожди несколько минут, перезапусти `etl_pipeline` и повтори
@@ -241,6 +243,10 @@ LIMIT 10;
пользователь с разными визитами пересекает границу замороженного мира.
Мир расходится: итоги превышают эталонные и различаются у менти. Это правильно.
Например, для границы `2026-01-06T00:00:00+00:00` найден пользователь
`5938d296-14cf-49cb-801e-82370518cf59`: `13` визитов до границы и `2` после
неё. Твои числа и идентификатор будут другими.
### Верни как было
Останови живой поток:
@@ -262,7 +268,7 @@ make generator-down
| Действие | Где смотреть | Что ожидать |
|----------|--------------|-------------|
| выполнить `make generator-continue` | Prometheus Targets | `generator` переходит в `UP` |
| измерить свежесть до и после ETL | запрос из секции 2 и часы | записаны модельное отставание и настенное ожидание <!-- сверить-на-стенде --> |
| измерить свежесть до и после ETL | запрос из секции 2 и часы | например: до ETL — `3:45:01.519535`, после — `0:45:55.977582` модельного времени; настенное ожидание — около `29` секунд |
| выполнить `make generator-down` | Grafana и Kafka | события перестают поступать, отставание читателей стекает к нулю |
| снова выполнить `make generator-continue` | Grafana, Kafka и STG | поток продолжается, offset-ы и правая граница снова растут |
| найти вернувшегося пользователя | запрос по границе `model_t_end` | есть пользователь с визитами до и после границы |