Remove a significant number of pageload queries

This commit is contained in:
byte[] 2021-11-07 21:21:24 -05:00
parent 45196e2619
commit 8c9216ff53
14 changed files with 214 additions and 202 deletions

View file

@ -165,16 +165,6 @@ defmodule Philomena.ArtistLinks do
ArtistLink.changeset(artist_link, %{}) ArtistLink.changeset(artist_link, %{})
end end
def count_artist_links(user) do
if Canada.Can.can?(user, :index, %ArtistLink{}) do
ArtistLink
|> where([ul], ul.aasm_state in ^["unverified", "link_verified", "contacted"])
|> Repo.aggregate(:count, :id)
else
nil
end
end
defp fetch_tag(name) do defp fetch_tag(name) do
Tag Tag
|> preload(:aliased_tag) |> preload(:aliased_tag)

View file

@ -111,14 +111,4 @@ defmodule Philomena.DnpEntries do
def change_dnp_entry(%DnpEntry{} = dnp_entry) do def change_dnp_entry(%DnpEntry{} = dnp_entry) do
DnpEntry.changeset(dnp_entry, %{}) DnpEntry.changeset(dnp_entry, %{})
end end
def count_dnp_entries(user) do
if Canada.Can.can?(user, :index, DnpEntry) do
DnpEntry
|> where([dnp], dnp.aasm_state in ["requested", "claimed", "acknowledged"])
|> Repo.aggregate(:count, :id)
else
nil
end
end
end end

View file

@ -173,14 +173,4 @@ defmodule Philomena.DuplicateReports do
def change_duplicate_report(%DuplicateReport{} = duplicate_report) do def change_duplicate_report(%DuplicateReport{} = duplicate_report) do
DuplicateReport.changeset(duplicate_report, %{}) DuplicateReport.changeset(duplicate_report, %{})
end end
def count_duplicate_reports(user) do
if Canada.Can.can?(user, :index, DuplicateReport) do
DuplicateReport
|> where(state: "open")
|> Repo.aggregate(:count, :id)
else
nil
end
end
end end

24
lib/philomena/layouts.ex Normal file
View file

@ -0,0 +1,24 @@
defmodule Philomena.Layouts do
@moduledoc """
The Layouts context.
"""
import Ecto.Query, warn: false
alias Philomena.Repo
alias Philomena.Layouts.Layout
@doc """
Gets a single layout.
## Examples
iex> get_layout!()
%Layout{}
"""
@spec get_layout!() :: Layout.t()
def get_layout! do
Repo.one!(Layout)
end
end

View file

@ -0,0 +1,18 @@
defmodule Philomena.Layouts.Layout do
use Ecto.Schema
alias Philomena.SiteNotices.SiteNotice
alias Philomena.Forums.Forum
@primary_key false
schema "layouts" do
field :artist_link_count, :integer
field :channel_count, :integer
field :dnp_entry_count, :integer
field :duplicate_report_count, :integer
field :report_count, :integer
embeds_many :site_notices, SiteNotice
embeds_many :forums, Forum
end
end

View file

@ -165,14 +165,4 @@ defmodule Philomena.Reports do
|> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type]) |> Polymorphic.load_polymorphic(reportable: [reportable_id: :reportable_type])
|> Enum.map(&Elasticsearch.index_document(&1, Report)) |> Enum.map(&Elasticsearch.index_document(&1, Report))
end end
def count_reports(user) do
if Canada.Can.can?(user, :index, Report) do
Report
|> where(open: true)
|> Repo.aggregate(:count, :id)
else
nil
end
end
end end

View file

