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
55 "blocking_user" => user,
57 "muting_user" => user,
58 "type" => ["Create", "Announce"],
60 "with_muted" => "true"
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",
114 "blocking_user" => user,
118 "with_muted" => "true"
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"],
177 "local_only" => false,
178 "blocking_user" => user,
179 "muting_user" => user
183 defp opts_for_public_timeline(user, :local) do
185 "type" => ["Create", "Announce"],
186 "local_only" => true,
187 "blocking_user" => user,
188 "muting_user" => user
192 defp opts_for_public_timeline(user, :tag) do
194 "blocking_user" => user,
197 "muting_user" => user,
203 "with_muted" => "true"
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 # TODO: remove using `:method` after benchmarks
232 defp fetch_public_timeline(user, :with_blocks) do
233 opts = opts_for_public_timeline(user)
235 remote_non_friends = Agent.get(:non_friends_remote, & &1)
239 "public timeline without blocks" => fn opts ->
240 ActivityPub.fetch_public_activities(opts)
244 "old filtering" => Map.delete(opts, :method),
245 "with psql fun" => Map.put(opts, :method, :fun),
246 "with unnest" => Map.put(opts, :method, :unnest)
250 Enum.each(remote_non_friends, fn non_friend ->
251 {:ok, _} = User.block(user, non_friend)
254 user = User.get_by_id(user.id)
256 opts = Map.put(opts, "blocking_user", user)
260 "public timeline with user block" => fn opts ->
261 ActivityPub.fetch_public_activities(opts)
265 "old filtering" => Map.delete(opts, :method),
266 "with psql fun" => Map.put(opts, :method, :fun),
267 "with unnest" => Map.put(opts, :method, :unnest)
272 Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
273 {:ok, _user} = User.unblock(user, non_friend)
274 %{host: host} = URI.parse(non_friend.ap_id)
278 domains = Enum.uniq(domains)
280 Enum.each(domains, fn domain ->
281 {:ok, _} = User.block_domain(user, domain)
284 user = User.get_by_id(user.id)
285 opts = Map.put(opts, "blocking_user", user)
289 "public timeline with domain block" => fn opts ->
290 ActivityPub.fetch_public_activities(opts)
294 "old filtering" => Map.delete(opts, :method),
295 "with psql fun" => Map.put(opts, :method, :fun),
296 "with unnest" => Map.put(opts, :method, :unnest)
301 defp fetch_public_timeline(opts, title) when is_binary(title) do
302 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
305 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", first_page_last.id))
309 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", second_page_last.id))
313 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", third_page_last.id))
319 ActivityPub.fetch_public_activities(opts)
324 "2 page" => Map.put(opts, "max_id", first_page_last.id),
325 "3 page" => Map.put(opts, "max_id", second_page_last.id),
326 "4 page" => Map.put(opts, "max_id", third_page_last.id),
327 "5 page" => Map.put(opts, "max_id", forth_page_last.id)
329 formatters: formatters()
333 defp opts_for_notifications do
334 %{"count" => "20", "with_muted" => "true"}
337 defp fetch_notifications(user) do
338 opts = opts_for_notifications()
340 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
343 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", first_page_last.id))
347 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", second_page_last.id))
351 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", third_page_last.id))
356 "Notifications" => fn opts ->
357 MastodonAPI.get_notifications(user, opts)
362 "2 page" => Map.put(opts, "max_id", first_page_last.id),
363 "3 page" => Map.put(opts, "max_id", second_page_last.id),
364 "4 page" => Map.put(opts, "max_id", third_page_last.id),
365 "5 page" => Map.put(opts, "max_id", forth_page_last.id)
367 formatters: formatters()
371 defp fetch_favourites(user) do
372 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
375 ActivityPub.fetch_favourites(user, %{"max_id" => first_page_last.id}) |> List.last()
378 ActivityPub.fetch_favourites(user, %{"max_id" => second_page_last.id}) |> List.last()
381 ActivityPub.fetch_favourites(user, %{"max_id" => third_page_last.id}) |> List.last()
385 "Favourites" => fn opts ->
386 ActivityPub.fetch_favourites(user, opts)
391 "2 page" => %{"max_id" => first_page_last.id},
392 "3 page" => %{"max_id" => second_page_last.id},
393 "4 page" => %{"max_id" => third_page_last.id},
394 "5 page" => %{"max_id" => forth_page_last.id}
396 formatters: formatters()
400 defp opts_for_long_thread(user) do
402 "blocking_user" => user,
407 defp fetch_long_thread(user) do
408 %{public_thread: public, private_thread: private} =
409 Agent.get(:benchmark_state, fn state -> state end)
411 opts = opts_for_long_thread(user)
413 private_input = {private.data["context"], Map.put(opts, "exclude_id", private.id)}
415 public_input = {public.data["context"], Map.put(opts, "exclude_id", public.id)}
419 "fetch context" => fn {context, opts} ->
420 ActivityPub.fetch_activities_for_context(context, opts)
424 "Private long thread" => private_input,
425 "Public long thread" => public_input
427 formatters: formatters()
431 defp render_timelines(user) do
432 opts = opts_for_home_timeline(user)
434 recipients = [user.ap_id | User.following(user)]
436 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
438 recipients = [user.ap_id]
440 opts = opts_for_direct_timeline(user)
444 |> ActivityPub.fetch_activities_query(opts)
445 |> Pagination.fetch_paginated(opts)
447 opts = opts_for_public_timeline(user)
449 public_activities = ActivityPub.fetch_public_activities(opts)
451 opts = opts_for_public_timeline(user, :tag)
453 tag_activities = ActivityPub.fetch_public_activities(opts)
455 opts = opts_for_notifications()
457 notifications = MastodonAPI.get_notifications(user, opts)
459 favourites = ActivityPub.fetch_favourites(user)
463 "Rendering home timeline" => fn ->
464 StatusView.render("index.json", %{
465 activities: home_activities,
470 "Rendering direct timeline" => fn ->
471 StatusView.render("index.json", %{
472 activities: direct_activities,
477 "Rendering public timeline" => fn ->
478 StatusView.render("index.json", %{
479 activities: public_activities,
484 "Rendering tag timeline" => fn ->
485 StatusView.render("index.json", %{
486 activities: tag_activities,
491 "Rendering notifications" => fn ->
492 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
493 notifications: notifications,
497 "Rendering favourites timeline" => fn ->
498 StatusView.render("index.json", %{
499 activities: favourites,
505 formatters: formatters()
509 defp render_long_thread(user) do
510 %{public_thread: public, private_thread: private} =
511 Agent.get(:benchmark_state, fn state -> state end)
514 public_activity = Activity.get_by_id_with_object(public.id)
515 private_activity = Activity.get_by_id_with_object(private.id)
519 "render" => fn opts ->
520 StatusView.render("show.json", opts)
524 "Public root" => Map.put(opts, :activity, public_activity),
525 "Private root" => Map.put(opts, :activity, private_activity)
527 formatters: formatters()
530 fetch_opts = opts_for_long_thread(user)
533 ActivityPub.fetch_activities_for_context(
534 public.data["context"],
535 Map.put(fetch_opts, "exclude_id", public.id)
539 ActivityPub.fetch_activities_for_context(
540 private.data["context"],
541 Map.put(fetch_opts, "exclude_id", private.id)
546 "render" => fn opts ->
547 StatusView.render("context.json", opts)
551 "Public context" => %{user: user, activity: public_activity, activities: public_context},
552 "Private context" => %{
554 activity: private_activity,
555 activities: private_context
558 formatters: formatters()
562 defp fetch_timelines_with_reply_filtering(user) do
563 public_params = opts_for_public_timeline(user)
567 "Public timeline without reply filtering" => fn ->
568 ActivityPub.fetch_public_activities(public_params)
570 "Public timeline with reply filtering - following" => fn ->
572 |> Map.put("reply_visibility", "following")
573 |> Map.put("reply_filtering_user", user)
574 |> ActivityPub.fetch_public_activities()
576 "Public timeline with reply filtering - self" => fn ->
578 |> Map.put("reply_visibility", "self")
579 |> Map.put("reply_filtering_user", user)
580 |> ActivityPub.fetch_public_activities()
583 formatters: formatters()
586 private_params = opts_for_home_timeline(user)
588 recipients = [user.ap_id | User.following(user)]
592 "Home timeline without reply filtering" => fn ->
593 ActivityPub.fetch_activities(recipients, private_params)
595 "Home timeline with reply filtering - following" => fn ->
598 |> Map.put("reply_filtering_user", user)
599 |> Map.put("reply_visibility", "following")
601 ActivityPub.fetch_activities(recipients, private_params)
603 "Home timeline with reply filtering - self" => fn ->
606 |> Map.put("reply_filtering_user", user)
607 |> Map.put("reply_visibility", "self")
609 ActivityPub.fetch_activities(recipients, private_params)
612 formatters: formatters()