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(%Pleroma.Filter{
38 phrase: "must be filtered",
43 defp delete_filter(filter), do: Repo.delete(filter)
45 defp fetch_timelines(user) do
46 fetch_home_timeline(user)
47 fetch_home_timeline_with_filter(user)
48 fetch_direct_timeline(user)
49 fetch_public_timeline(user)
50 fetch_public_timeline_with_filter(user)
51 fetch_public_timeline(user, :with_blocks)
52 fetch_public_timeline(user, :local)
53 fetch_public_timeline(user, :tag)
54 fetch_notifications(user)
55 fetch_favourites(user)
56 fetch_long_thread(user)
57 fetch_timelines_with_reply_filtering(user)
60 defp render_views(user) do
61 render_timelines(user)
62 render_long_thread(user)
65 defp opts_for_home_timeline(user) do
70 type: ["Create", "Announce"],
76 defp fetch_home_timeline(user, title_end \\ "") do
77 opts = opts_for_home_timeline(user)
79 recipients = [user.ap_id | User.following(user)]
82 ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
85 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, first_page_last.id))
90 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, second_page_last.id))
95 ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, third_page_last.id))
99 title = "home timeline " <> title_end
103 title => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
107 "2 page" => Map.put(opts, :max_id, first_page_last.id),
108 "3 page" => Map.put(opts, :max_id, second_page_last.id),
109 "4 page" => Map.put(opts, :max_id, third_page_last.id),
110 "5 page" => Map.put(opts, :max_id, forth_page_last.id),
111 "1 page only media" => Map.put(opts, :only_media, true),
112 "2 page only media" =>
113 Map.put(opts, :max_id, first_page_last.id) |> Map.put(:only_media, true),
114 "3 page only media" =>
115 Map.put(opts, :max_id, second_page_last.id) |> Map.put(:only_media, true),
116 "4 page only media" =>
117 Map.put(opts, :max_id, third_page_last.id) |> Map.put(:only_media, true),
118 "5 page only media" =>
119 Map.put(opts, :max_id, forth_page_last.id) |> Map.put(:only_media, true)
121 formatters: formatters()
125 defp fetch_home_timeline_with_filter(user) do
126 {:ok, filter} = create_filter(user)
128 fetch_home_timeline(user, "with filters")
130 delete_filter(filter)
133 defp opts_for_direct_timeline(user) do
135 visibility: "direct",
144 defp fetch_direct_timeline(user) do
145 recipients = [user.ap_id]
147 opts = opts_for_direct_timeline(user)
151 |> ActivityPub.fetch_activities_query(opts)
152 |> Pagination.fetch_paginated(opts)
155 opts2 = Map.put(opts, :max_id, first_page_last.id)
159 |> ActivityPub.fetch_activities_query(opts2)
160 |> Pagination.fetch_paginated(opts2)
163 opts3 = Map.put(opts, :max_id, second_page_last.id)
167 |> ActivityPub.fetch_activities_query(opts3)
168 |> Pagination.fetch_paginated(opts3)
171 opts4 = Map.put(opts, :max_id, third_page_last.id)
175 |> ActivityPub.fetch_activities_query(opts4)
176 |> Pagination.fetch_paginated(opts4)
181 "direct timeline" => fn opts ->
182 ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
190 "5 page" => Map.put(opts4, :max_id, forth_page_last.id)
192 formatters: formatters()
196 defp opts_for_public_timeline(user) do
198 type: ["Create", "Announce"],
205 defp opts_for_public_timeline(user, :local) do
207 type: ["Create", "Announce"],
214 defp opts_for_public_timeline(user, :tag) do
229 defp fetch_public_timeline(user) do
230 opts = opts_for_public_timeline(user)
232 fetch_public_timeline(opts, "public timeline")
235 defp fetch_public_timeline_with_filter(user) do
236 {:ok, filter} = create_filter(user)
237 opts = opts_for_public_timeline(user)
239 fetch_public_timeline(opts, "public timeline with filters")
240 delete_filter(filter)
243 defp fetch_public_timeline(user, :local) do
244 opts = opts_for_public_timeline(user, :local)
246 fetch_public_timeline(opts, "public timeline only local")
249 defp fetch_public_timeline(user, :tag) do
250 opts = opts_for_public_timeline(user, :tag)
252 fetch_public_timeline(opts, "hashtag timeline")
255 defp fetch_public_timeline(user, :only_media) do
256 opts = opts_for_public_timeline(user) |> Map.put(:only_media, true)
258 fetch_public_timeline(opts, "public timeline only media")
261 defp fetch_public_timeline(user, :with_blocks) do
262 opts = opts_for_public_timeline(user)
264 remote_non_friends = Agent.get(:non_friends_remote, & &1)
267 "public timeline without blocks" => fn ->
268 ActivityPub.fetch_public_activities(opts)
272 Enum.each(remote_non_friends, fn non_friend ->
273 {:ok, _} = User.block(user, non_friend)
276 user = User.get_by_id(user.id)
278 opts = Map.put(opts, :blocking_user, user)
281 "public timeline with user block" => fn ->
282 ActivityPub.fetch_public_activities(opts)
287 Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
288 {:ok, _user} = User.unblock(user, non_friend)
289 %{host: host} = URI.parse(non_friend.ap_id)
293 domains = Enum.uniq(domains)
295 Enum.each(domains, fn domain ->
296 {:ok, _} = User.block_domain(user, domain)
299 user = User.get_by_id(user.id)
300 opts = Map.put(opts, :blocking_user, user)
303 "public timeline with domain block" => fn ->
304 ActivityPub.fetch_public_activities(opts)
309 defp fetch_public_timeline(opts, title) when is_binary(title) do
310 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
313 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, first_page_last.id))
317 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, second_page_last.id))
321 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, third_page_last.id))
327 ActivityPub.fetch_public_activities(opts)
332 "2 page" => Map.put(opts, :max_id, first_page_last.id),
333 "3 page" => Map.put(opts, :max_id, second_page_last.id),
334 "4 page" => Map.put(opts, :max_id, third_page_last.id),
335 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
337 formatters: formatters()
341 defp opts_for_notifications do
342 %{count: "20", with_muted: true}
345 defp fetch_notifications(user) do
346 opts = opts_for_notifications()
348 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
351 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, first_page_last.id))
355 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, second_page_last.id))
359 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, third_page_last.id))
364 "Notifications" => fn opts ->
365 MastodonAPI.get_notifications(user, opts)
370 "2 page" => Map.put(opts, :max_id, first_page_last.id),
371 "3 page" => Map.put(opts, :max_id, second_page_last.id),
372 "4 page" => Map.put(opts, :max_id, third_page_last.id),
373 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
375 formatters: formatters()
379 defp fetch_favourites(user) do
380 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
383 ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
386 ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
389 ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
393 "Favourites" => fn opts ->
394 ActivityPub.fetch_favourites(user, opts)
399 "2 page" => %{:max_id => first_page_last.id},
400 "3 page" => %{:max_id => second_page_last.id},
401 "4 page" => %{:max_id => third_page_last.id},
402 "5 page" => %{:max_id => forth_page_last.id}
404 formatters: formatters()
408 defp opts_for_long_thread(user) do
415 defp fetch_long_thread(user) do
416 %{public_thread: public, private_thread: private} =
417 Agent.get(:benchmark_state, fn state -> state end)
419 opts = opts_for_long_thread(user)
421 private_input = {private.data["context"], Map.put(opts, :exclude_id, private.id)}
423 public_input = {public.data["context"], Map.put(opts, :exclude_id, public.id)}
427 "fetch context" => fn {context, opts} ->
428 ActivityPub.fetch_activities_for_context(context, opts)
432 "Private long thread" => private_input,
433 "Public long thread" => public_input
435 formatters: formatters()
439 defp render_timelines(user) do
440 opts = opts_for_home_timeline(user)
442 recipients = [user.ap_id | User.following(user)]
444 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
446 recipients = [user.ap_id]
448 opts = opts_for_direct_timeline(user)
452 |> ActivityPub.fetch_activities_query(opts)
453 |> Pagination.fetch_paginated(opts)
455 opts = opts_for_public_timeline(user)
457 public_activities = ActivityPub.fetch_public_activities(opts)
459 opts = opts_for_public_timeline(user, :tag)
461 tag_activities = ActivityPub.fetch_public_activities(opts)
463 opts = opts_for_notifications()
465 notifications = MastodonAPI.get_notifications(user, opts)
467 favourites = ActivityPub.fetch_favourites(user)
471 "Rendering home timeline" => fn ->
472 StatusView.render("index.json", %{
473 activities: home_activities,
478 "Rendering direct timeline" => fn ->
479 StatusView.render("index.json", %{
480 activities: direct_activities,
485 "Rendering public timeline" => fn ->
486 StatusView.render("index.json", %{
487 activities: public_activities,
492 "Rendering tag timeline" => fn ->
493 StatusView.render("index.json", %{
494 activities: tag_activities,
499 "Rendering notifications" => fn ->
500 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
501 notifications: notifications,
505 "Rendering favourites timeline" => fn ->
506 StatusView.render("index.json", %{
507 activities: favourites,
513 formatters: formatters()
517 defp render_long_thread(user) do
518 %{public_thread: public, private_thread: private} =
519 Agent.get(:benchmark_state, fn state -> state end)
522 public_activity = Activity.get_by_id_with_object(public.id)
523 private_activity = Activity.get_by_id_with_object(private.id)
527 "render" => fn opts ->
528 StatusView.render("show.json", opts)
532 "Public root" => Map.put(opts, :activity, public_activity),
533 "Private root" => Map.put(opts, :activity, private_activity)
535 formatters: formatters()
538 fetch_opts = opts_for_long_thread(user)
541 ActivityPub.fetch_activities_for_context(
542 public.data["context"],
543 Map.put(fetch_opts, :exclude_id, public.id)
547 ActivityPub.fetch_activities_for_context(
548 private.data["context"],
549 Map.put(fetch_opts, :exclude_id, private.id)
554 "render" => fn opts ->
555 StatusView.render("context.json", opts)
559 "Public context" => %{user: user, activity: public_activity, activities: public_context},
560 "Private context" => %{
562 activity: private_activity,
563 activities: private_context
566 formatters: formatters()
570 defp fetch_timelines_with_reply_filtering(user) do
571 public_params = opts_for_public_timeline(user)
575 "Public timeline without reply filtering" => fn ->
576 ActivityPub.fetch_public_activities(public_params)
578 "Public timeline with reply filtering - following" => fn ->
580 |> Map.put(:reply_visibility, "following")
581 |> Map.put(:reply_filtering_user, user)
582 |> ActivityPub.fetch_public_activities()
584 "Public timeline with reply filtering - self" => fn ->
586 |> Map.put(:reply_visibility, "self")
587 |> Map.put(:reply_filtering_user, user)
588 |> ActivityPub.fetch_public_activities()
591 formatters: formatters()
594 private_params = opts_for_home_timeline(user)
596 recipients = [user.ap_id | User.following(user)]
600 "Home timeline without reply filtering" => fn ->
601 ActivityPub.fetch_activities(recipients, private_params)
603 "Home timeline with reply filtering - following" => fn ->
606 |> Map.put(:reply_filtering_user, user)
607 |> Map.put(:reply_visibility, "following")
609 ActivityPub.fetch_activities(recipients, private_params)
611 "Home timeline with reply filtering - self" => fn ->
614 |> Map.put(:reply_filtering_user, user)
615 |> Map.put(:reply_visibility, "self")
617 ActivityPub.fetch_activities(recipients, private_params)
620 formatters: formatters()