Skip to content

Commit

Permalink
page warming
Browse files Browse the repository at this point in the history
  • Loading branch information
APB9785 committed Nov 25, 2024
1 parent f0413a0 commit 68bae7f
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
26 changes: 25 additions & 1 deletion lib/beacon/boot.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ defmodule Beacon.Boot do
:ignore
end

def init(%{site: site, mode: :live}) when is_atom(site) do
def init(%{site: site, mode: :live} = config) when is_atom(site) do
Logger.info("Beacon.Boot booting site #{site}")
task_supervisor = Beacon.Registry.via({site, TaskSupervisor})

Expand All @@ -42,9 +42,33 @@ defmodule Beacon.Boot do
# TODO: revisit this timeout after we upgrade to Tailwind v4
Task.await_many(assets, :timer.minutes(5))

warm_pages(config)

# TODO: add telemetry to measure booting time
Logger.info("Beacon.Boot finished booting site #{site}")

:ignore
end

defp warm_pages(config) do
pages =
case config.page_warming do
{:shortest_paths, count} ->
Logger.info("Beacon.Boot warming pages - #{count} shortest paths")
Beacon.Content.list_published_pages(config.site, sort: {:length, :path}, limit: count)

{:specify_paths, paths} ->
Logger.info("Beacon.Boot warming pages - specified paths")
Beacon.Content.list_published_pages_for_paths(config.site, paths)

:none ->
Logger.info("Beacon.Boot page warming disabled")
[]
end

Enum.each(pages, fn page ->
Logger.info("Beacon.Boot warming page #{page.id} #{page.path}")
Beacon.Loader.load_page_module(config.site, page.id)
end)
end
end
23 changes: 19 additions & 4 deletions lib/beacon/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ defmodule Beacon.Config do
"""
@type default_meta_tags :: [%{binary() => binary()}]

@typedoc """
The strategy for pre-loading page modules at boot time.
"""
@type page_warming :: {:shortest_paths, integer()} | {:specify_paths, [String.t()]} | :none

@type t :: %__MODULE__{
site: Beacon.Types.Site.t(),
endpoint: endpoint(),
Expand All @@ -211,7 +216,8 @@ defmodule Beacon.Config do
lifecycle: lifecycle(),
extra_page_fields: extra_page_fields(),
extra_asset_fields: extra_asset_fields(),
default_meta_tags: default_meta_tags()
default_meta_tags: default_meta_tags(),
page_warming: page_warming()
}

@default_load_template [
Expand Down Expand Up @@ -257,7 +263,8 @@ defmodule Beacon.Config do
],
extra_page_fields: [],
extra_asset_fields: [],
default_meta_tags: []
default_meta_tags: [],
page_warming: {:shortest_paths, 10}

@type option ::
{:site, Beacon.Types.Site.t()}
Expand All @@ -277,6 +284,7 @@ defmodule Beacon.Config do
| {:extra_page_fields, extra_page_fields()}
| {:extra_asset_fields, extra_asset_fields()}
| {:default_meta_tags, default_meta_tags()}
| {:page_warming, page_warming()}

@doc """
Build a new `%Beacon.Config{}` instance to hold the entire configuration for each site.
Expand Down Expand Up @@ -324,6 +332,8 @@ defmodule Beacon.Config do
* `:default_meta_tags` - `t:default_meta_tags/0` (optional). Defaults to `%{}`.
* `:page_warming` - `t:page_warming/0` (optional). Defaults to `{:shortest_paths, 10}`.
## Example
iex> Beacon.Config.new(
Expand Down Expand Up @@ -352,7 +362,8 @@ defmodule Beacon.Config do
after_publish_page: [
notify_admin: fn page -> {:cont, MyApp.Admin.send_email(page)} end
]
]
],
page_warming: {:specify_paths, ["/", "/home", "/blog"]}
)
%Beacon.Config{
site: :my_site,
Expand Down Expand Up @@ -400,7 +411,8 @@ defmodule Beacon.Config do
],
extra_page_fields: [],
extra_asset_fields: [],
default_meta_tags: []
default_meta_tags: [],
page_warming: {:specify_paths, ["/", "/home", "/blog"]}
}
"""
Expand Down Expand Up @@ -442,6 +454,8 @@ defmodule Beacon.Config do
default_meta_tags = Keyword.get(opts, :default_meta_tags, [])
extra_asset_fields = Keyword.get(opts, :extra_asset_fields, [{"image/*", [Beacon.MediaLibrary.AssetFields.AltText]}])

page_warming = Keyword.get(opts, :page_warming, {:shortest_paths, 10})

opts =
opts
|> Keyword.put(:tailwind_config, ensure_tailwind_config(opts[:tailwind_config]))
Expand All @@ -452,6 +466,7 @@ defmodule Beacon.Config do
|> Keyword.put(:assets, assets)
|> Keyword.put(:default_meta_tags, default_meta_tags)
|> Keyword.put(:extra_asset_fields, extra_asset_fields)
|> Keyword.put(:page_warming, page_warming)

struct!(__MODULE__, opts)
end
Expand Down
15 changes: 15 additions & 0 deletions lib/beacon/content.ex
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,20 @@ defmodule Beacon.Content do
|> Enum.map(&extract_page_snapshot/1)
end

@doc """
Similar to `list_published_pages/2`, but does not accept any options. Instead, provide a list
of paths, and this function will return any published pages which match one of those paths.
"""
@doc type: :pages
@spec list_published_pages_for_paths(Site.t(), [String.t()]) :: [Page.t()]
def list_published_pages_for_paths(site, paths) do
site
|> query_list_published_pages_base()
|> then(fn query -> from(q in query, where: q.path in ^paths) end)
|> repo(site).all()
|> Enum.map(&extract_page_snapshot/1)
end

defp query_list_published_pages_base(site) do
events =
from event in PageEvent,
Expand Down Expand Up @@ -1026,6 +1040,7 @@ defmodule Beacon.Content do

defp query_list_published_pages_search(query, _search), do: query

defp query_list_published_pages_sort(query, {:length, key}), do: from(q in query, order_by: [{:asc, fragment("length(?)", field(q, ^key))}])
defp query_list_published_pages_sort(query, sort), do: from(q in query, order_by: [asc: ^sort])

@doc """
Expand Down
25 changes: 25 additions & 0 deletions test/beacon/content_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,31 @@ defmodule Beacon.ContentTest do
assert [%Page{path: "/with-tags"}] = Content.list_published_pages(:my_site, search: %{extra: %{"tags" => "tag1"}})
end

test "list_published_pages sort by path length" do
beacon_published_page_fixture(path: "/")
beacon_published_page_fixture(path: "/foo")
beacon_published_page_fixture(path: "/a")

assert [
%Page{path: "/"},
%Page{path: "/a"},
%Page{path: "/foo"}
] = Content.list_published_pages(:my_site, sort: {:length, :path})
end

test "list_published_pages_for_paths/2" do
beacon_published_page_fixture(path: "/foo")
beacon_published_page_fixture(path: "/bar")
beacon_published_page_fixture(path: "/baz")
beacon_published_page_fixture(path: "/bong")
beacon_page_fixture(path: "/unpublished")

assert [
%Page{path: "/bar"},
%Page{path: "/baz"}
] = Content.list_published_pages_for_paths(:my_site, ["/bar", "/baz", "/unpublished"])
end

test "list_page_events" do
page = beacon_page_fixture()
Content.publish_page(page)
Expand Down

0 comments on commit 68bae7f

Please sign in to comment.