Architecture

How myflames works internally: parser, renderers, advisor, and the teach module.

Design principles

Data flow

Input JSON Parser Renderer Output (MySQL or MariaDB) → parser.py → flamegraph.py → .svg / .html • auto-detect engine output_bargraph.py • normalize MariaDB output_treemap.py • build unified tree output_diagram.py • analyze_plan() output_tree.py ↓ advisor.py → warnings + suggestions output_sidecar.py → .json sidecar output_html.py → .html wrapper

Module map

ModuleRole
cli.pyArgument parsing, live-connection orchestration, SVG height patching for the analysis panel.
parser.pySingle entry point. Builds unified tree from MySQL or MariaDB JSON. analyze_plan(root) scans for full scans, hash joins, temp tables, filesorts, non-sargable joins, etc.
flamegraph.pySVG flame graph renderer (pure-Python port of Brendan Gregg's FlameGraph).
output_bargraph.pySVG bar chart renderer, sorted by self-time.
output_treemap.pySVG treemap renderer with squarified layout.
output_diagram.pySVG Visual Explain-style diagram with drag/zoom.
output_tree.pySVG collapsible execution tree.
output_html.pyHTML wrapper with progressive-disclosure UI, glossary chips, and JSON-LD in <head>.
output_sidecar.pyJSON sidecar generator (v1 schema). Machine-readable plan summary, warnings, suggestions.
advisor.pyEnvironment advisor: 8 rules that combine plan signals with collected server state.
glossary.py31 glossary entries with 3-tier explanations (short / technical / newcomer). Powers the HTML glossary chips.
teach/Interactive algorithm lessons. Shared animation runtime + cost models + per-lesson renderers.

Parser internals

The parser (parser.py) handles two structurally different JSON formats:

MariaDB normalization (_normalize_mariadb*() functions) converts MariaDB's structure into MySQL's operation/inputs tree before parse_node() processes it. This means every renderer and the advisor work with one canonical tree format.

analyze_plan(root) walks the parsed tree and returns a dictionary of detected features: full_scans, hash_joins, temp_tables, filesorts, bnl_nodes, nonsargable_joins, index_suggestions, and more.

Teach module architecture

The teach/ subpackage is a self-contained animation platform:

FileRole
__init__.pyLesson registry (LESSONS dict), render_lesson(), CLI dispatch.
_anim.pyShared JS animation runtime: tween, timeline, easing, pause/speed, scrubber, phase marks.
_html.pyShared HTML chrome: CSS, controls, toolbar, phase nav, query card, explainer, readout grid.
_cost_model.pyCost-model functions tied to MySQL 8.4 / MariaDB 11.4 defaults. Constants enforced by tests.
join_family/, index_family/, scan_family/, cache_family/Lesson modules grouped by topic. Each exports a render() function. Some lessons remain as top-level *.py files with thin family wrappers.

Every lesson is a single self-contained HTML file (~65-85 KB) with inlined CSS, JS, and SVG. No build step, no bundler, no CI.

Environment advisor rules

advisor.py runs 8 rules that cross-reference the parsed plan with collected server state:

RuleFires when
Non-sargable join predicateJoin uses CONCAT(col), CAST(col), LOWER(col), DATE(col), etc.
Buffer pool vs working setinnodb_buffer_pool_size < 25-50% of referenced tables
Sort buffer vs filesortFilesort detected + sort_buffer_size < 2 MB
Join buffer vs hash/BNLHash join or BNL + join_buffer_size < 2 MB
Tmp table sizeTemp table + min(tmp_table_size, max_heap_table_size) < 32 MB
Optimizer switch overrideshash_join=off, mrr=off, derived_condition_pushdown=off
Missing indexesParser heuristic + collected schema confirms no covering index
Engine != InnoDBTable uses MyISAM/MEMORY

Every suggestion carries a Why: clause grounded in the MySQL cost model. This is enforced by a test.

Testing

The test suite has 1200+ tests across two files:

# Full suite
./run-tests.sh

# Just teach tests
python3 -m unittest discover -s test -p "test_teach.py" -v

Fixtures are generated from live MySQL/MariaDB Docker containers:

./scripts/generate-fixtures.sh           # MySQL fixtures
./scripts/generate-mariadb-fixtures.sh   # MariaDB fixtures