@@ -22,8 +22,8 @@ That arrangement had three problems:
2222
2323This folder solves all three: the table is named ` mamba_fact_follow_up ` (peer of
2424` mamba_fact_client ` and ` mamba_fact_location_* ` ), the rebuild is a clean
25- DROP+ rebuild on every ETL run, and the layout is replicated mechanically for
26- the other 8 forms.
25+ blue-green (staging + atomic rename) rebuild on every ETL run, and the layout
26+ is replicated mechanically for the other 8 forms.
2727
2828## Layout
2929
@@ -32,7 +32,6 @@ follow_up/
3232├── README.md this file
3333├── sp_makefile lists sp_data_processing_derived_follow_up.sql
3434├── sp_data_processing_derived_follow_up.sql entry-point called by sp_mamba_data_processing_etl
35- ├── sp_compress_follow_up_flat_tables.sql post-create compression for the 10 source flat tables
3635└── facts/
3736 └── follow_up/
3837 ├── sp_makefile lists the 3 SP files below
@@ -46,77 +45,75 @@ since the materialization is always full-rebuild, there is no separate
4645` _update.sql ` for delta refresh. (The original ` _update.sql ` was reverted —
4746see "Full rebuild on every run" below.)
4847
49- ## Full rebuild on every run
48+ ## Full rebuild on every run (blue-green)
5049
5150The entry-point ` sp_data_processing_derived_follow_up ` performs a full rebuild
52- of ` mamba_fact_follow_up ` on every ETL run:
53-
54- 1 . ` DROP TABLE IF EXISTS mamba_fact_follow_up ` — always, regardless of mode
55- 2 . ` CALL sp_fact_follow_up() ` — creates the table (with `ROW_FORMAT=COMPRESSED
56- KEY_BLOCK_SIZE=8` ) and populates it via ` TRUNCATE` + 10-way LEFT JOIN INSERT
57- 3 . ` CALL sp_compress_follow_up_flat_tables() ` — re-applies compression to the
58- 10 source flat tables and the new materialization (idempotent metadata-only
59- change on already-compressed tables)
51+ of ` mamba_fact_follow_up ` on every ETL run, using a blue-green (staging +
52+ atomic swap) strategy so the live table stays available with old data for
53+ the whole rebuild:
54+
55+ 1 . ` DROP TABLE IF EXISTS mamba_fact_follow_up_staging ` — clean up any leftover
56+ staging table from a previously aborted run.
57+ 2 . ` CALL sp_fact_follow_up() ` — creates ` mamba_fact_follow_up_staging ` (with
58+ ` ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 ` ) and populates it via ` TRUNCATE `
59+ + 10-way LEFT JOIN INSERT. The live ` mamba_fact_follow_up ` is untouched
60+ and fully queryable throughout this step.
61+ 3 . ` DROP TABLE IF EXISTS mamba_fact_follow_up_old ` — guard against a
62+ pre-existing ` _old ` table left by a previously crashed run.
63+ 4 . Atomic `RENAME TABLE mamba_fact_follow_up TO mamba_fact_follow_up_old,
64+ mamba_fact_follow_up_staging TO mamba_fact_follow_up` — readers see either
65+ the fully old or fully new table, never a partial rebuild. The archived
66+ ` _old ` table is dropped immediately after.
6067
6168** Why full rebuild instead of incremental:**
6269
6370- ETL is user-triggered (after data changes), not a high-frequency timer.
6471- The user expects a known-correct state after each run, with no drift and
6572 no dependence on a ` _mamba_etl_schedule ` watermark.
66- - The performance cost is acceptable: a 10-way JOIN over 5M rows + 10
67- idempotent ` ALTER TABLE ` statements takes ~ 5–10 seconds on a typical
68- 8 GB / HDD box. Within the headroom of a 5-minute ETL schedule.
69- - Compression keeps the disk footprint small and the I/O bounded.
73+ - The performance cost is acceptable: a 10-way JOIN over 5M rows takes
74+ ~ 5–10 seconds on a typical 8 GB / HDD box. Within the headroom of a
75+ 5-minute ETL schedule.
76+ - Blue-green keeps the live table queryable throughout, instead of dropping
77+ it up front and leaving a "table not found" gap for the duration of the
78+ rebuild.
7079
7180** Behavior change from the previous incremental design:**
7281
7382- The ` sp_fact_follow_up_update.sql ` (delta refresh) was deleted. It is
7483 no longer called from any SP.
7584- The wrapper ` sp_fact_follow_up.sql ` lost its ` etl_incremental_mode `
76- parameter and the IF/ELSE branch. It always calls ` _create ` then ` _insert ` .
77- - The entry-point ` sp_data_processing_derived_follow_up.sql ` always does
78- ` DROP TABLE IF EXISTS mamba_fact_follow_up ` (no mode gating).
85+ parameter and the IF/ELSE branch. It always calls ` _create ` then ` _insert ` ,
86+ building into ` mamba_fact_follow_up_staging ` .
87+ - The entry-point ` sp_data_processing_derived_follow_up.sql ` no longer drops
88+ the live table up front — it drops leftover staging/` _old ` tables, builds
89+ staging, then does the atomic rename swap described above.
7990- The ` etl_incremental_mode ` parameter is kept in the entry-point's
8091 signature for backward compatibility with the call site in
8192 ` sp_mamba_data_processing_etl ` , but is intentionally ignored.
8293
8394## Compression
8495
85- Both ` mamba_fact_follow_up ` and the 10 source flat tables are configured with
86- ` ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 ` . This applies to deployments on both
87- small (8 GB / HDD) and large (powerful) machines — the trade is cheap CPU for
88- less I/O, which is the right trade on either profile.
89-
90- ### How compression is applied
91-
92- - ** ` mamba_fact_follow_up ` ** : created with `ROW_FORMAT=COMPRESSED
93- KEY_BLOCK_SIZE=8` in ` sp_fact_follow_up_create.sql`. Idempotent via
94- ` CREATE TABLE IF NOT EXISTS ` .
95- - ** The 10 source flat tables** : the core (separate repo) creates them as
96- default ` ROW_FORMAT=DYNAMIC ` via ` sp_mamba_flat_encounter_table_create ` .
97- We can't change that from this repo, so we re-apply compression via
98- ` sp_compress_follow_up_flat_tables.sql ` , called from the entry-point
99- after the materialization runs. The 10 ` ALTER TABLE ` statements are
100- idempotent: on an already-compressed table MySQL 8 performs a
101- metadata-only change (no rewrite); on an uncompressed table it rewrites
102- the table once.
96+ ` mamba_fact_follow_up ` (built as ` mamba_fact_follow_up_staging ` , then renamed
97+ into place) is created with ` ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 ` in
98+ ` sp_fact_follow_up_create.sql ` , idempotent via ` CREATE TABLE IF NOT EXISTS ` .
99+ The trade is cheap CPU for less I/O on both small (8 GB / HDD) and large
100+ deployments.
101+
102+ The 10 source flat tables (` mamba_flat_encounter_follow_up ` , ` _1 ` …` _9 ` ) are
103+ ** not** compressed by this repo. They're created by the core
104+ (` sp_mamba_flat_encounter_table_create ` , separate repo) with the default
105+ ` ROW_FORMAT=DYNAMIC ` , and we can't change that from here. An earlier version
106+ of this folder re-applied compression to those 10 tables via
107+ ` sp_compress_follow_up_flat_tables.sql ` , called from the entry-point after
108+ the materialization ran — that SP has been removed; only the materialized
109+ fact table itself is compressed now.
103110
104111### Why ` KEY_BLOCK_SIZE=8 ` and not 4 or 16
105112
106113- ` KEY_BLOCK_SIZE=4 ` : ~ 70% compression, ~ 20% CPU, more risk of compression failures on busy pages
107114- ` KEY_BLOCK_SIZE=8 ` : ~ 55% compression, ~ 10% CPU — sweet spot for HDD
108115- ` KEY_BLOCK_SIZE=16 ` : ~ 30% compression, ~ 5% CPU — marginal win over DYNAMIC
109116
110- ### What about existing deployments
111-
112- This folder assumes a fresh deploy: the next ETL run that hits the
113- full-rebuild path will produce compressed tables. For an existing
114- deployment with >5 GB of uncompressed data already on disk, run a
115- one-time `ALTER TABLE mamba_flat_encounter_follow_up[ _ 1.._ 9]
116- ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8` on each of the 10 flat tables
117- during a maintenance window. The compression cost is ~ minutes per
118- GB on HDD.
119-
120117## Known limitations (out of scope)
121118
122119- ** Drift correction** — the full-rebuild model makes this moot. Every
0 commit comments