Skip to content

Reuse shared Polyline cache for focused shapes; drop decoded double-cache (#1908)#1919

Merged
bmander merged 1 commit into
mainfrom
fix/1908-shape-double-cache
Jul 17, 2026
Merged

Reuse shared Polyline cache for focused shapes; drop decoded double-cache (#1908)#1919
bmander merged 1 commit into
mainfrom
fix/1908-shape-double-cache

Conversation

@bmander

@bmander bmander commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Addresses item 2 of #1908 (medium-severity debt review finding 8) — the first of three separate PRs closing that issue.

Problem

A focused trip's shape was cached twice under the same shapeId, with divergent policies:

  • TripObservationRepository's ShapeCachePolyline, LRU 100, no TTL (GTFS shapes are immutable; rationale documented in ShapeCache.kt).
  • FocusedTripRepository.shapeCache — decoded List<GeoPoint>, LRU 32, with an unexplained 10-minute TTL on effectively-immutable data.

ensureShape() already returns the shared, deduped Polyline (coalescing the network fetch via its own SingleFlight), so the decoded cache was a redundant second copy: ~2× memory per focused shape, a second eviction policy, and a freshness threshold that contradicts the immutability rationale one layer down.

Change

  • Drop FocusedTripRepository.shapeCache; fetchShape decodes on demand from the shared Polyline cache. The Location → GeoPoint map is cheap and runs on a focus change, not the per-frame render path.
  • Keep the fetch-concurrency throttle (shapePermits) and same-shape decode coalescing (shapeFetches) — those are concurrency controls, not caching, so behavior beyond the removed cache is unchanged.
  • routeStopCache and its ExpiringLruCache/TTL are untouched here (the stops-for-route cache is item 1 of Focused-trip data layer: stops-for-route fetched via two paths, shapes double-cached, focusedTrips in three copies #1908, a separate PR).

Tests

The cross-call shape memo now lives entirely in TripObservationRepository (already covered by TripObservationRepositoryTest). The FocusedTripRepositoryTest case that asserted FocusedTripRepository's own memo is reframed to document the delegation. The within-getGeometry shared-shape dedup test is unchanged and still passes.

  • ./gradlew :onebusaway-android:compileObaGoogleDebugKotlin -PwarningsAsErrors=true — clean
  • ./gradlew :onebusaway-android:testObaGoogleDebugUnitTest --tests '*FocusedTripRepositoryTest' --tests '*TripObservationRepositoryTest' --tests '*ShapeCacheTest' — green

Follow-ups (same issue, separate PRs)

  • Item 1: one caching stops-for-route repository both the map and route-info paths project from.
  • Item 3: derive the focused-trip set from the focus state flow (remove the scattered manual resets).

🤖 Generated with Claude Code

…ache (#1908)

A focused trip's shape was cached twice under the same shapeId with divergent
policies: TripObservationRepository's ShapeCache (Polyline, LRU 100, no TTL —
GTFS shapes are immutable) and FocusedTripRepository.shapeCache (decoded
List<GeoPoint>, LRU 32, with an unexplained 10-minute TTL on immutable data).

ensureShape() already returns the shared, deduped Polyline (coalescing the
network fetch via its own SingleFlight), so the decoded cache was a redundant
second copy (~2x memory, a second eviction policy, and the stray TTL). Drop it
and decode on demand from the shared Polyline cache; the Location -> GeoPoint
map is cheap and runs on a focus change, not the per-frame path. The fetch
concurrency throttle (shapePermits) and same-shape decode coalescing
(shapeFetches) are unchanged.

The cross-call shape memo now lives entirely in TripObservationRepository
(covered by TripObservationRepositoryTest); reframe the FocusedTripRepository
test that asserted its own memo to document the delegation.

Part of #1908 (finding 8, item 2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bmander
bmander merged commit 29b01c2 into main Jul 17, 2026
2 checks passed
@bmander
bmander deleted the fix/1908-shape-double-cache branch July 17, 2026 02:35
bmander added a commit that referenced this pull request Jul 17, 2026
FocusedTripRepository.kt conflicted because main (#1919) dropped the
local shape cache while this branch dropped the local route-stop
cache — after merging, neither ExpiringLruCache instance is used
(routing now flows through StopsForRouteRepository's own cache,
shapes through TripObservationRepository's shared Polyline cache), so
the shared cache/now/cacheSize/cacheTtl plumbing is dead and removed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant