Skip to content

perf: Prevent resource exhaustion from ghost requests via deep context cancellation #406

@ARCoder181105

Description

@ARCoder181105

The Problem
Currently, many of our data-heavy API endpoints (especially location-based searches, schedule builders, and hierarchical reference mappers) perform synchronous operations inside large for loops. These operations include heavy math (haversine spatial calculations), sequential database queries, and massive JSON array allocations.

When a client drops their connection early (e.g., closing the mobile app, navigating away, or hitting a timeout), Go automatically cancels the http.Request context. However, because we do not explicitly check this context state deep inside our heavy for loops, the goroutine continues executing these massive calculations for a client that is no longer there.

The Impact
These "ghost requests" needlessly consume CPU and memory. Under high load or during a sudden burst of canceled requests (such as a user panning around a map rapidly), this can lead to severe resource exhaustion, garbage collection pressure, and latency spikes for active users.

Proposed Solution
Implement the "Load Shedding" pattern by adding Deep Context Cancellation checks. We should inject the following check at the top of the heaviest iteration loops and right before massive array filtering operations:

if ctx.Err() != nil {
    return // Client disconnected, abandon heavy calculations instantly
}

Affected Areas / Implementation Checklist:
This pattern needs to be applied to the heavy loops in the following files:

  • internal/gtfs/gtfs_manager.go
  • internal/restapi/arrivals_and_departure_for_stop.go
  • internal/restapi/block_handler.go
  • internal/restapi/reference_utils.go
  • internal/restapi/route_search_handler.go
  • internal/restapi/routes_for_location_handler.go
  • internal/restapi/schedule_for_route_handler.go
  • internal/restapi/schedule_for_stop_handler.go
  • internal/restapi/search_stops_handler.go
  • internal/restapi/shapes_handler.go
  • internal/restapi/stops_for_agency_handler.go
  • internal/restapi/stops_for_location_handler.go
  • internal/restapi/stops_for_route_handler.go
  • internal/restapi/trip_details_handler.go
  • internal/restapi/trips_for_location_handler.go
  • internal/restapi/trips_for_route_handler.go
  • internal/restapi/vehicles_for_agency_handler.go

Implementing this will make the API dramatically more resilient under load and ensure we instantly free up threads and memory the millisecond a client disconnects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions