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 create_filter(user) do
36 Pleroma.Filter.create(%{
38 phrase: "must be filtered",
44 defp delete_filter(filter), do: Repo.delete(filter)
46 defp fetch_timelines(user) do
47 fetch_home_timeline(user)
48 fetch_home_timeline_with_filter(user)
49 fetch_direct_timeline(user)
50 fetch_public_timeline(user)
51 fetch_public_timeline_with_filter(user)
52 fetch_public_timeline(user, :with_blocks)
53 fetch_public_timeline(user, :local)
54 fetch_public_timeline(user, :tag)
55 fetch_notifications(user)
56 fetch_favourites(user)
57 fetch_long_thread(user)
58 fetch_timelines_with_reply_filtering(user)
61 defp render_views(user) do
62 render_timelines(user)
63 render_long_thread(user)
66 defp opts_for_home_timeline(user) do
71 type: ["Create", "Announce"],
77 defp fetch_home_timeline(user, title_end \\ "") do
78 opts = opts_for_home_timeline(user)
80 recipients = [user.ap_id | User.following(user)]
83 ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
86 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, first_page_last.id))
91 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, second_page_last.id))
96 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, third_page_last.id))
100 title = "home timeline " <> title_end
104 title => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
108 "2 page" => Map.put(opts, :max_id, first_page_last.id),
109 "3 page" => Map.put(opts, :max_id, second_page_last.id),
110 "4 page" => Map.put(opts, :max_id, third_page_last.id),
111 "5 page" => Map.put(opts, :max_id, forth_page_last.id),
112 "1 page only media" => Map.put(opts, :only_media, true),
113 "2 page only media" =>
114 Map.put(opts, :max_id, first_page_last.id) |> Map.put(:only_media, true),
115 "3 page only media" =>
116 Map.put(opts, :max_id, second_page_last.id) |> Map.put(:only_media, true),
117 "4 page only media" =>
118 Map.put(opts, :max_id, third_page_last.id) |> Map.put(:only_media, true),
119 "5 page only media" =>
120 Map.put(opts, :max_id, forth_page_last.id) |> Map.put(:only_media, true)
122 formatters: formatters()
126 defp fetch_home_timeline_with_filter(user) do
127 {:ok, filter} = create_filter(user)
129 fetch_home_timeline(user, "with filters")
131 delete_filter(filter)
134 defp opts_for_direct_timeline(user) do
136 visibility: "direct",
145 defp fetch_direct_timeline(user) do
146 recipients = [user.ap_id]
148 opts = opts_for_direct_timeline(user)
152 |> ActivityPub.fetch_activities_query(opts)
153 |> Pagination.fetch_paginated(opts)
156 opts2 = Map.put(opts, :max_id, first_page_last.id)
160 |> ActivityPub.fetch_activities_query(opts2)
161 |> Pagination.fetch_paginated(opts2)
164 opts3 = Map.put(opts, :max_id, second_page_last.id)
168 |> ActivityPub.fetch_activities_query(opts3)
169 |> Pagination.fetch_paginated(opts3)
172 opts4 = Map.put(opts, :max_id, third_page_last.id)
176 |> ActivityPub.fetch_activities_query(opts4)
177 |> Pagination.fetch_paginated(opts4)
182 "direct timeline" => fn opts ->
183 ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
191 "5 page" => Map.put(opts4, :max_id, forth_page_last.id)
193 formatters: formatters()
197 defp opts_for_public_timeline(user) do
199 type: ["Create", "Announce"],
206 defp opts_for_public_timeline(user, :local) do
208 type: ["Create", "Announce"],
215 defp opts_for_public_timeline(user, :tag) do
230 defp fetch_public_timeline(user) do
231 opts = opts_for_public_timeline(user)
233 fetch_public_timeline(opts, "public timeline")
236 defp fetch_public_timeline_with_filter(user) do
237 {:ok, filter} = create_filter(user)
238 opts = opts_for_public_timeline(user)
240 fetch_public_timeline(opts, "public timeline with filters")
241 delete_filter(filter)
244 defp fetch_public_timeline(user, :local) do
245 opts = opts_for_public_timeline(user, :local)
247 fetch_public_timeline(opts, "public timeline only local")
250 defp fetch_public_timeline(user, :tag) do
251 opts = opts_for_public_timeline(user, :tag)
253 fetch_public_timeline(opts, "hashtag timeline")
256 defp fetch_public_timeline(user, :only_media) do
257 opts = opts_for_public_timeline(user) |> Map.put(:only_media, true)
259 fetch_public_timeline(opts, "public timeline only media")
262 defp fetch_public_timeline(user, :with_blocks) do
263 opts = opts_for_public_timeline(user)
265 remote_non_friends = Agent.get(:non_friends_remote, & &1)
268 "public timeline without blocks" => fn ->
269 ActivityPub.fetch_public_activities(opts)
273 Enum.each(remote_non_friends, fn non_friend ->
274 {:ok, _} = User.block(user, non_friend)
277 user = User.get_by_id(user.id)
279 opts = Map.put(opts, :blocking_user, user)
282 "public timeline with user block" => fn ->
283 ActivityPub.fetch_public_activities(opts)
288 Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
289 {:ok, _user} = User.unblock(user, non_friend)
290 %{host: host} = URI.parse(non_friend.ap_id)
294 domains = Enum.uniq(domains)
296 Enum.each(domains, fn domain ->
297 {:ok, _} = User.block_domain(user, domain)
300 user = User.get_by_id(user.id)
301 opts = Map.put(opts, :blocking_user, user)
304 "public timeline with domain block" => fn ->
305 ActivityPub.fetch_public_activities(opts)
310 defp fetch_public_timeline(opts, title) when is_binary(title) do
311 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
314 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, first_page_last.id))
318 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, second_page_last.id))
322 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, third_page_last.id))
328 ActivityPub.fetch_public_activities(opts)
333 "2 page" => Map.put(opts, :max_id, first_page_last.id),
334 "3 page" => Map.put(opts, :max_id, second_page_last.id),
335 "4 page" => Map.put(opts, :max_id, third_page_last.id),
336 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
338 formatters: formatters()
342 defp opts_for_notifications do
343 %{count: "20", with_muted: true}
346 defp fetch_notifications(user) do
347 opts = opts_for_notifications()
349 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
352 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, first_page_last.id))
356 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, second_page_last.id))
360 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, third_page_last.id))
365 "Notifications" => fn opts ->
366 MastodonAPI.get_notifications(user, opts)
371 "2 page" => Map.put(opts, :max_id, first_page_last.id),
372 "3 page" => Map.put(opts, :max_id, second_page_last.id),
373 "4 page" => Map.put(opts, :max_id, third_page_last.id),
374 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
376 formatters: formatters()
380 defp fetch_favourites(user) do
381 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
384 ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
387 ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
390 ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
394 "Favourites" => fn opts ->
395 ActivityPub.fetch_favourites(user, opts)
400 "2 page" => %{:max_id => first_page_last.id},
401 "3 page" => %{:max_id => second_page_last.id},
402 "4 page" => %{:max_id => third_page_last.id},
403 "5 page" => %{:max_id => forth_page_last.id}
405 formatters: formatters()
409 defp opts_for_long_thread(user) do
416 defp fetch_long_thread(user) do
417 %{public_thread: public, private_thread: private} =
418 Agent.get(:benchmark_state, fn state -> state end)
420 opts = opts_for_long_thread(user)
422 private_input = {private.data["context"], Map.put(opts, :exclude_id, private.id)}
424 public_input = {public.data["context"], Map.put(opts, :exclude_id, public.id)}
428 "fetch context" => fn {context, opts} ->
429 ActivityPub.fetch_activities_for_context(context, opts)
433 "Private long thread" => private_input,
434 "Public long thread" => public_input
436 formatters: formatters()
440 defp render_timelines(user) do
441 opts = opts_for_home_timeline(user)
443 recipients = [user.ap_id | User.following(user)]
445 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
447 recipients = [user.ap_id]
449 opts = opts_for_direct_timeline(user)
453 |> ActivityPub.fetch_activities_query(opts)
454 |> Pagination.fetch_paginated(opts)
456 opts = opts_for_public_timeline(user)
458 public_activities = ActivityPub.fetch_public_activities(opts)
460 opts = opts_for_public_timeline(user, :tag)
462 tag_activities = ActivityPub.fetch_public_activities(opts)
464 opts = opts_for_notifications()
466 notifications = MastodonAPI.get_notifications(user, opts)
468 favourites = ActivityPub.fetch_favourites(user)
472 "Rendering home timeline" => fn ->
473 StatusView.render("index.json", %{
474 activities: home_activities,
479 "Rendering direct timeline" => fn ->
480 StatusView.render("index.json", %{
481 activities: direct_activities,
486 "Rendering public timeline" => fn ->
487 StatusView.render("index.json", %{
488 activities: public_activities,
493 "Rendering tag timeline" => fn ->
494 StatusView.render("index.json", %{
495 activities: tag_activities,
500 "Rendering notifications" => fn ->
501 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
502 notifications: notifications,
506 "Rendering favourites timeline" => fn ->
507 StatusView.render("index.json", %{
508 activities: favourites,
514 formatters: formatters()
518 defp render_long_thread(user) do
519 %{public_thread: public, private_thread: private} =
520 Agent.get(:benchmark_state, fn state -> state end)
523 public_activity = Activity.get_by_id_with_object(public.id)
524 private_activity = Activity.get_by_id_with_object(private.id)
528 "render" => fn opts ->
529 StatusView.render("show.json", opts)
533 "Public root" => Map.put(opts, :activity, public_activity),
534 "Private root" => Map.put(opts, :activity, private_activity)
536 formatters: formatters()
539 fetch_opts = opts_for_long_thread(user)
542 ActivityPub.fetch_activities_for_context(
543 public.data["context"],
544 Map.put(fetch_opts, :exclude_id, public.id)
548 ActivityPub.fetch_activities_for_context(
549 private.data["context"],
550 Map.put(fetch_opts, :exclude_id, private.id)
555 "render" => fn opts ->
556 StatusView.render("context.json", opts)
560 "Public context" => %{user: user, activity: public_activity, activities: public_context},
561 "Private context" => %{
563 activity: private_activity,
564 activities: private_context
567 formatters: formatters()
571 defp fetch_timelines_with_reply_filtering(user) do
572 public_params = opts_for_public_timeline(user)
576 "Public timeline without reply filtering" => fn ->
577 ActivityPub.fetch_public_activities(public_params)
579 "Public timeline with reply filtering - following" => fn ->
581 |> Map.put(:reply_visibility, "following")
582 |> Map.put(:reply_filtering_user, user)
583 |> ActivityPub.fetch_public_activities()
585 "Public timeline with reply filtering - self" => fn ->
587 |> Map.put(:reply_visibility, "self")
588 |> Map.put(:reply_filtering_user, user)
589 |> ActivityPub.fetch_public_activities()
592 formatters: formatters()
595 private_params = opts_for_home_timeline(user)
597 recipients = [user.ap_id | User.following(user)]
601 "Home timeline without reply filtering" => fn ->
602 ActivityPub.fetch_activities(recipients, private_params)
604 "Home timeline with reply filtering - following" => fn ->
607 |> Map.put(:reply_filtering_user, user)
608 |> Map.put(:reply_visibility, "following")
610 ActivityPub.fetch_activities(recipients, private_params)
612 "Home timeline with reply filtering - self" => fn ->
615 |> Map.put(:reply_filtering_user, user)
616 |> Map.put(:reply_visibility, "self")
618 ActivityPub.fetch_activities(recipients, private_params)
621 formatters: formatters()