Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions docs/dv2-multi-branch/demo.cast.README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# demo.cast — terminal recording of the live 2-minute pitch

> **⚠ Superseded (legend reset).** `demo.cast` and `demo_voiced.mp4` were
> captured 2026-05-23, before the kitchen-appliance-importer legend migration
> (2026-07-03) — the video/audio still shows the retired fashion-retailer
> framing and the old 40/25/15/10/10 branch split. The narration and transcript
> **text** were rewritten to the current legend in the G2 S5 follow-up PR
> (`demo_voiced.narration.txt`, `demo_transcript.txt`); the media itself is
> pending re-recording on the Mac kind stand (plan step S6). Until that
> re-recording happens, treat the `.cast`/`.mp4` files as stale and read the
> text scripts (or `demo_evidence.md`) for the current numbers.

`demo.cast` is an [asciinema](https://asciinema.org/) v2 recording of
`bash demo_runner.sh` executed against the `hq-demo` cluster. Captured
on 2026-05-23 at 130×35 with `xterm-256color`. Total runtime ~42 s
Expand Down Expand Up @@ -69,12 +79,18 @@ the running cluster:
| Beat | Command | Live output |
|------|---------|-------------|
| 1 | `kubectl get nodes --show-labels` | 3 nodes with `branch=msk` / `nodepool` / `workload` labels |
| 2 | CH: count tables in `rv` by prefix | bv=6, hub=8, lnk=8, sat=39 |
| 3 | CH: `record_source` distribution from `hub_order` | msk 40.5% / spb 24.1% / ekb 14.5% / dxb 11.2% / ala 9.7% |
| 4 | CH: BV MDM PII + loyalty per branch | msk 800/800/640, dxb 200/200/0 |
| 2 | CH: count tables in `rv` by prefix | bv=6, hub=8, lnk=8, sat=48 |
| 3 | CH: `record_source` distribution from `hub_order`, collapsed to branch | msk 95.4% / spb 1.8% / ekb 1.3% / dxb 0.8% / ala 0.8% |
| 4 | CH: BV MDM PII + loyalty per branch | msk 2190/2190/152, dxb 60/60/0 |
| 5a | `kubectl create job --from=cronjob/dv2-cold-offload-msk` | job created, succeeds in ~8 s |
| 5b | `mc ls -r local/cold-tier` | parquet files in MinIO for all 5 branches |

Beats 2–4 reflect the current kitchen-appliance-importer legend (matching
`demo_evidence.md` §4/§5/§8, re-captured 2026-07-03). Beats 1, 5a and 5b are
still the pre-migration `demo.cast` capture — legend-independent mechanism,
row counts/labels pending Mac re-recording (see the superseded notice above
and `demo_evidence.md` §1/§9).

The recording was made on the `hq-demo` cluster in the multi-branch state
described by the DV2 docs in this directory.

Expand Down
16 changes: 13 additions & 3 deletions docs/dv2-multi-branch/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ <h1>DV2.0 Multi-Branch — Live Demo</h1>
Self-hosted upstream Kubernetes, ClickHouse 25.5 + Postgres 17 + MinIO,
Data Vault 2.0 schema with five-branch <code>record_source</code> dimension.
</p>
<p class="lede" style="color:#a15c00">
<strong>Superseded — pending re-recording.</strong> The embedded
<code>demo.cast</code> below was captured 2026-05-23, before the
kitchen-appliance-importer legend migration (2026-07-03), and the beat
table beneath it is written to the current legend's numbers, not the
figures the old recording actually shows. Media re-recording on the Mac
kind stand is tracked as plan step S6; until then, treat the playing
video as stale. See <code>demo.cast.README.md</code> and
<code>demo_evidence.md</code>.
</p>

<div class="player" id="player"></div>

Expand All @@ -117,9 +127,9 @@ <h1>DV2.0 Multi-Branch — Live Demo</h1>
<div class="beats">
<div class="h">Time</div><div class="h">Beat</div><div class="h">Output</div>
<div class="t">00:00</div><div>Cluster topology — <code>kubectl get nodes</code></div><div>3 nodes, labelled <code>branch</code> / <code>nodepool</code> / <code>workload</code></div>
<div class="t">00:08</div><div>Model surface — table kinds in <code>rv</code></div><div>bv 6, hub 8, lnk 8, sat 39</div>
<div class="t">00:14</div><div>Multi-branch distribution from <code>hub_order</code></div><div>msk 40.5 / spb 24.1 / ekb 14.5 / dxb 11.2 / ala 9.7 %</div>
<div class="t">00:22</div><div>Business Vault MDM — PII + loyalty</div><div>msk 800/800/640, dxb 200/200/0</div>
<div class="t">00:08</div><div>Model surface — table kinds in <code>rv</code></div><div>bv 6, hub 8, lnk 8, sat 48</div>
<div class="t">00:14</div><div>Multi-branch distribution from <code>hub_order</code></div><div>msk 95.4 / spb 1.8 / ekb 1.3 / dxb 0.8 / ala 0.8 %</div>
<div class="t">00:22</div><div>Business Vault MDM — PII + loyalty</div><div>msk 2190/2190/152, dxb 60/60/0</div>
<div class="t">00:30</div><div>Cold-offload <code>kubectl create job</code> + MinIO listing</div><div>parquet files for all 5 branches in <code>local/cold-tier</code></div>
<div class="t">00:40</div><div>Closing — single bootstrap reproduces everything</div><div><code>bash infrastructure/dv2/bootstrap.sh</code></div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions docs/dv2-multi-branch/demo_evidence.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ Docker:
> seed is now **2,500 customers / 10,000 orders / 160 SKU / 14,853 line
> items, priced in ₽** — see `docs/domain.md` and `docs/generator-spec.md`
> for the model.
>
> **⚠ `demo_voiced.mp4` / `demo.cast` are superseded.** Both were captured
> 2026-05-23, before this legend reset, and still show the retired
> fashion-retailer narration and the old 40/25/15/10/10 branch split.
> `demo_voiced.narration.txt` and `demo_transcript.txt` (the scripts behind
> them) were rewritten to the current legend in the G2 S5 follow-up PR; the
> `.mp4`/`.cast` media itself is pending re-recording on the Mac kind stand
> (plan step S6) — see `demo.cast.README.md` for detail.

> **⚠ Infra sections (§1–3, §9–15) are pending re-capture on the kind
> cluster (Mac stand).** Topology, workload pinning, PVCs, the MinIO cold
Expand Down
96 changes: 48 additions & 48 deletions docs/dv2-multi-branch/demo_transcript.txt
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
# === Beat 1 — Cluster topology (00:25) ===
$ kubectl get nodes --show-labels | head -5
NAME STATUS ROLES AGE VERSION LABELS
hq-demo-control-plane Ready control-plane 6h18m v1.35.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,branch=msk,kubernetes.io/arch=amd64,kubernetes.io/hostname=hq-demo-control-plane,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=,nodepool=hq-control
hq-demo-worker Ready <none> 6h18m v1.35.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,branch=msk,kubernetes.io/arch=amd64,kubernetes.io/hostname=hq-demo-worker,kubernetes.io/os=linux,nodepool=hq-data-tier-a,workload=postgres
hq-demo-worker2 Ready <none> 6h18m v1.35.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,branch=msk,kubernetes.io/arch=amd64,kubernetes.io/hostname=hq-demo-worker2,kubernetes.io/os=linux,nodepool=hq-data-tier-b,workload=clickhouse
# === Beat 2 — DV2.0 model surface (00:45) ===
$ kubectl exec -n dv2 clickhouse-0 -- clickhouse-client --user default --password demo --query "SELECT splitByString('_', name)[1] AS kind, count() FROM system.tables WHERE database='rv' GROUP BY kind ORDER BY kind"
bv 6
hub 8
lnk 8
sat 39
# === Beat 3 — Multi-branch distribution (01:05) ===
$ kubectl exec -n dv2 clickhouse-0 -- clickhouse-client --user default --password demo --query "SELECT splitByString('__', record_source)[2] AS branch, count() AS orders, round(count()*100.0/(SELECT count() FROM rv.hub_order),1) AS pct FROM rv.hub_order GROUP BY branch ORDER BY pct DESC FORMAT PrettyCompact"
┌─branch─┬─orders─┬──pct─┐
1. │ msk │ 420040.5 │
2. │ spb │ 2500 │ 24.1 │
3. │ ekb │ 1500 │ 14.5 │
4. │ dxb │ 1160 │ 11.2 │
5. │ ala │ 1000 9.7 │
└────────┴────────┴──────┘
# === Beat 4 — Business Vault MDM (01:25) ===
$ kubectl exec -n dv2 clickhouse-0 -- clickhouse-client --user default --password demo --multiline --query "SELECT 'msk' AS branch, count() AS rows, countIf(first_name != '') AS with_pii, countIf(loyalty_segment != '') AS with_loyalty FROM rv.bv_customer_mdm__msk UNION ALL SELECT 'dxb', count(), countIf(first_name != ''), countIf(loyalty_segment IS NOT NULL AND loyalty_segment != '') FROM rv.bv_customer_mdm__dxb FORMAT PrettyCompact"
┌─branch─┬─rows─┬─with_pii─┬─with_loyalty─┐
1. │ dxb │ 200 200 │ 0 │
2. │ msk │ 800 800 640 │
└────────┴──────┴──────────┴──────────────┘
# === Beat 5a — Cold offload (01:50) ===
$ kubectl create job --from=cronjob/dv2-cold-offload-msk cold-demo-3455 -n dv2
job.batch/cold-demo-3455 created
# === Beat 5b — MinIO parquet listing ===
$ kubectl exec -n dv2 minio-0 -- mc ls -r local/cold-tier | tail -5
[2026-05-23 09:44:37 UTC] 6.7KiB STANDARD branch=ala/year=2026/month=05/customers_anon.parquet
[2026-05-23 09:44:32 UTC] 6.7KiB STANDARD branch=dxb/year=2026/month=05/customers_anon.parquet
[2026-05-23 09:44:37 UTC] 8.6KiB STANDARD branch=ekb/year=2026/month=05/customers_anon.parquet
[2026-05-23 10:20:13 UTC] 20KiB STANDARD branch=msk/year=2026/month=05/customers_anon.parquet
[2026-05-23 09:44:32 UTC] 13KiB STANDARD branch=spb/year=2026/month=05/customers_anon.parquet
# === Closing (02:00) — Single bootstrap reproduces everything ===
$ bash infrastructure/dv2/bootstrap.sh
Demo complete. See docs/dv2-multi-branch/demo_evidence.md for the full breakdown.

# === Beat 1 — Cluster topology (00:25) ===
$ kubectl get nodes --show-labels | head -5
NAME STATUS ROLES AGE VERSION LABELS
hq-demo-control-plane Ready control-plane 6h18m v1.35.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,branch=msk,kubernetes.io/arch=amd64,kubernetes.io/hostname=hq-demo-control-plane,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=,nodepool=hq-control
hq-demo-worker Ready <none> 6h18m v1.35.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,branch=msk,kubernetes.io/arch=amd64,kubernetes.io/hostname=hq-demo-worker,kubernetes.io/os=linux,nodepool=hq-data-tier-a,workload=postgres
hq-demo-worker2 Ready <none> 6h18m v1.35.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,branch=msk,kubernetes.io/arch=amd64,kubernetes.io/hostname=hq-demo-worker2,kubernetes.io/os=linux,nodepool=hq-data-tier-b,workload=clickhouse

# === Beat 2 — DV2.0 model surface (00:45) ===
$ kubectl exec -n dv2 clickhouse-0 -- clickhouse-client --user default --password demo --query "SELECT splitByString('_', name)[1] AS kind, count() FROM system.tables WHERE database='rv' GROUP BY kind ORDER BY kind"
bv 6
hub 8
lnk 8
sat 48

# === Beat 3 — Multi-branch distribution (01:05) ===
$ kubectl exec -n dv2 clickhouse-0 -- clickhouse-client --user default --password demo --query "SELECT splitByString('__', record_source)[2] AS branch, count() AS orders, round(count()*100.0/(SELECT count() FROM rv.hub_order),1) AS pct FROM rv.hub_order GROUP BY branch ORDER BY pct DESC FORMAT PrettyCompact"
┌─branch─┬─orders─┬──pct─┐
1. │ msk │ 954095.4 │
2. │ spb │ 180 │ 1.8 │
3. │ ekb │ 130 │ 1.3 │
4. │ dxb │ 75 │ 0.8 │
5. │ ala │ 75 0.8 │
└────────┴────────┴──────┘

# === Beat 4 — Business Vault MDM (01:25) ===
$ kubectl exec -n dv2 clickhouse-0 -- clickhouse-client --user default --password demo --multiline --query "SELECT 'msk' AS branch, count() AS rows, countIf(first_name != '') AS with_pii, countIf(loyalty_segment != '') AS with_loyalty FROM rv.bv_customer_mdm__msk UNION ALL SELECT 'dxb', count(), countIf(first_name != ''), countIf(loyalty_segment IS NOT NULL AND loyalty_segment != '') FROM rv.bv_customer_mdm__dxb FORMAT PrettyCompact"
┌─branch─┬─rows─┬─with_pii─┬─with_loyalty─┐
1. │ dxb │ 60 60 │ 0 │
2. │ msk │ 2190 2190 152 │
└────────┴──────┴──────────┴──────────────┘

# === Beat 5a — Cold offload (01:50) ===
$ kubectl create job --from=cronjob/dv2-cold-offload-msk cold-demo-3455 -n dv2
job.batch/cold-demo-3455 created

# === Beat 5b — MinIO parquet listing ===
$ kubectl exec -n dv2 minio-0 -- mc ls -r local/cold-tier | tail -5
[2026-05-23 09:44:37 UTC] 6.7KiB STANDARD branch=ala/year=2026/month=05/customers_anon.parquet
[2026-05-23 09:44:32 UTC] 6.7KiB STANDARD branch=dxb/year=2026/month=05/customers_anon.parquet
[2026-05-23 09:44:37 UTC] 8.6KiB STANDARD branch=ekb/year=2026/month=05/customers_anon.parquet
[2026-05-23 10:20:13 UTC] 20KiB STANDARD branch=msk/year=2026/month=05/customers_anon.parquet
[2026-05-23 09:44:32 UTC] 13KiB STANDARD branch=spb/year=2026/month=05/customers_anon.parquet

# === Closing (02:00) — Single bootstrap reproduces everything ===
$ bash infrastructure/dv2/bootstrap.sh

Demo complete. See docs/dv2-multi-branch/demo_evidence.md for the full breakdown.
8 changes: 4 additions & 4 deletions docs/dv2-multi-branch/demo_voiced.narration.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Mid-market fashion ритейлер. 50 человек, пять локаций — Москва, Питер, Екатеринбург, Дубай, Алматы. 18 процентов возвратов съедают маржу, реконсиляция шла 72 часа. Мы построили DV2.0 хранилище, и цикл сжался до четырёх часов. Покажу.
Импортёр мелкой кухонной техники под собственным брендом. Пять локаций — Москва, Питер, Екатеринбург, Дубай, Алматы. Три боли: кросс-канальный оверселл — один склад на все каналы, и партия дилеру должна попасть в остаток раньше, чем маркетплейсы допродадут те же единицы; контейнеры из Китая идут 40-60 дней, и застрявший на таможне груз — риск топ-3; операционка жонглирует пятью программами — 1С, Битрикс24, экран WMS, кабинеты маркетплейсов, Excel — ради ответа об одном заказе. Мы построили DV2.0 хранилище и операционный слой поверх: один ответ вместо пяти вкладок. Покажу.

Кластер. Три ноды Kubernetes, лейблы branch равно msk, dxb, ala. По ним nodeSelector закрепляет Postgres и ClickHouse — data sovereignty на уровне планировщика.

Модель. Шестьдесят таблиц: восемь хабов, восемь линков, под сорок сателлитов, BV вьюхи поверх. Сателлиты разрезаны по record_source равно система плюс филиал. Один customer_hk из 1С и из Битрикса — конфликт решается приоритетом, аудит сохраняется.
Модель. Семьдесят таблиц: восемь хабов, восемь линков, сорок восемь сателлитов, шесть BV вьюх поверх. Сателлиты разрезаны по record_source равно система плюс филиал. Один customer_hk из 1С и из Битрикса — конфликт решается приоритетом, аудит сохраняется.

Multi-branch. Распределение 40, 25, 15, 10, 10 — consistent hashing на store_id. Три миллисекунды на 10 тысяч строк. Production-движок даёт тот же план.
Multi-branch. Заказы концентрируются в MSK — девяносто пять процентов, весь e-com отгружается только оттуда; остальное — дилерский B2B по SPB, EKB, DXB и ALA. Пятнадцать миллисекунд на 10 тысяч строк. Production-движок даёт тот же план.

Business Vault. PII из 1С, loyalty из Битрикса, argMax по load_ts, source колонки для аудита. Дубайский view — отдельные сателлиты, PII никогда не пересекается с МСК.
Business Vault. PII из 1С, дилерские бонусы из Битрикса, argMax по load_ts, source колонки для аудита. Дубайский view — отдельные сателлиты, PII никогда не пересекается с МСК.

Холодный слой. CronJob раз в сутки выгружает только anon-сателлиты в S3, без PII. В проде — B2 или Hugging Face Datasets. 152 ФЗ и закон Казахстана соблюдены.

Expand Down