032: fix remaining sync→async propagation (17 call sites)

Core fixes:
  - service_datasource.py: fetch_datasource_metadata() → async
  - service.py: create_job(), update_job() → async (callers await)
  - _job_routes.py: await create_job/update_job

Maintenance scanners:
  - _dashboard_scanner.py: 4 functions → async (find_affected, _get_linked,
    _apply_filters, _resolve_title)
  - _chart_manager.py: 3 functions → async
  - _banner_renderer.py: rebuild_banner → async
  - _orchestrators.py: 3 orchestrators → async
  - maintenance_banner.py: await async calls

Migration:
  - dry_run_orchestrator.py: run(), _build_target_signatures() → async
  - risk_assessor.py: build_risks() → async
  - migration.py: await service.run()
  - mapping_service.py: sync_environment() → async

Dead code:
  - _helpers.py: _find_dashboard_id_by_slug marked DEPRECATED
This commit is contained in:
2026-06-05 08:56:37 +03:00
parent a5cd06546f
commit c7d8f4431e
13 changed files with 67 additions and 66 deletions

View File

@@ -171,7 +171,7 @@ def _build_banner_text_for_dashboard(
# @SIDE_EFFECT Modifies Superset chart. Writes DB.
# @RELATION DEPENDS_ON -> [build_banner_text]
# @RELATION DEPENDS_ON -> [SupersetDashboardsWriteMixin]
def rebuild_banner(
async def rebuild_banner(
banner_id: str,
db_session: Session,
superset_client: SupersetClient,
@@ -225,7 +225,7 @@ def rebuild_banner(
# Update banner on dashboard (native MARKDOWN in layout)
if banner.chart_id:
try:
superset_client.update_banner_on_dashboard(
await superset_client.update_banner_on_dashboard(
banner.dashboard_id, banner.chart_id, banner_text
)
except Exception as e:

View File

@@ -29,7 +29,7 @@ from ._dashboard_scanner import _resolve_dashboard_title
# @POST Returns MaintenanceDashboardBanner with chart_id set. Chart is placed at top of dashboard.
# @SIDE_EFFECT Creates chart in Superset, modifies dashboard layout, writes DB row.
# @RELATION DEPENDS_ON -> [SupersetDashboardsWriteMixin]
def ensure_banner_chart(
async def ensure_banner_chart(
dashboard_id: int,
environment_id: str,
superset_client: SupersetClient,
@@ -61,7 +61,7 @@ def ensure_banner_chart(
)
# Verify the chart still exists in Superset
try:
superset_client.get_chart(existing.chart_id)
await superset_client.get_chart(existing.chart_id)
app_logger.reflect(
"Existing banner chart is alive, reusing",
extra={"banner_id": existing.id, "chart_id": existing.chart_id},
@@ -81,7 +81,7 @@ def ensure_banner_chart(
# Create markdown chart in Superset
try:
chart_id = superset_client.create_markdown_chart(
chart_id = await superset_client.create_markdown_chart(
dashboard_id, "*Maintenance banner placeholder*"
)
except Exception as e:
@@ -94,7 +94,7 @@ def ensure_banner_chart(
# Insert chart at top of dashboard layout
try:
superset_client.update_dashboard_layout(dashboard_id, chart_id)
await superset_client.update_dashboard_layout(dashboard_id, chart_id)
except Exception as e:
app_logger.explore(
"Failed to update dashboard layout, deleting chart",
@@ -103,7 +103,7 @@ def ensure_banner_chart(
)
# Clean up the chart we just created
try:
superset_client.delete_chart(chart_id)
await superset_client.delete_chart(chart_id)
except Exception:
pass
raise
@@ -138,7 +138,7 @@ def ensure_banner_chart(
# @RELATION CALLS -> [ensure_banner_chart]
# @RELATION CALLS -> [_build_banner_text_for_dashboard]
# @RELATION CALLS -> [_resolve_dashboard_title]
def _process_dashboards_for_start(
async def _process_dashboards_for_start(
dashboard_ids: list[int],
event_id: str,
environment_id: str,
@@ -158,7 +158,7 @@ def _process_dashboards_for_start(
)
try:
# Ensure banner chart exists
banner = ensure_banner_chart(
banner = await ensure_banner_chart(
dash_id,
environment_id,
superset_client,
@@ -184,7 +184,7 @@ def _process_dashboards_for_start(
# Update banner content on dashboard (native MARKDOWN in layout)
if banner.chart_id:
try:
superset_client.update_banner_on_dashboard(
await superset_client.update_banner_on_dashboard(
dash_id, banner.chart_id, banner_text
)
except Exception as e:
@@ -207,7 +207,7 @@ def _process_dashboards_for_start(
db_session.flush()
# Resolve dashboard title
title = _resolve_dashboard_title(dash_id, superset_client)
title = await _resolve_dashboard_title(dash_id, superset_client)
successful_dashboards.append(
{
"id": dash_id,
@@ -239,7 +239,7 @@ def _process_dashboards_for_start(
# still use same banner → rebuild; if no other events → remove banner (chart + layout).
# @SIDE_EFFECT Modifies Superset dashboards (removes charts, updates layouts). Writes DB.
# @RELATION CALLS -> [rebuild_banner]
def _process_states_for_end(
async def _process_states_for_end(
states: list[MaintenanceDashboardState],
event_id: str,
superset_client: SupersetClient,
@@ -278,7 +278,7 @@ def _process_states_for_end(
# Other events still active — rebuild banner text
state.status = MaintenanceDashboardStateStatus.REMOVED
db_session.flush()
rebuild_banner(banner_id, db_session, superset_client)
await rebuild_banner(banner_id, db_session, superset_client)
app_logger.reflect(
"Banner rebuilt after removing event",
extra={"banner_id": banner_id, "dashboard_id": dash_id},
@@ -291,7 +291,7 @@ def _process_states_for_end(
# Remove chart from layout
if banner.chart_id:
try:
superset_client.remove_chart_from_layout(
await superset_client.remove_chart_from_layout(
dash_id, banner.chart_id
)
except Exception as e:
@@ -311,7 +311,7 @@ def _process_states_for_end(
# Delete chart
try:
superset_client.delete_chart(banner.chart_id)
await superset_client.delete_chart(banner.chart_id)
except Exception as e:
app_logger.explore(
"Failed to delete chart",

View File

@@ -23,7 +23,7 @@ from ..sql_table_extractor import extract_tables_from_sql
# @SIDE_EFFECT Fetches all datasets from Superset (paginated). May be slow with many datasets.
# @RELATION DEPENDS_ON -> [SupersetClient]
# @RELATION DEPENDS_ON -> [SqlTableExtractorModule]
def find_affected_dashboards(
async def find_affected_dashboards(
tables: list[str],
superset_client: SupersetClient,
settings: MaintenanceSettings | None = None,
@@ -48,7 +48,7 @@ def find_affected_dashboards(
# Fetch all datasets from Superset
try:
_, datasets = superset_client.get_datasets()
_, datasets = await superset_client.get_datasets()
except Exception as e:
app_logger.explore(
"Failed to fetch datasets from Superset",
@@ -78,7 +78,7 @@ def find_affected_dashboards(
if not is_virtual and ds_schema and ds_table:
qualified = f"{ds_schema}.{ds_table}"
if qualified in target_tables:
linked = _get_linked_dashboards(superset_client, ds_id)
linked = await _get_linked_dashboards(superset_client, ds_id)
matched_dashboard_ids.update(linked)
continue
@@ -86,7 +86,7 @@ def find_affected_dashboards(
if is_virtual and ds_sql:
extracted = extract_tables_from_sql(ds_sql)
if extracted & target_tables:
linked = _get_linked_dashboards(superset_client, ds_id)
linked = await _get_linked_dashboards(superset_client, ds_id)
matched_dashboard_ids.update(linked)
app_logger.reflect(
@@ -95,7 +95,7 @@ def find_affected_dashboards(
)
# Apply filtering from settings
result = _apply_dashboard_filters(
result = await _apply_dashboard_filters(
list(matched_dashboard_ids), superset_client, settings
)
@@ -110,14 +110,14 @@ def find_affected_dashboards(
# #region _get_linked_dashboards [C:2] [TYPE Function]
# @BRIEF Fetch linked dashboards for a dataset from Superset.
# @SIDE_EFFECT Calls Superset API.
def _get_linked_dashboards(
async def _get_linked_dashboards(
superset_client: SupersetClient,
dataset_id: int | None,
) -> set[int]:
if not dataset_id:
return set()
try:
detail = superset_client.get_dataset_detail(dataset_id)
detail = await superset_client.get_dataset_detail(dataset_id)
linked = detail.get("linked_dashboards", [])
return {int(d["id"]) for d in linked if d.get("id")}
except Exception as e:
@@ -132,7 +132,7 @@ def _get_linked_dashboards(
# #region _apply_dashboard_filters [C:2] [TYPE Function]
# @BRIEF Apply scope (published/draft/all), excluded, and forced filters from settings.
def _apply_dashboard_filters(
async def _apply_dashboard_filters(
dashboard_ids: list[int],
superset_client: SupersetClient,
settings: MaintenanceSettings | None,
@@ -147,7 +147,7 @@ def _apply_dashboard_filters(
# Apply scope filtering
if settings.dashboard_scope != DashboardScope.ALL:
try:
_, all_dashboards = superset_client.get_dashboards()
_, all_dashboards = await superset_client.get_dashboards()
scope = settings.dashboard_scope
filtered: list[int] = []
for did in dashboard_ids:
@@ -186,12 +186,12 @@ def _apply_dashboard_filters(
# #region _resolve_dashboard_title [C:2] [TYPE Function]
# @BRIEF Resolve dashboard title from Superset by ID.
def _resolve_dashboard_title(
async def _resolve_dashboard_title(
dashboard_id: int,
superset_client: SupersetClient,
) -> str:
try:
_, dashboards = superset_client.get_dashboards()
_, dashboards = await superset_client.get_dashboards()
for d in dashboards:
if d.get("id") == dashboard_id:
return d.get("dashboard_title") or d.get("title") or str(dashboard_id)

View File

@@ -56,16 +56,12 @@ def build_idempotency_key(
# @BELIEF Uses belief_scope with REASON/REFLECT/EXPLORE markers.
# @RELATION CALLS -> [find_affected_dashboards]
# @RELATION CALLS -> [_process_dashboards_for_start]
def start_maintenance(
async def start_maintenance(
event_id: str,
db_session: Session,
superset_client: SupersetClient,
) -> dict[str, Any]:
with belief_scope("start_maintenance", f"event_id={event_id}"):
app_logger.reason(
"Starting maintenance event",
extra={"event_id": event_id},
)
# Load event
event = db_session.query(MaintenanceEvent).filter(
@@ -100,7 +96,7 @@ def start_maintenance(
# Find affected dashboards
try:
dashboard_ids = find_affected_dashboards(
dashboard_ids = await find_affected_dashboards(
event.tables,
superset_client,
settings,
@@ -142,7 +138,7 @@ def start_maintenance(
}
# Process each dashboard
successful_dashboards, failed_dashboards, _ = _process_dashboards_for_start(
successful_dashboards, failed_dashboards, _ = await _process_dashboards_for_start(
dashboard_ids,
event_id,
environment_id,
@@ -193,7 +189,7 @@ def start_maintenance(
# @SIDE_EFFECT Modifies Superset dashboards (removes charts, updates layouts). Writes DB.
# @BELIEF Uses belief_scope with REASON/REFLECT/EXPLORE markers.
# @RELATION CALLS -> [_process_states_for_end]
def end_maintenance(
async def end_maintenance(
event_id: str,
db_session: Session,
superset_client: SupersetClient,
@@ -234,7 +230,7 @@ def end_maintenance(
).all()
# Delegate per-state processing
removed_count, failed_removals = _process_states_for_end(
removed_count, failed_removals = await _process_states_for_end(
states, event_id, superset_client, db_session,
)
@@ -268,7 +264,7 @@ def end_maintenance(
# @SIDE_EFFECT Modifies multiple Superset dashboards. Writes multiple DB rows.
# @BELIEF Uses belief_scope with REASON/REFLECT/EXPLORE markers.
# @RELATION CALLS -> [end_maintenance]
def end_all_maintenance(
async def end_all_maintenance(
db_session: Session,
superset_client: SupersetClient,
) -> dict[str, Any]:
@@ -302,7 +298,7 @@ def end_all_maintenance(
extra={"event_id": event.id},
)
try:
result = end_maintenance(
result = await end_maintenance(
event.id, db_session, superset_client
)
total_removed += result.get("removed_from", 0)