feat: reduce backend memory by 300+ MB via on-demand SQLite stop_times#100
Merged
Conversation
…loading The _stop_times dict was the single largest memory consumer, holding ~500k entries (~300 MB) permanently in RAM. Now it's loaded from the existing SQLite cache only when get_stop_departures() is called, freeing ~300 MB of baseline memory. Changes: - Move _stop_times from in-memory Cache to on-demand SQLite queries - Add load_stop_times_for_urls() to gtfs_db.py for fetching stop_times by feed URL from the SQLite cache - Make get_db_path() public so Cache can reference it - Add recursive _deep_size() estimator for per-component memory tracking - Expose memory_estimate in /health endpoint - Update FakeCache in tests to match new interface
Contributor
|
🚅 Deployed to the bus-tracker-pr-100 environment in grt-bus-tracker
1 service not affected by this PR
|
railway-app
Bot
temporarily deployed
to
grt-bus-tracker / bus-tracker-pr-100
June 18, 2026 13:17
Destroyed
railway-app
Bot
temporarily deployed
to
grt-bus-tracker / bus-tracker-pr-100
June 18, 2026 13:18
Destroyed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The backend was using ~600 MB of RAM — ~300 MB of which was the
_stop_timesdict permanently in memory. This dict maps every trip ID to its list of stop-time entries, and with GRT + LRT data that's roughly 500k entries stored as Python dicts.Solution
Stop keeping
_stop_timesin the in-memory Cache. Instead, load it from the existing SQLite cache on demand whenget_stop_departures()is called. The SQLite DB already stores the full stop_times JSON blob — we just weren't using it as the live data source.Changes
services/gtfs_db.py— Addedload_stop_times_for_urls()to query stop_times from SQLite by feed URL; madeget_db_path()publicservices/cache.py— Removedself._stop_times, stop_times collection inrefresh_once(), and the deep-copy inget_stop_departures(); now loads from SQLite on demand instead; added_deep_size()estimator andget_memory_estimate()methodapi/routes.py— Addedmemory_estimatefield to health responsetests/test_api.py— UpdatedFakeCacheto match new interfaceTrade-off
get_stop_departures()now does a SQLite read + JSON parse per request instead of accessing pre-loaded dicts. This adds ~50-150ms latency to that endpoint but removes ~300 MB from baseline memory. Since stop departures is a user-initiated endpoint (not polled), this is a worthwhile trade.Impact