Fix race condition when zooming map quickly

- Increase debounce time from 300ms to 400ms for bounds updates
- Cancel in-progress historical loading when new bounds are scheduled
- Add stale update detection to skip outdated bounds updates
- Clear timer reference and pending bounds when processing updates
- Add debug logging for cancelled loads and stale updates

This prevents overlapping historical data loads during rapid zoom
changes, eliminating duplicate packets and improving performance.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Graham McIntire 2025-07-18 12:27:46 -05:00
parent 5aae01e66c
commit 92585ae34f
No known key found for this signature in database
2 changed files with 36 additions and 17 deletions

View file

@ -38,6 +38,11 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do
"start_progressive_historical_loading called with zoom: #{socket.assigns.map_zoom}, bounds: #{inspect(socket.assigns.map_bounds)}"
)
# If we're already loading, just update the generation to cancel old requests
if socket.assigns[:historical_loading] do
Logger.debug("Already loading historical data, cancelling previous load")
end
# Increment generation to invalidate any in-flight loads
new_generation = socket.assigns.loading_generation + 1

View file

@ -639,24 +639,33 @@ defmodule AprsmeWeb.MapLive.Index do
defp handle_info_process_bounds_update(map_bounds, socket) do
require Logger
# Check if we need to process this bounds update
should_process =
!socket.assigns[:initial_bounds_loaded] or
socket.assigns[:needs_initial_historical_load] or
not BoundsUtils.compare_bounds(map_bounds, socket.assigns.map_bounds)
Logger.debug(
"handle_info_process_bounds_update - should_process: #{should_process}, initial_bounds_loaded: #{socket.assigns[:initial_bounds_loaded]}, needs_initial_historical_load: #{socket.assigns[:needs_initial_historical_load]}"
)
if should_process do
Logger.debug("Processing bounds update: #{inspect(map_bounds)}")
socket = process_bounds_update(map_bounds, socket)
socket = assign(socket, initial_bounds_loaded: true)
# Check if this is a stale update (newer bounds have been scheduled)
if socket.assigns[:pending_bounds] && socket.assigns.pending_bounds != map_bounds do
Logger.debug("Skipping stale bounds update, newer bounds pending")
{:noreply, socket}
else
Logger.debug("Skipping bounds update - no change detected")
{:noreply, socket}
# Clear the timer reference since we're processing this update
socket = assign(socket, bounds_update_timer: nil, pending_bounds: nil)
# Check if we need to process this bounds update
should_process =
!socket.assigns[:initial_bounds_loaded] or
socket.assigns[:needs_initial_historical_load] or
not BoundsUtils.compare_bounds(map_bounds, socket.assigns.map_bounds)
Logger.debug(
"handle_info_process_bounds_update - should_process: #{should_process}, initial_bounds_loaded: #{socket.assigns[:initial_bounds_loaded]}, needs_initial_historical_load: #{socket.assigns[:needs_initial_historical_load]}"
)
if should_process do
Logger.debug("Processing bounds update: #{inspect(map_bounds)}")
socket = process_bounds_update(map_bounds, socket)
socket = assign(socket, initial_bounds_loaded: true)
{:noreply, socket}
else
Logger.debug("Skipping bounds update - no change detected")
{:noreply, socket}
end
end
end
@ -1355,11 +1364,16 @@ defmodule AprsmeWeb.MapLive.Index do
end
defp schedule_bounds_update(map_bounds, socket) do
# Cancel existing timer if present
if socket.assigns[:bounds_update_timer] do
Process.cancel_timer(socket.assigns.bounds_update_timer)
end
timer_ref = Process.send_after(self(), {:process_bounds_update, map_bounds}, 300)
# Cancel any in-progress historical loading to prevent race conditions
socket = HistoricalLoader.cancel_pending_loads(socket)
# Increase debounce time slightly for better stability during rapid zooming
timer_ref = Process.send_after(self(), {:process_bounds_update, map_bounds}, 400)
socket = assign(socket, bounds_update_timer: timer_ref, pending_bounds: map_bounds)
{:noreply, socket}
end