From 2e5b61f2f94c976b09b9660916b91bb562d22eb3 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 24 Apr 2026 20:17:24 -0500 Subject: [PATCH] fix(weather): drop expensive grid fetch from mount to avoid LV join timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /weather mount called Weather.latest_weather_grid/1 directly, which runs WeatherLayers.derive over 92k cells and Jason-encodes the result. Production HTTP logs showed dead renders consistently landing at 9.8–10.2 s — right at LiveView's socket join deadline. The browser would render half the page, the WS would fail to mount, and the user saw "attempting to reconnect". The JS hook already fires map_bounds via requestAnimationFrame immediately after mount, and the existing handle_event("map_bounds") path returns viewport-scoped weather data via the update_weather push event. So the dead-render data fetch is redundant — drop it. Mount now returns initial_data_json: "[]"; the layer paints ~50–200 ms after WS connect via the bounds-driven flow. --- .../live/weather_map_live.ex | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/microwaveprop_web/live/weather_map_live.ex b/lib/microwaveprop_web/live/weather_map_live.ex index a044fa5e..a2f2cf3f 100644 --- a/lib/microwaveprop_web/live/weather_map_live.ex +++ b/lib/microwaveprop_web/live/weather_map_live.ex @@ -173,27 +173,22 @@ defmodule MicrowavepropWeb.WeatherMapLive do # the underlying cache can advance to while the worker is walking # f00→f18). # - # Hot-path mount: when the closest-to-now time happens to be the - # cached latest, use `latest_weather_grid/1` so mount stays under - # the LV socket join timeout (~10s). A ProfilesFile read for any - # other hour is a single ~2 MB ETF decode, also fine for mount. + # Mount stays cheap — no grid materialisation. The dead render + # would otherwise spend ~5–10 s materialising 92k cells through + # `WeatherLayers.derive`, blocking the HTTP response and tripping + # the LiveView socket join timeout. The JS hook fires `map_bounds` + # via `requestAnimationFrame` immediately after mount, and the + # existing `handle_event("map_bounds", ...)` path drives the + # initial layer paint via `update_weather`. valid_times = Weather.available_weather_valid_times() - latest_vt = Weather.latest_grid_valid_time() initial_vt = pick_initial_valid_time(valid_times) - data = - cond do - is_nil(initial_vt) -> [] - initial_vt == latest_vt -> Weather.latest_weather_grid(@initial_bounds) - true -> Weather.weather_grid_at(initial_vt, @initial_bounds) - end - {:ok, assign(socket, page_title: "Weather Map", layers: @layers, selected_layer: "temperature", - initial_data_json: Jason.encode!(data), + initial_data_json: "[]", valid_time: initial_vt, valid_times: valid_times, initial_valid_times_json: Jason.encode!(Enum.map(valid_times, &DateTime.to_iso8601/1)),