1 defmodule Pleroma.LoadTesting.Fetcher do
3 alias Pleroma.Pagination
6 alias Pleroma.Web.ActivityPub.ActivityPub
7 alias Pleroma.Web.MastodonAPI.MastodonAPI
8 alias Pleroma.Web.MastodonAPI.StatusView
10 @spec run_benchmarks(User.t()) :: any()
11 def run_benchmarks(user) do
19 Benchee.Formatters.Console
23 defp fetch_user(user) do
26 "By id" => fn -> Repo.get_by(User, id: user.id) end,
27 "By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end,
28 "By email" => fn -> Repo.get_by(User, email: user.email) end,
29 "By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end
31 formatters: formatters()
35 defp fetch_timelines(user) do
36 fetch_home_timeline(user)
37 fetch_direct_timeline(user)
38 fetch_public_timeline(user)
39 fetch_public_timeline(user, :with_blocks)
40 fetch_public_timeline(user, :local)
41 fetch_public_timeline(user, :tag)
42 fetch_notifications(user)
43 fetch_favourites(user)
44 fetch_long_thread(user)
45 fetch_timelines_with_reply_filtering(user)
48 defp render_views(user) do
49 render_timelines(user)
50 render_long_thread(user)
53 defp opts_for_home_timeline(user) do
58 type: ["Create", "Announce"],
64 defp fetch_home_timeline(user) do
65 opts = opts_for_home_timeline(user)
67 recipients = [user.ap_id | User.following(user)]
70 ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
73 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, first_page_last.id))
78 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, second_page_last.id))
83 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, third_page_last.id))
89 "home timeline" => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
93 "2 page" => Map.put(opts, :max_id, first_page_last.id),
94 "3 page" => Map.put(opts, :max_id, second_page_last.id),
95 "4 page" => Map.put(opts, :max_id, third_page_last.id),
96 "5 page" => Map.put(opts, :max_id, forth_page_last.id),
97 "1 page only media" => Map.put(opts, :only_media, true),
98 "2 page only media" =>
99 Map.put(opts, :max_id, first_page_last.id) |> Map.put(:only_media, true),
100 "3 page only media" =>
101 Map.put(opts, :max_id, second_page_last.id) |> Map.put(:only_media, true),
102 "4 page only media" =>
103 Map.put(opts, :max_id, third_page_last.id) |> Map.put(:only_media, true),
104 "5 page only media" =>
105 Map.put(opts, :max_id, forth_page_last.id) |> Map.put(:only_media, true)
107 formatters: formatters()
111 defp opts_for_direct_timeline(user) do
113 visibility: "direct",
122 defp fetch_direct_timeline(user) do
123 recipients = [user.ap_id]
125 opts = opts_for_direct_timeline(user)
129 |> ActivityPub.fetch_activities_query(opts)
130 |> Pagination.fetch_paginated(opts)
133 opts2 = Map.put(opts, :max_id, first_page_last.id)
137 |> ActivityPub.fetch_activities_query(opts2)
138 |> Pagination.fetch_paginated(opts2)
141 opts3 = Map.put(opts, :max_id, second_page_last.id)
145 |> ActivityPub.fetch_activities_query(opts3)
146 |> Pagination.fetch_paginated(opts3)
149 opts4 = Map.put(opts, :max_id, third_page_last.id)
153 |> ActivityPub.fetch_activities_query(opts4)
154 |> Pagination.fetch_paginated(opts4)
159 "direct timeline" => fn opts ->
160 ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
168 "5 page" => Map.put(opts4, :max_id, forth_page_last.id)
170 formatters: formatters()
174 defp opts_for_public_timeline(user) do
176 type: ["Create", "Announce"],
183 defp opts_for_public_timeline(user, :local) do
185 type: ["Create", "Announce"],
192 defp opts_for_public_timeline(user, :tag) do
207 defp fetch_public_timeline(user) do
208 opts = opts_for_public_timeline(user)
210 fetch_public_timeline(opts, "public timeline")
213 defp fetch_public_timeline(user, :local) do
214 opts = opts_for_public_timeline(user, :local)
216 fetch_public_timeline(opts, "public timeline only local")
219 defp fetch_public_timeline(user, :tag) do
220 opts = opts_for_public_timeline(user, :tag)
222 fetch_public_timeline(opts, "hashtag timeline")
225 defp fetch_public_timeline(user, :only_media) do
226 opts = opts_for_public_timeline(user) |> Map.put(:only_media, true)
228 fetch_public_timeline(opts, "public timeline only media")
231 defp fetch_public_timeline(user, :with_blocks) do
232 opts = opts_for_public_timeline(user)
234 remote_non_friends = Agent.get(:non_friends_remote, & &1)
237 "public timeline without blocks" => fn ->
238 ActivityPub.fetch_public_activities(opts)
242 Enum.each(remote_non_friends, fn non_friend ->
243 {:ok, _} = User.block(user, non_friend)
246 user = User.get_by_id(user.id)
248 opts = Map.put(opts, :blocking_user, user)
251 "public timeline with user block" => fn ->
252 ActivityPub.fetch_public_activities(opts)
257 Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
258 {:ok, _user} = User.unblock(user, non_friend)
259 %{host: host} = URI.parse(non_friend.ap_id)
263 domains = Enum.uniq(domains)
265 Enum.each(domains, fn domain ->
266 {:ok, _} = User.block_domain(user, domain)
269 user = User.get_by_id(user.id)
270 opts = Map.put(opts, :blocking_user, user)
273 "public timeline with domain block" => fn ->
274 ActivityPub.fetch_public_activities(opts)
279 defp fetch_public_timeline(opts, title) when is_binary(title) do
280 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
283 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, first_page_last.id))
287 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, second_page_last.id))
291 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, third_page_last.id))
297 ActivityPub.fetch_public_activities(opts)
302 "2 page" => Map.put(opts, :max_id, first_page_last.id),
303 "3 page" => Map.put(opts, :max_id, second_page_last.id),
304 "4 page" => Map.put(opts, :max_id, third_page_last.id),
305 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
307 formatters: formatters()
311 defp opts_for_notifications do
312 %{count: "20", with_muted: true}
315 defp fetch_notifications(user) do
316 opts = opts_for_notifications()
318 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
321 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, first_page_last.id))
325 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, second_page_last.id))
329 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, third_page_last.id))
334 "Notifications" => fn opts ->
335 MastodonAPI.get_notifications(user, opts)
340 "2 page" => Map.put(opts, :max_id, first_page_last.id),
341 "3 page" => Map.put(opts, :max_id, second_page_last.id),
342 "4 page" => Map.put(opts, :max_id, third_page_last.id),
343 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
345 formatters: formatters()
349 defp fetch_favourites(user) do
350 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
353 ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
356 ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
359 ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
363 "Favourites" => fn opts ->
364 ActivityPub.fetch_favourites(user, opts)
369 "2 page" => %{:max_id => first_page_last.id},
370 "3 page" => %{:max_id => second_page_last.id},
371 "4 page" => %{:max_id => third_page_last.id},
372 "5 page" => %{:max_id => forth_page_last.id}
374 formatters: formatters()
378 defp opts_for_long_thread(user) do
385 defp fetch_long_thread(user) do
386 %{public_thread: public, private_thread: private} =
387 Agent.get(:benchmark_state, fn state -> state end)
389 opts = opts_for_long_thread(user)
391 private_input = {private.data["context"], Map.put(opts, :exclude_id, private.id)}
393 public_input = {public.data["context"], Map.put(opts, :exclude_id, public.id)}
397 "fetch context" => fn {context, opts} ->
398 ActivityPub.fetch_activities_for_context(context, opts)
402 "Private long thread" => private_input,
403 "Public long thread" => public_input
405 formatters: formatters()
409 defp render_timelines(user) do
410 opts = opts_for_home_timeline(user)
412 recipients = [user.ap_id | User.following(user)]
414 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
416 recipients = [user.ap_id]
418 opts = opts_for_direct_timeline(user)
422 |> ActivityPub.fetch_activities_query(opts)
423 |> Pagination.fetch_paginated(opts)
425 opts = opts_for_public_timeline(user)
427 public_activities = ActivityPub.fetch_public_activities(opts)
429 opts = opts_for_public_timeline(user, :tag)
431 tag_activities = ActivityPub.fetch_public_activities(opts)
433 opts = opts_for_notifications()
435 notifications = MastodonAPI.get_notifications(user, opts)
437 favourites = ActivityPub.fetch_favourites(user)
441 "Rendering home timeline" => fn ->
442 StatusView.render("index.json", %{
443 activities: home_activities,
448 "Rendering direct timeline" => fn ->
449 StatusView.render("index.json", %{
450 activities: direct_activities,
455 "Rendering public timeline" => fn ->
456 StatusView.render("index.json", %{
457 activities: public_activities,
462 "Rendering tag timeline" => fn ->
463 StatusView.render("index.json", %{
464 activities: tag_activities,
469 "Rendering notifications" => fn ->
470 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
471 notifications: notifications,
475 "Rendering favourites timeline" => fn ->
476 StatusView.render("index.json", %{
477 activities: favourites,
483 formatters: formatters()
487 defp render_long_thread(user) do
488 %{public_thread: public, private_thread: private} =
489 Agent.get(:benchmark_state, fn state -> state end)
492 public_activity = Activity.get_by_id_with_object(public.id)
493 private_activity = Activity.get_by_id_with_object(private.id)
497 "render" => fn opts ->
498 StatusView.render("show.json", opts)
502 "Public root" => Map.put(opts, :activity, public_activity),
503 "Private root" => Map.put(opts, :activity, private_activity)
505 formatters: formatters()
508 fetch_opts = opts_for_long_thread(user)
511 ActivityPub.fetch_activities_for_context(
512 public.data["context"],
513 Map.put(fetch_opts, :exclude_id, public.id)
517 ActivityPub.fetch_activities_for_context(
518 private.data["context"],
519 Map.put(fetch_opts, :exclude_id, private.id)
524 "render" => fn opts ->
525 StatusView.render("context.json", opts)
529 "Public context" => %{user: user, activity: public_activity, activities: public_context},
530 "Private context" => %{
532 activity: private_activity,
533 activities: private_context
536 formatters: formatters()
540 defp fetch_timelines_with_reply_filtering(user) do
541 public_params = opts_for_public_timeline(user)
545 "Public timeline without reply filtering" => fn ->
546 ActivityPub.fetch_public_activities(public_params)
548 "Public timeline with reply filtering - following" => fn ->
550 |> Map.put(:reply_visibility, "following")
551 |> Map.put(:reply_filtering_user, user)
552 |> ActivityPub.fetch_public_activities()
554 "Public timeline with reply filtering - self" => fn ->
556 |> Map.put(:reply_visibility, "self")
557 |> Map.put(:reply_filtering_user, user)
558 |> ActivityPub.fetch_public_activities()
561 formatters: formatters()
564 private_params = opts_for_home_timeline(user)
566 recipients = [user.ap_id | User.following(user)]
570 "Home timeline without reply filtering" => fn ->
571 ActivityPub.fetch_activities(recipients, private_params)
573 "Home timeline with reply filtering - following" => fn ->
576 |> Map.put(:reply_filtering_user, user)
577 |> Map.put(:reply_visibility, "following")
579 ActivityPub.fetch_activities(recipients, private_params)
581 "Home timeline with reply filtering - self" => fn ->
584 |> Map.put(:reply_filtering_user, user)
585 |> Map.put(:reply_visibility, "self")
587 ActivityPub.fetch_activities(recipients, private_params)
590 formatters: formatters()