defmodule Pleroma.LoadTesting.Fetcher do
- use Pleroma.LoadTesting.Helper
+ alias Pleroma.Activity
+ alias Pleroma.Pagination
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.MastodonAPI.MastodonAPI
+ alias Pleroma.Web.MastodonAPI.StatusView
- def fetch_user(user) do
- Benchee.run(%{
- "By id" => fn -> Repo.get_by(User, id: user.id) end,
- "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end,
- "By email" => fn -> Repo.get_by(User, email: user.email) end,
- "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end
- })
+ @spec run_benchmarks(User.t()) :: any()
+ def run_benchmarks(user) do
+ fetch_user(user)
+ fetch_timelines(user)
+ render_views(user)
end
- def query_timelines(user) do
- home_timeline_params = %{
- "count" => 20,
- "with_muted" => true,
- "type" => ["Create", "Announce"],
+ defp formatters do
+ [
+ Benchee.Formatters.Console
+ ]
+ end
+
+ defp fetch_user(user) do
+ Benchee.run(
+ %{
+ "By id" => fn -> Repo.get_by(User, id: user.id) end,
+ "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end,
+ "By email" => fn -> Repo.get_by(User, email: user.email) end,
+ "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp fetch_timelines(user) do
+ fetch_home_timeline(user)
+ fetch_direct_timeline(user)
+ fetch_public_timeline(user)
+ fetch_public_timeline(user, :local)
+ fetch_public_timeline(user, :tag)
+ fetch_notifications(user)
+ fetch_favourites(user)
+ fetch_long_thread(user)
+ fetch_timelines_with_reply_filtering(user)
+ end
+
+ defp render_views(user) do
+ render_timelines(user)
+ render_long_thread(user)
+ end
+
+ defp opts_for_home_timeline(user) do
+ %{
"blocking_user" => user,
+ "count" => "20",
"muting_user" => user,
- "user" => user
+ "type" => ["Create", "Announce"],
+ "user" => user,
+ "with_muted" => "true"
}
+ end
- mastodon_public_timeline_params = %{
- "count" => 20,
- "local_only" => true,
- "only_media" => "false",
+ defp fetch_home_timeline(user) do
+ opts = opts_for_home_timeline(user)
+
+ recipients = [user.ap_id | User.following(user)]
+
+ first_page_last =
+ ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
+
+ second_page_last =
+ ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", first_page_last.id))
+ |> Enum.reverse()
+ |> List.last()
+
+ third_page_last =
+ ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", second_page_last.id))
+ |> Enum.reverse()
+ |> List.last()
+
+ forth_page_last =
+ ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", third_page_last.id))
+ |> Enum.reverse()
+ |> List.last()
+
+ Benchee.run(
+ %{
+ "home timeline" => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
+ },
+ inputs: %{
+ "1 page" => opts,
+ "2 page" => Map.put(opts, "max_id", first_page_last.id),
+ "3 page" => Map.put(opts, "max_id", second_page_last.id),
+ "4 page" => Map.put(opts, "max_id", third_page_last.id),
+ "5 page" => Map.put(opts, "max_id", forth_page_last.id),
+ "1 page only media" => Map.put(opts, "only_media", "true"),
+ "2 page only media" =>
+ Map.put(opts, "max_id", first_page_last.id) |> Map.put("only_media", "true"),
+ "3 page only media" =>
+ Map.put(opts, "max_id", second_page_last.id) |> Map.put("only_media", "true"),
+ "4 page only media" =>
+ Map.put(opts, "max_id", third_page_last.id) |> Map.put("only_media", "true"),
+ "5 page only media" =>
+ Map.put(opts, "max_id", forth_page_last.id) |> Map.put("only_media", "true")
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp opts_for_direct_timeline(user) do
+ %{
+ :visibility => "direct",
+ "blocking_user" => user,
+ "count" => "20",
+ "type" => "Create",
+ "user" => user,
+ "with_muted" => "true"
+ }
+ end
+
+ defp fetch_direct_timeline(user) do
+ recipients = [user.ap_id]
+
+ opts = opts_for_direct_timeline(user)
+
+ first_page_last =
+ recipients
+ |> ActivityPub.fetch_activities_query(opts)
+ |> Pagination.fetch_paginated(opts)
+ |> List.last()
+
+ opts2 = Map.put(opts, "max_id", first_page_last.id)
+
+ second_page_last =
+ recipients
+ |> ActivityPub.fetch_activities_query(opts2)
+ |> Pagination.fetch_paginated(opts2)
+ |> List.last()
+
+ opts3 = Map.put(opts, "max_id", second_page_last.id)
+
+ third_page_last =
+ recipients
+ |> ActivityPub.fetch_activities_query(opts3)
+ |> Pagination.fetch_paginated(opts3)
+ |> List.last()
+
+ opts4 = Map.put(opts, "max_id", third_page_last.id)
+
+ forth_page_last =
+ recipients
+ |> ActivityPub.fetch_activities_query(opts4)
+ |> Pagination.fetch_paginated(opts4)
+ |> List.last()
+
+ Benchee.run(
+ %{
+ "direct timeline" => fn opts ->
+ ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
+ end
+ },
+ inputs: %{
+ "1 page" => opts,
+ "2 page" => opts2,
+ "3 page" => opts3,
+ "4 page" => opts4,
+ "5 page" => Map.put(opts4, "max_id", forth_page_last.id)
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp opts_for_public_timeline(user) do
+ %{
"type" => ["Create", "Announce"],
- "with_muted" => "true",
+ "local_only" => false,
"blocking_user" => user,
"muting_user" => user
}
+ end
- mastodon_federated_timeline_params = %{
- "count" => 20,
- "only_media" => "false",
+ defp opts_for_public_timeline(user, :local) do
+ %{
"type" => ["Create", "Announce"],
- "with_muted" => "true",
+ "local_only" => true,
"blocking_user" => user,
"muting_user" => user
}
+ end
- Benchee.run(%{
- "User home timeline" => fn ->
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities(
- [user.ap_id | user.following],
- home_timeline_params
- )
- end,
- "User mastodon public timeline" => fn ->
- Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
- mastodon_public_timeline_params
- )
- end,
- "User mastodon federated public timeline" => fn ->
- Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
- mastodon_federated_timeline_params
- )
- end
- })
-
- home_activities =
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities(
- [user.ap_id | user.following],
- home_timeline_params
- )
+ defp opts_for_public_timeline(user, :tag) do
+ %{
+ "blocking_user" => user,
+ "count" => "20",
+ "local_only" => nil,
+ "muting_user" => user,
+ "tag" => ["tag"],
+ "tag_all" => [],
+ "tag_reject" => [],
+ "type" => "Create",
+ "user" => user,
+ "with_muted" => "true"
+ }
+ end
- public_activities =
- Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(mastodon_public_timeline_params)
+ defp fetch_public_timeline(user) do
+ opts = opts_for_public_timeline(user)
- public_federated_activities =
- Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities(
- mastodon_federated_timeline_params
- )
+ fetch_public_timeline(opts, "public timeline")
+ end
- Benchee.run(%{
- "Rendering home timeline" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
- activities: home_activities,
- for: user,
- as: :activity
- })
- end,
- "Rendering public timeline" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
- activities: public_activities,
- for: user,
- as: :activity
- })
- end,
- "Rendering public federated timeline" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
- activities: public_federated_activities,
- for: user,
- as: :activity
- })
- end
- })
- end
-
- def query_notifications(user) do
- without_muted_params = %{"count" => "20", "with_muted" => "false"}
- with_muted_params = %{"count" => "20", "with_muted" => "true"}
-
- Benchee.run(%{
- "Notifications without muted" => fn ->
- Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params)
- end,
- "Notifications with muted" => fn ->
- Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params)
- end
- })
-
- without_muted_notifications =
- Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, without_muted_params)
-
- with_muted_notifications =
- Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(user, with_muted_params)
-
- Benchee.run(%{
- "Render notifications without muted" => fn ->
- Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
- notifications: without_muted_notifications,
- for: user
- })
- end,
- "Render notifications with muted" => fn ->
- Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
- notifications: with_muted_notifications,
- for: user
- })
- end
- })
- end
-
- def query_dms(user) do
- params = %{
- "count" => "20",
- "with_muted" => "true",
- "type" => "Create",
+ defp fetch_public_timeline(user, :local) do
+ opts = opts_for_public_timeline(user, :local)
+
+ fetch_public_timeline(opts, "public timeline only local")
+ end
+
+ defp fetch_public_timeline(user, :tag) do
+ opts = opts_for_public_timeline(user, :tag)
+
+ fetch_public_timeline(opts, "hashtag timeline")
+ end
+
+ defp fetch_public_timeline(user, :only_media) do
+ opts = opts_for_public_timeline(user) |> Map.put("only_media", "true")
+
+ fetch_public_timeline(opts, "public timeline only media")
+ end
+
+ defp fetch_public_timeline(opts, title) when is_binary(title) do
+ first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
+
+ second_page_last =
+ ActivityPub.fetch_public_activities(Map.put(opts, "max_id", first_page_last.id))
+ |> List.last()
+
+ third_page_last =
+ ActivityPub.fetch_public_activities(Map.put(opts, "max_id", second_page_last.id))
+ |> List.last()
+
+ forth_page_last =
+ ActivityPub.fetch_public_activities(Map.put(opts, "max_id", third_page_last.id))
+ |> List.last()
+
+ Benchee.run(
+ %{
+ title => fn opts ->
+ ActivityPub.fetch_public_activities(opts)
+ end
+ },
+ inputs: %{
+ "1 page" => opts,
+ "2 page" => Map.put(opts, "max_id", first_page_last.id),
+ "3 page" => Map.put(opts, "max_id", second_page_last.id),
+ "4 page" => Map.put(opts, "max_id", third_page_last.id),
+ "5 page" => Map.put(opts, "max_id", forth_page_last.id)
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp opts_for_notifications do
+ %{"count" => "20", "with_muted" => "true"}
+ end
+
+ defp fetch_notifications(user) do
+ opts = opts_for_notifications()
+
+ first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
+
+ second_page_last =
+ MastodonAPI.get_notifications(user, Map.put(opts, "max_id", first_page_last.id))
+ |> List.last()
+
+ third_page_last =
+ MastodonAPI.get_notifications(user, Map.put(opts, "max_id", second_page_last.id))
+ |> List.last()
+
+ forth_page_last =
+ MastodonAPI.get_notifications(user, Map.put(opts, "max_id", third_page_last.id))
+ |> List.last()
+
+ Benchee.run(
+ %{
+ "Notifications" => fn opts ->
+ MastodonAPI.get_notifications(user, opts)
+ end
+ },
+ inputs: %{
+ "1 page" => opts,
+ "2 page" => Map.put(opts, "max_id", first_page_last.id),
+ "3 page" => Map.put(opts, "max_id", second_page_last.id),
+ "4 page" => Map.put(opts, "max_id", third_page_last.id),
+ "5 page" => Map.put(opts, "max_id", forth_page_last.id)
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp fetch_favourites(user) do
+ first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
+
+ second_page_last =
+ ActivityPub.fetch_favourites(user, %{"max_id" => first_page_last.id}) |> List.last()
+
+ third_page_last =
+ ActivityPub.fetch_favourites(user, %{"max_id" => second_page_last.id}) |> List.last()
+
+ forth_page_last =
+ ActivityPub.fetch_favourites(user, %{"max_id" => third_page_last.id}) |> List.last()
+
+ Benchee.run(
+ %{
+ "Favourites" => fn opts ->
+ ActivityPub.fetch_favourites(user, opts)
+ end
+ },
+ inputs: %{
+ "1 page" => %{},
+ "2 page" => %{"max_id" => first_page_last.id},
+ "3 page" => %{"max_id" => second_page_last.id},
+ "4 page" => %{"max_id" => third_page_last.id},
+ "5 page" => %{"max_id" => forth_page_last.id}
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp opts_for_long_thread(user) do
+ %{
"blocking_user" => user,
- "user" => user,
- visibility: "direct"
+ "user" => user
}
+ end
- Benchee.run(%{
- "Direct messages with muted" => fn ->
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
- |> Pleroma.Pagination.fetch_paginated(params)
- end,
- "Direct messages without muted" => fn ->
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
- |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false))
- end
- })
-
- dms_with_muted =
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
- |> Pleroma.Pagination.fetch_paginated(params)
-
- dms_without_muted =
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_query([user.ap_id], params)
- |> Pleroma.Pagination.fetch_paginated(Map.put(params, "with_muted", false))
-
- Benchee.run(%{
- "Rendering dms with muted" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
- activities: dms_with_muted,
- for: user,
- as: :activity
- })
- end,
- "Rendering dms without muted" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
- activities: dms_without_muted,
- for: user,
- as: :activity
- })
- end
- })
- end
-
- def query_long_thread(user, activity) do
- Benchee.run(%{
- "Fetch main post" => fn ->
- Pleroma.Activity.get_by_id_with_object(activity.id)
- end,
- "Fetch context of main post" => fn ->
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context(
- activity.data["context"],
- %{
- "blocking_user" => user,
- "user" => user,
- "exclude_id" => activity.id
- }
- )
- end
- })
-
- activity = Pleroma.Activity.get_by_id_with_object(activity.id)
-
- context =
- Pleroma.Web.ActivityPub.ActivityPub.fetch_activities_for_context(
- activity.data["context"],
- %{
- "blocking_user" => user,
- "user" => user,
- "exclude_id" => activity.id
- }
+ defp fetch_long_thread(user) do
+ %{public_thread: public, private_thread: private} =
+ Agent.get(:benchmark_state, fn state -> state end)
+
+ opts = opts_for_long_thread(user)
+
+ private_input = {private.data["context"], Map.put(opts, "exclude_id", private.id)}
+
+ public_input = {public.data["context"], Map.put(opts, "exclude_id", public.id)}
+
+ Benchee.run(
+ %{
+ "fetch context" => fn {context, opts} ->
+ ActivityPub.fetch_activities_for_context(context, opts)
+ end
+ },
+ inputs: %{
+ "Private long thread" => private_input,
+ "Public long thread" => public_input
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp render_timelines(user) do
+ opts = opts_for_home_timeline(user)
+
+ recipients = [user.ap_id | User.following(user)]
+
+ home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
+
+ recipients = [user.ap_id]
+
+ opts = opts_for_direct_timeline(user)
+
+ direct_activities =
+ recipients
+ |> ActivityPub.fetch_activities_query(opts)
+ |> Pagination.fetch_paginated(opts)
+
+ opts = opts_for_public_timeline(user)
+
+ public_activities = ActivityPub.fetch_public_activities(opts)
+
+ opts = opts_for_public_timeline(user, :tag)
+
+ tag_activities = ActivityPub.fetch_public_activities(opts)
+
+ opts = opts_for_notifications()
+
+ notifications = MastodonAPI.get_notifications(user, opts)
+
+ favourites = ActivityPub.fetch_favourites(user)
+
+ Benchee.run(
+ %{
+ "Rendering home timeline" => fn ->
+ StatusView.render("index.json", %{
+ activities: home_activities,
+ for: user,
+ as: :activity
+ })
+ end,
+ "Rendering direct timeline" => fn ->
+ StatusView.render("index.json", %{
+ activities: direct_activities,
+ for: user,
+ as: :activity
+ })
+ end,
+ "Rendering public timeline" => fn ->
+ StatusView.render("index.json", %{
+ activities: public_activities,
+ for: user,
+ as: :activity
+ })
+ end,
+ "Rendering tag timeline" => fn ->
+ StatusView.render("index.json", %{
+ activities: tag_activities,
+ for: user,
+ as: :activity
+ })
+ end,
+ "Rendering notifications" => fn ->
+ Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
+ notifications: notifications,
+ for: user
+ })
+ end,
+ "Rendering favourites timeline" => fn ->
+ StatusView.render("index.json", %{
+ activities: favourites,
+ for: user,
+ as: :activity
+ })
+ end
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp render_long_thread(user) do
+ %{public_thread: public, private_thread: private} =
+ Agent.get(:benchmark_state, fn state -> state end)
+
+ opts = %{for: user}
+ public_activity = Activity.get_by_id_with_object(public.id)
+ private_activity = Activity.get_by_id_with_object(private.id)
+
+ Benchee.run(
+ %{
+ "render" => fn opts ->
+ StatusView.render("show.json", opts)
+ end
+ },
+ inputs: %{
+ "Public root" => Map.put(opts, :activity, public_activity),
+ "Private root" => Map.put(opts, :activity, private_activity)
+ },
+ formatters: formatters()
+ )
+
+ fetch_opts = opts_for_long_thread(user)
+
+ public_context =
+ ActivityPub.fetch_activities_for_context(
+ public.data["context"],
+ Map.put(fetch_opts, "exclude_id", public.id)
)
- Benchee.run(%{
- "Render status" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{
- activity: activity,
- for: user
- })
- end,
- "Render context" => fn ->
- Pleroma.Web.MastodonAPI.StatusView.render(
- "index.json",
- for: user,
- activities: context,
- as: :activity
- )
- |> Enum.reverse()
- end
- })
+ private_context =
+ ActivityPub.fetch_activities_for_context(
+ private.data["context"],
+ Map.put(fetch_opts, "exclude_id", private.id)
+ )
+
+ Benchee.run(
+ %{
+ "render" => fn opts ->
+ StatusView.render("context.json", opts)
+ end
+ },
+ inputs: %{
+ "Public context" => %{user: user, activity: public_activity, activities: public_context},
+ "Private context" => %{
+ user: user,
+ activity: private_activity,
+ activities: private_context
+ }
+ },
+ formatters: formatters()
+ )
+ end
+
+ defp fetch_timelines_with_reply_filtering(user) do
+ public_params = opts_for_public_timeline(user)
+
+ Benchee.run(
+ %{
+ "Public timeline without reply filtering" => fn ->
+ ActivityPub.fetch_public_activities(public_params)
+ end,
+ "Public timeline with reply filtering - following" => fn ->
+ public_params
+ |> Map.put("reply_visibility", "following")
+ |> Map.put("reply_filtering_user", user)
+ |> ActivityPub.fetch_public_activities()
+ end,
+ "Public timeline with reply filtering - self" => fn ->
+ public_params
+ |> Map.put("reply_visibility", "self")
+ |> Map.put("reply_filtering_user", user)
+ |> ActivityPub.fetch_public_activities()
+ end
+ },
+ formatters: formatters()
+ )
+
+ private_params = opts_for_home_timeline(user)
+
+ recipients = [user.ap_id | User.following(user)]
+
+ Benchee.run(
+ %{
+ "Home timeline without reply filtering" => fn ->
+ ActivityPub.fetch_activities(recipients, private_params)
+ end,
+ "Home timeline with reply filtering - following" => fn ->
+ private_params =
+ private_params
+ |> Map.put("reply_filtering_user", user)
+ |> Map.put("reply_visibility", "following")
+
+ ActivityPub.fetch_activities(recipients, private_params)
+ end,
+ "Home timeline with reply filtering - self" => fn ->
+ private_params =
+ private_params
+ |> Map.put("reply_filtering_user", user)
+ |> Map.put("reply_visibility", "self")
+
+ ActivityPub.fetch_activities(recipients, private_params)
+ end
+ },
+ formatters: formatters()
+ )
end
end