Skip to content

Commit

Permalink
feat: Cache stop to route relationships
Browse files Browse the repository at this point in the history
Adds a Nebulex cache for stops -> routes to weaken the dependency on the
V3 API when querying the Alerts cache.
  • Loading branch information
sloanelybutsurely committed Aug 16, 2024
1 parent f53e952 commit 99cc65e
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
3 changes: 2 additions & 1 deletion lib/screens/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ defmodule Screens.Application do
Screens.OlCrowding.DynamicSupervisor,
{Screens.OlCrowding.Agent, %{}},
{Screens.ScreenApiResponseCache, []},
Screens.Streams.Alerts
Screens.Streams.Alerts,
Screens.Stops.StopsToRoutes
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
57 changes: 57 additions & 0 deletions lib/screens/stops/stops_to_routes.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Screens.Stops.StopsToRoutes do
@moduledoc """
Cache of stop ids to route ids. Information for stop ids missing from the
cache is fetched automatically from the V3 API when requested.
"""
use Nebulex.Cache,
otp_app: :screens,
adapter: Nebulex.Adapters.Local

@base_ttl :timer.hours(1)

@spec stops_to_routes([stop_id :: String.t()]) :: [route_id :: String.t()]
def stops_to_routes(stop_ids) do
from_cache = get_all(stop_ids)
missing_stop_ids = stop_ids -- Map.keys(from_cache)

from_api =
if Enum.empty?(missing_stop_ids) do
%{}
else
from_api =
for stop_id <- missing_stop_ids, into: %{} do
{:ok, routes} = fetch_routes_for_stops(stop_id)
route_ids = Enum.map(routes, & &1.id)

{stop_id, route_ids}
end

put_all(from_api, ttl: ttl())

from_api
end

[from_cache, from_api]
|> Enum.map(&ungroup_values/1)
|> Enum.concat()
|> Enum.uniq()
end

defp fetch_routes_for_stops(stop_ids) do
stop_impl = Application.get_env(:screens, :stops_to_routes_stop_mod, Screens.Stops.Stop)
stop_impl.fetch_routes_for_stops(stop_ids)
end

defp ungroup_values(map) do
map
|> Map.values()
|> List.flatten()
|> Enum.uniq()
end

# Set random TTLs from 1hr to 1.5hrs to alleviate the thundering herd problem
defp ttl do
additional_minutes = :rand.uniform(30)
@base_ttl + :timer.minutes(additional_minutes)
end
end

0 comments on commit 99cc65e

Please sign in to comment.