Add heuristic warnings to plan summary

Detects 7 common GP plan anti-patterns (broadcast of large table,
redistribute of large table, hot node, nested loop with large outer,
large sort, spill to disk, stale row estimates, planning-heavy queries)
and surfaces them as summary.warnings[] with severity + evidence.

Raw plan and raw summary metrics are preserved so the agent can verify
each warning against the underlying numbers.
This commit is contained in:
2026-05-31 14:17:52 +03:00
parent 7c9487e0f9
commit ddaf277703
2 changed files with 235 additions and 4 deletions

View File

@@ -31,15 +31,44 @@ MCP-сервер для оценки плана запросов dbt-модел
"planning_time_ms": 12.1,
"slowest_node": { "node_type": "Seq Scan", "actual_total_time_ms": 700.2, "...": "..." },
"motion_nodes": [{ "node_type": "Redistribute Motion", "...": "..." }],
"rows_misestimation_factor": 1.02
"rows_misestimation_factor": 1.02,
"warnings": [
{
"code": "broadcast_large_table",
"severity": "critical",
"message": "Broadcast Motion ships 5,400,000 rows to every segment ...",
"evidence": { "node_type": "Broadcast Motion", "actual_rows": 5400000, "...": "..." }
}
]
},
"plan": [ /* raw EXPLAIN JSON */ ],
"plan": [ /* raw EXPLAIN JSON пруф для каждого warning */ ],
"statement_timeout_ms": 300000,
"compiled_sql": "select ...",
"model_name": "fct_orders"
}
```
### Warnings
`summary.warnings[]` — это эвристический разбор плана для агента: какие узлы выглядят
неоптимально и что с этим делать. Сырые числа из плана **остаются** в `summary.*` и
`plan[]` — каждый warning несёт `evidence` со ссылкой на конкретные значения, чтобы
агент мог перепроверить рекомендацию, а не доверять ей слепо.
| `code` | `severity` | Триггер |
|--------|------------|---------|
| `rows_misestimation` | warning | plan vs actual rows расходятся в > 10× — статистика устарела |
| `broadcast_large_table` | critical | `Broadcast Motion` гонит > 1M строк на все сегменты |
| `redistribute_large_table` | warning | `Redistribute Motion` тасует > 10M строк — проверить distribution key |
| `hot_node` | warning | Один узел занимает > 60% `execution_time_ms` |
| `nested_loop_large` | critical | `Nested Loop` с > 10 000 итераций |
| `large_sort` | warning | `Sort` по > 10M строк |
| `spill_to_disk` | warning | Узел льётся на диск (`Disk Usage > 0`, `Sort Method: external merge Disk` и т.п.) |
| `planning_heavy` | info | `planning_time_ms / execution_time_ms > 20%` |
Пороги — константы в верхней части [src/gp_mcp/explain.py](src/gp_mcp/explain.py)
(`ROWS_MISESTIMATION_FACTOR`, `BROADCAST_LARGE_ROWS`, …), калибруются под кластер.
## Установка
```bash