@ -1,48 +0,0 @@
defmodule PhilomenaWeb.AdminCountersPlug do
@moduledoc """
This plug stores the counts used by the admin bar.
## Example
plug PhilomenaWeb.AdminCountersPlug
"""
alias Philomena.DuplicateReports
alias Philomena.Reports
alias Philomena.ArtistLinks
alias Philomena.DnpEntries
import Plug.Conn, only: [assign: 3]
@doc false
@spec init(any()) :: any()
def init(opts), do: opts
@doc false
@spec call(Plug.Conn.t()) :: Plug.Conn.t()
def call(conn), do: call(conn, nil)
@doc false
@spec call(Plug.Conn.t(), any()) :: Plug.Conn.t()
def call(conn, _opts) do
user = conn.assigns.current_user
maybe_assign_admin_metrics(conn, user, staff?(user))
end
defp maybe_assign_admin_metrics(conn, _user, false), do: conn
defp maybe_assign_admin_metrics(conn, user, true) do
duplicate_reports = DuplicateReports.count_duplicate_reports(user)
reports = Reports.count_reports(user)
artist_links = ArtistLinks.count_artist_links(user)
dnps = DnpEntries.count_dnp_entries(user)
conn
|> assign(:duplicate_report_count, duplicate_reports)
|> assign(:report_count, reports)
|> assign(:artist_link_count, artist_links)
|> assign(:dnp_entry_count, dnps)
end
defp staff?(%{role: role}) when role in ["assistant", "moderator", "admin"], do: true
defp staff?(_user), do: false
end

View file

@ -1,18 +0,0 @@
defmodule PhilomenaWeb.ChannelPlug do
alias Plug.Conn
alias Philomena.Channels.Channel
alias Philomena.Repo
import Ecto.Query
def init([]), do: []
def call(conn, _opts) do
live_channels =
Channel
|> where(is_live: true)
|> Repo.aggregate(:count, :id)
conn
|> Conn.assign(:live_channels, live_channels)
end
end

View file

@ -1,25 +0,0 @@
defmodule PhilomenaWeb.ForumListPlug do
alias Plug.Conn
alias Philomena.Forums.Forum
alias Philomena.Repo
alias Canada.Can
import Ecto.Query
def init(opts), do: opts
def call(conn, _opts) do
forums = lookup_visible_forums(conn.assigns.current_user)
conn
|> Conn.assign(:forums, forums)
end
# fixme: add caching!
defp lookup_visible_forums(user) do
Forum
|> order_by(asc: :name)
|> Repo.all()
|> Enum.filter(&Can.can?(user, :show, &1))
end
end

View file

@ -0,0 +1,43 @@
defmodule PhilomenaWeb.LayoutPlug do
@moduledoc """
This plug stores the current site-wide layout attributes.
## Example
plug PhilomenaWeb.LayoutPlug
"""
alias Canada.Can
alias Philomena.Layouts
import Plug.Conn
@doc false
@spec init(any()) :: any()
def init(opts), do: opts
@doc false
@spec call(Plug.Conn.t(), any()) :: Plug.Conn.t()
def call(conn, _opts) do
user = conn.assigns.current_user
layout = Layouts.get_layout!()
conn
|> assign(:artist_link_count, layout.duplicate_report_count)
|> assign(:dnp_entry_count, layout.dnp_entry_count)
|> assign(:duplicate_report_count, layout.duplicate_report_count)
|> assign(:live_channels, layout.channel_count)
|> assign(:report_count, layout.report_count)
|> assign(:forums, visible_forums(user, layout.forums))
|> assign(:site_notices, site_notices(layout.site_notices))
end
defp visible_forums(user, forum_list) do
forum_list
|> Enum.filter(&Can.can?(user, :show, &1))
|> Enum.sort_by(& &1.name)
end
defp site_notices(notice_list) do
Enum.sort_by(notice_list, & &1.start_date, Date)
end
end

View file

@ -1,25 +0,0 @@
defmodule PhilomenaWeb.SiteNoticePlug do
@moduledoc """
This plug stores the current site-wide notices.
## Example
plug PhilomenaWeb.SiteNoticePlug
"""
alias Plug.Conn
alias Philomena.SiteNotices
@doc false
@spec init(any()) :: any()
def init(opts), do: opts
@doc false
@spec call(Plug.Conn.t(), any()) :: Plug.Conn.t()
def call(conn, _opts) do
notices = SiteNotices.active_site_notices()
conn
|> Conn.assign(:site_notices, notices)
end
end

