diff --git a/config/config.exs b/config/config.exs index 762e36f1f..080d2aa16 100644 --- a/config/config.exs +++ b/config/config.exs @@ -486,6 +486,7 @@ config :screens, Screens.ScreenApiResponseCache, allocated_memory: 250_000_000 config :screens, Screens.Stops.StopsToRoutes, adapter: Nebulex.Adapters.Local +config :screens, Screens.Routes.RoutesCache, adapter: Nebulex.Adapters.Local # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/config/test.exs b/config/test.exs index 31ccf79b9..01855c5c4 100644 --- a/config/test.exs +++ b/config/test.exs @@ -27,7 +27,7 @@ config :screens, blue_bikes_station_status_url: [:no_api_requests_allowed_during_testing], blue_bikes_api_client: Screens.BlueBikes.FakeClient, stops_to_routes_route_mod: Screens.Routes.Route.Mock, - alerts_cache_filter_route_mod: Screens.Routes.Route.Mock, + routes_cache_route_mod: Screens.Routes.Route.Mock, dup_headsign_replacements: %{ "Test 1" => "T1" }, @@ -164,3 +164,4 @@ config :screens, :screens_by_alert, screens_ttl_seconds: 1 config :screens, Screens.Stops.StopsToRoutes, adapter: Nebulex.Adapters.Nil +config :screens, Screens.Routes.RoutesCache, adapter: Nebulex.Adapters.Nil diff --git a/lib/screens/alerts/cache/filter.ex b/lib/screens/alerts/cache/filter.ex index 40ee875aa..e496bab78 100644 --- a/lib/screens/alerts/cache/filter.ex +++ b/lib/screens/alerts/cache/filter.ex @@ -3,11 +3,10 @@ defmodule Screens.Alerts.Cache.Filter do Logic to apply filters to a list of `Screens.Alerts.Alert` structs. """ alias Screens.Routes.Route + alias Screens.Routes.RoutesCache alias Screens.RouteType alias Screens.Stops.StopsToRoutes - @route_mod Application.compile_env(:screens, :alerts_cache_filter_route_mod, Route) - @default_activities ~w[BOARD EXIT RIDE] @type filter_opts() :: %{ @@ -127,8 +126,8 @@ defmodule Screens.Alerts.Cache.Filter do end defp matchers_for_route_id(route_id) do - case @route_mod.by_id(route_id) do - {:ok, %Route{type: type}} -> + case RoutesCache.by_id(route_id) do + %Route{type: type} -> [ %{ route_type: RouteType.to_id(type), diff --git a/lib/screens/application.ex b/lib/screens/application.ex index 3630957d9..2fdf4841b 100644 --- a/lib/screens/application.ex +++ b/lib/screens/application.ex @@ -38,7 +38,8 @@ defmodule Screens.Application do {Screens.OlCrowding.Agent, %{}}, {Screens.ScreenApiResponseCache, []}, Screens.Streams.Alerts, - Screens.Stops.StopsToRoutes + Screens.Stops.StopsToRoutes, + Screens.Routes.RoutesCache ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/lib/screens/routes/routes_cache.ex b/lib/screens/routes/routes_cache.ex new file mode 100644 index 000000000..0222319b9 --- /dev/null +++ b/lib/screens/routes/routes_cache.ex @@ -0,0 +1,39 @@ +defmodule Screens.Routes.RoutesCache do + @moduledoc """ + A read-through cache of routes by ID. + """ + use Nebulex.Cache, + otp_app: :screens, + adapter: Application.compile_env(:screens, [__MODULE__, :adapter]) + + alias Screens.Routes.Route + + @route_mod Application.compile_env(:screens, :routes_cache_route_mod, Screens.Routes.Route) + + @base_ttl :timer.hours(1) + + @spec by_id(id :: String.t()) :: Route.t() | nil + def by_id(id) do + if route = get(id) do + route + else + route = fetch_by_id(id) + + unless is_nil(route), do: put(id, route, ttl: ttl()) + + route + end + end + + defp fetch_by_id(id) do + case @route_mod.by_id(id) do + {:ok, %Route{} = route} -> route + _ -> nil + end + end + + def ttl do + additional_minutes = :rand.uniform(30) + @base_ttl + :timer.minutes(additional_minutes) + end +end