View file

@ -17,11 +17,8 @@ defmodule PhilomenaWeb.Router do
plug PhilomenaWeb.EnsureUserEnabledPlug plug PhilomenaWeb.EnsureUserEnabledPlug
plug PhilomenaWeb.CurrentBanPlug plug PhilomenaWeb.CurrentBanPlug
plug PhilomenaWeb.NotificationCountPlug plug PhilomenaWeb.NotificationCountPlug
plug PhilomenaWeb.SiteNoticePlug plug PhilomenaWeb.LayoutPlug
plug PhilomenaWeb.ForumListPlug
plug PhilomenaWeb.FilterSelectPlug plug PhilomenaWeb.FilterSelectPlug
plug PhilomenaWeb.ChannelPlug
plug PhilomenaWeb.AdminCountersPlug
end end
pipeline :api do pipeline :api do

View file

@ -0,0 +1,36 @@
defmodule Philomena.Repo.Migrations.AddLayouts do
use Ecto.Migration
def change do
execute(
"""
CREATE VIEW layouts AS
WITH
artist_link_count AS (SELECT COUNT(*) FROM artist_links WHERE aasm_state IN ('unverified', 'link_verified', 'contacted')),
channel_count AS (SELECT COUNT(*) FROM channels WHERE is_live='t'),
duplicate_report_count AS (SELECT COUNT(*) FROM duplicate_reports WHERE state='open'),
dnp_entry_count AS (SELECT COUNT(*) FROM dnp_entries WHERE aasm_state IN ('requested', 'claimed', 'acknowledged')),
report_count AS (SELECT COUNT(*) FROM reports WHERE open='t'),
forums AS (SELECT array_agg(row_to_json(f)) AS array FROM forums f),
site_notices AS (SELECT array_agg(row_to_json(sn)) AS array FROM site_notices sn WHERE start_date <= now() AND finish_date > now())
SELECT
artist_link_count.count AS artist_link_count,
channel_count.count AS channel_count,
dnp_entry_count.count AS dnp_entry_count,
duplicate_report_count.count AS duplicate_report_count,
report_count.count AS report_count,
forums.array AS forums,
site_notices.array AS site_notices
FROM
artist_link_count,
channel_count,
duplicate_report_count,
dnp_entry_count,
report_count,
forums,
site_notices
""",
"DROP VIEW layouts"
)
end
end

View file

@ -984,6 +984,97 @@ CREATE SEQUENCE public.images_id_seq
ALTER SEQUENCE public.images_id_seq OWNED BY public.images.id; ALTER SEQUENCE public.images_id_seq OWNED BY public.images.id;
--
-- Name: reports; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.reports (
id integer NOT NULL,
ip inet NOT NULL,
fingerprint character varying,
user_agent character varying DEFAULT ''::character varying,
referrer character varying DEFAULT ''::character varying,
reason_textile character varying DEFAULT ''::character varying NOT NULL,
state character varying DEFAULT 'open'::character varying NOT NULL,
open boolean DEFAULT true NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
user_id integer,
admin_id integer,
reportable_id integer NOT NULL,
reportable_type character varying NOT NULL,
reason character varying NOT NULL
);
--
-- Name: site_notices; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.site_notices (
id integer NOT NULL,
title character varying NOT NULL,
text character varying NOT NULL,
link character varying NOT NULL,
link_text character varying NOT NULL,
live boolean DEFAULT false NOT NULL,
start_date timestamp without time zone NOT NULL,
finish_date timestamp without time zone NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
user_id integer NOT NULL
);
--
-- Name: layouts; Type: VIEW; Schema: public; Owner: -
--
CREATE VIEW public.layouts AS
WITH artist_link_count AS (
SELECT count(*) AS count
FROM public.artist_links
WHERE ((artist_links.aasm_state)::text = ANY ((ARRAY['unverified'::character varying, 'link_verified'::character varying, 'contacted'::character varying])::text[]))
), channel_count AS (
SELECT count(*) AS count
FROM public.channels
WHERE (channels.is_live = true)
), duplicate_report_count AS (
SELECT count(*) AS count
FROM public.duplicate_reports
WHERE ((duplicate_reports.state)::text = 'open'::text)
), dnp_entry_count AS (
SELECT count(*) AS count
FROM public.dnp_entries
WHERE ((dnp_entries.aasm_state)::text = ANY ((ARRAY['requested'::character varying, 'claimed'::character varying, 'acknowledged'::character varying])::text[]))
), report_count AS (
SELECT count(*) AS count
FROM public.reports
WHERE (reports.open = true)
), forums AS (
SELECT array_agg(row_to_json(f.*)) AS "array"
FROM public.forums f
), site_notices AS (
SELECT array_agg(row_to_json(sn.*)) AS "array"
FROM public.site_notices sn
WHERE ((sn.start_date <= now()) AND (sn.finish_date > now()))
)
SELECT artist_link_count.count AS artist_link_count,
channel_count.count AS channel_count,
dnp_entry_count.count AS dnp_entry_count,
duplicate_report_count.count AS duplicate_report_count,
report_count.count AS report_count,
forums."array" AS forums,
site_notices."array" AS site_notices
FROM artist_link_count,
channel_count,
duplicate_report_count,
dnp_entry_count,
report_count,
forums,
site_notices;
-- --
-- Name: messages; Type: TABLE; Schema: public; Owner: - -- Name: messages; Type: TABLE; Schema: public; Owner: -
-- --
@ -1238,29 +1329,6 @@ CREATE SEQUENCE public.posts_id_seq
ALTER SEQUENCE public.posts_id_seq OWNED BY public.posts.id; ALTER SEQUENCE public.posts_id_seq OWNED BY public.posts.id;
--
-- Name: reports; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.reports (
id integer NOT NULL,
ip inet NOT NULL,
fingerprint character varying,
user_agent character varying DEFAULT ''::character varying,
referrer character varying DEFAULT ''::character varying,
reason_textile character varying DEFAULT ''::character varying NOT NULL,
state character varying DEFAULT 'open'::character varying NOT NULL,
open boolean DEFAULT true NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
user_id integer,
admin_id integer,
reportable_id integer NOT NULL,
reportable_type character varying NOT NULL,
reason character varying NOT NULL
);
-- --
-- Name: reports_id_seq; Type: SEQUENCE; Schema: public; Owner: - -- Name: reports_id_seq; Type: SEQUENCE; Schema: public; Owner: -
-- --
@ -1323,25 +1391,6 @@ CREATE TABLE public.schema_migrations (
); );
--
-- Name: site_notices; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.site_notices (
id integer NOT NULL,
title character varying NOT NULL,
text character varying NOT NULL,
link character varying NOT NULL,
link_text character varying NOT NULL,
live boolean DEFAULT false NOT NULL,
start_date timestamp without time zone NOT NULL,
finish_date timestamp without time zone NOT NULL,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
user_id integer NOT NULL
);
-- --
-- Name: site_notices_id_seq; Type: SEQUENCE; Schema: public; Owner: - -- Name: site_notices_id_seq; Type: SEQUENCE; Schema: public; Owner: -
-- --
@ -4867,3 +4916,4 @@ INSERT INTO public."schema_migrations" (version) VALUES (20210912171343);
INSERT INTO public."schema_migrations" (version) VALUES (20210917190346); INSERT INTO public."schema_migrations" (version) VALUES (20210917190346);
INSERT INTO public."schema_migrations" (version) VALUES (20210921025336); INSERT INTO public."schema_migrations" (version) VALUES (20210921025336);
INSERT INTO public."schema_migrations" (version) VALUES (20210929181319); INSERT INTO public."schema_migrations" (version) VALUES (20210929181319);
INSERT INTO public."schema_migrations" (version) VALUES (20211108003620);