Merge branch 'replies-domain-block' into 'develop'
[akkoma] / benchmarks / load_testing / fetcher.ex
1 defmodule Pleroma.LoadTesting.Fetcher do
2 alias Pleroma.Activity
3 alias Pleroma.Pagination
4 alias Pleroma.Repo
5 alias Pleroma.User
6 alias Pleroma.Web.ActivityPub.ActivityPub
7 alias Pleroma.Web.MastodonAPI.MastodonAPI
8 alias Pleroma.Web.MastodonAPI.StatusView
9
10 @spec run_benchmarks(User.t()) :: any()
11 def run_benchmarks(user) do
12 fetch_user(user)
13 fetch_timelines(user)
14 render_views(user)
15 end
16
17 defp formatters do
18 [
19 Benchee.Formatters.Console
20 ]
21 end
22
23 defp fetch_user(user) do
24 Benchee.run(
25 %{
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
30 },
31 formatters: formatters()
32 )
33 end
34
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)
46 end
47
48 defp render_views(user) do
49 render_timelines(user)
50 render_long_thread(user)
51 end
52
53 defp opts_for_home_timeline(user) do
54 %{
55 "blocking_user" => user,
56 "count" => "20",
57 "muting_user" => user,
58 "type" => ["Create", "Announce"],
59 "user" => user,
60 "with_muted" => "true"
61 }
62 end
63
64 defp fetch_home_timeline(user) do
65 opts = opts_for_home_timeline(user)
66
67 recipients = [user.ap_id | User.following(user)]
68
69 first_page_last =
70 ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
71
72 second_page_last =
73 ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", first_page_last.id))
74 |> Enum.reverse()
75 |> List.last()
76
77 third_page_last =
78 ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", second_page_last.id))
79 |> Enum.reverse()
80 |> List.last()
81
82 forth_page_last =
83 ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", third_page_last.id))
84 |> Enum.reverse()
85 |> List.last()
86
87 Benchee.run(
88 %{
89 "home timeline" => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
90 },
91 inputs: %{
92 "1 page" => opts,
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")
106 },
107 formatters: formatters()
108 )
109 end
110
111 defp opts_for_direct_timeline(user) do
112 %{
113 :visibility => "direct",
114 "blocking_user" => user,
115 "count" => "20",
116 "type" => "Create",
117 "user" => user,
118 "with_muted" => "true"
119 }
120 end
121
122 defp fetch_direct_timeline(user) do
123 recipients = [user.ap_id]
124
125 opts = opts_for_direct_timeline(user)
126
127 first_page_last =
128 recipients
129 |> ActivityPub.fetch_activities_query(opts)
130 |> Pagination.fetch_paginated(opts)
131 |> List.last()
132
133 opts2 = Map.put(opts, "max_id", first_page_last.id)
134
135 second_page_last =
136 recipients
137 |> ActivityPub.fetch_activities_query(opts2)
138 |> Pagination.fetch_paginated(opts2)
139 |> List.last()
140
141 opts3 = Map.put(opts, "max_id", second_page_last.id)
142
143 third_page_last =
144 recipients
145 |> ActivityPub.fetch_activities_query(opts3)
146 |> Pagination.fetch_paginated(opts3)
147 |> List.last()
148
149 opts4 = Map.put(opts, "max_id", third_page_last.id)
150
151 forth_page_last =
152 recipients
153 |> ActivityPub.fetch_activities_query(opts4)
154 |> Pagination.fetch_paginated(opts4)
155 |> List.last()
156
157 Benchee.run(
158 %{
159 "direct timeline" => fn opts ->
160 ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
161 end
162 },
163 inputs: %{
164 "1 page" => opts,
165 "2 page" => opts2,
166 "3 page" => opts3,
167 "4 page" => opts4,
168 "5 page" => Map.put(opts4, "max_id", forth_page_last.id)
169 },
170 formatters: formatters()
171 )
172 end
173
174 defp opts_for_public_timeline(user) do
175 %{
176 "type" => ["Create", "Announce"],
177 "local_only" => false,
178 "blocking_user" => user,
179 "muting_user" => user
180 }
181 end
182
183 defp opts_for_public_timeline(user, :local) do
184 %{
185 "type" => ["Create", "Announce"],
186 "local_only" => true,
187 "blocking_user" => user,
188 "muting_user" => user
189 }
190 end
191
192 defp opts_for_public_timeline(user, :tag) do
193 %{
194 "blocking_user" => user,
195 "count" => "20",
196 "local_only" => nil,
197 "muting_user" => user,
198 "tag" => ["tag"],
199 "tag_all" => [],
200 "tag_reject" => [],
201 "type" => "Create",
202 "user" => user,
203 "with_muted" => "true"
204 }
205 end
206
207 defp fetch_public_timeline(user) do
208 opts = opts_for_public_timeline(user)
209
210 fetch_public_timeline(opts, "public timeline")
211 end
212
213 defp fetch_public_timeline(user, :local) do
214 opts = opts_for_public_timeline(user, :local)
215
216 fetch_public_timeline(opts, "public timeline only local")
217 end
218
219 defp fetch_public_timeline(user, :tag) do
220 opts = opts_for_public_timeline(user, :tag)
221
222 fetch_public_timeline(opts, "hashtag timeline")
223 end
224
225 defp fetch_public_timeline(user, :only_media) do
226 opts = opts_for_public_timeline(user) |> Map.put("only_media", "true")
227
228 fetch_public_timeline(opts, "public timeline only media")
229 end
230
231 defp fetch_public_timeline(user, :with_blocks) do
232 opts = opts_for_public_timeline(user)
233
234 remote_non_friends = Agent.get(:non_friends_remote, & &1)
235
236 Benchee.run(%{
237 "public timeline without blocks" => fn ->
238 ActivityPub.fetch_public_activities(opts)
239 end
240 })
241
242 Enum.each(remote_non_friends, fn non_friend ->
243 {:ok, _} = User.block(user, non_friend)
244 end)
245
246 user = User.get_by_id(user.id)
247
248 opts = Map.put(opts, "blocking_user", user)
249
250 Benchee.run(
251 %{
252 "public timeline with user block" => fn ->
253 ActivityPub.fetch_public_activities(opts)
254 end
255 },
256 )
257
258 domains =
259 Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
260 {:ok, _user} = User.unblock(user, non_friend)
261 %{host: host} = URI.parse(non_friend.ap_id)
262 [host | domains]
263 end)
264
265 domains = Enum.uniq(domains)
266
267 Enum.each(domains, fn domain ->
268 {:ok, _} = User.block_domain(user, domain)
269 end)
270
271 user = User.get_by_id(user.id)
272 opts = Map.put(opts, "blocking_user", user)
273
274 Benchee.run(
275 %{
276 "public timeline with domain block" => fn opts ->
277 ActivityPub.fetch_public_activities(opts)
278 end
279 }
280 )
281 end
282
283 defp fetch_public_timeline(opts, title) when is_binary(title) do
284 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
285
286 second_page_last =
287 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", first_page_last.id))
288 |> List.last()
289
290 third_page_last =
291 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", second_page_last.id))
292 |> List.last()
293
294 forth_page_last =
295 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", third_page_last.id))
296 |> List.last()
297
298 Benchee.run(
299 %{
300 title => fn opts ->
301 ActivityPub.fetch_public_activities(opts)
302 end
303 },
304 inputs: %{
305 "1 page" => opts,
306 "2 page" => Map.put(opts, "max_id", first_page_last.id),
307 "3 page" => Map.put(opts, "max_id", second_page_last.id),
308 "4 page" => Map.put(opts, "max_id", third_page_last.id),
309 "5 page" => Map.put(opts, "max_id", forth_page_last.id)
310 },
311 formatters: formatters()
312 )
313 end
314
315 defp opts_for_notifications do
316 %{"count" => "20", "with_muted" => "true"}
317 end
318
319 defp fetch_notifications(user) do
320 opts = opts_for_notifications()
321
322 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
323
324 second_page_last =
325 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", first_page_last.id))
326 |> List.last()
327
328 third_page_last =
329 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", second_page_last.id))
330 |> List.last()
331
332 forth_page_last =
333 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", third_page_last.id))
334 |> List.last()
335
336 Benchee.run(
337 %{
338 "Notifications" => fn opts ->
339 MastodonAPI.get_notifications(user, opts)
340 end
341 },
342 inputs: %{
343 "1 page" => opts,
344 "2 page" => Map.put(opts, "max_id", first_page_last.id),
345 "3 page" => Map.put(opts, "max_id", second_page_last.id),
346 "4 page" => Map.put(opts, "max_id", third_page_last.id),
347 "5 page" => Map.put(opts, "max_id", forth_page_last.id)
348 },
349 formatters: formatters()
350 )
351 end
352
353 defp fetch_favourites(user) do
354 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
355
356 second_page_last =
357 ActivityPub.fetch_favourites(user, %{"max_id" => first_page_last.id}) |> List.last()
358
359 third_page_last =
360 ActivityPub.fetch_favourites(user, %{"max_id" => second_page_last.id}) |> List.last()
361
362 forth_page_last =
363 ActivityPub.fetch_favourites(user, %{"max_id" => third_page_last.id}) |> List.last()
364
365 Benchee.run(
366 %{
367 "Favourites" => fn opts ->
368 ActivityPub.fetch_favourites(user, opts)
369 end
370 },
371 inputs: %{
372 "1 page" => %{},
373 "2 page" => %{"max_id" => first_page_last.id},
374 "3 page" => %{"max_id" => second_page_last.id},
375 "4 page" => %{"max_id" => third_page_last.id},
376 "5 page" => %{"max_id" => forth_page_last.id}
377 },
378 formatters: formatters()
379 )
380 end
381
382 defp opts_for_long_thread(user) do
383 %{
384 "blocking_user" => user,
385 "user" => user
386 }
387 end
388
389 defp fetch_long_thread(user) do
390 %{public_thread: public, private_thread: private} =
391 Agent.get(:benchmark_state, fn state -> state end)
392
393 opts = opts_for_long_thread(user)
394
395 private_input = {private.data["context"], Map.put(opts, "exclude_id", private.id)}
396
397 public_input = {public.data["context"], Map.put(opts, "exclude_id", public.id)}
398
399 Benchee.run(
400 %{
401 "fetch context" => fn {context, opts} ->
402 ActivityPub.fetch_activities_for_context(context, opts)
403 end
404 },
405 inputs: %{
406 "Private long thread" => private_input,
407 "Public long thread" => public_input
408 },
409 formatters: formatters()
410 )
411 end
412
413 defp render_timelines(user) do
414 opts = opts_for_home_timeline(user)
415
416 recipients = [user.ap_id | User.following(user)]
417
418 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
419
420 recipients = [user.ap_id]
421
422 opts = opts_for_direct_timeline(user)
423
424 direct_activities =
425 recipients
426 |> ActivityPub.fetch_activities_query(opts)
427 |> Pagination.fetch_paginated(opts)
428
429 opts = opts_for_public_timeline(user)
430
431 public_activities = ActivityPub.fetch_public_activities(opts)
432
433 opts = opts_for_public_timeline(user, :tag)
434
435 tag_activities = ActivityPub.fetch_public_activities(opts)
436
437 opts = opts_for_notifications()
438
439 notifications = MastodonAPI.get_notifications(user, opts)
440
441 favourites = ActivityPub.fetch_favourites(user)
442
443 Benchee.run(
444 %{
445 "Rendering home timeline" => fn ->
446 StatusView.render("index.json", %{
447 activities: home_activities,
448 for: user,
449 as: :activity
450 })
451 end,
452 "Rendering direct timeline" => fn ->
453 StatusView.render("index.json", %{
454 activities: direct_activities,
455 for: user,
456 as: :activity
457 })
458 end,
459 "Rendering public timeline" => fn ->
460 StatusView.render("index.json", %{
461 activities: public_activities,
462 for: user,
463 as: :activity
464 })
465 end,
466 "Rendering tag timeline" => fn ->
467 StatusView.render("index.json", %{
468 activities: tag_activities,
469 for: user,
470 as: :activity
471 })
472 end,
473 "Rendering notifications" => fn ->
474 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
475 notifications: notifications,
476 for: user
477 })
478 end,
479 "Rendering favourites timeline" => fn ->
480 StatusView.render("index.json", %{
481 activities: favourites,
482 for: user,
483 as: :activity
484 })
485 end
486 },
487 formatters: formatters()
488 )
489 end
490
491 defp render_long_thread(user) do
492 %{public_thread: public, private_thread: private} =
493 Agent.get(:benchmark_state, fn state -> state end)
494
495 opts = %{for: user}
496 public_activity = Activity.get_by_id_with_object(public.id)
497 private_activity = Activity.get_by_id_with_object(private.id)
498
499 Benchee.run(
500 %{
501 "render" => fn opts ->
502 StatusView.render("show.json", opts)
503 end
504 },
505 inputs: %{
506 "Public root" => Map.put(opts, :activity, public_activity),
507 "Private root" => Map.put(opts, :activity, private_activity)
508 },
509 formatters: formatters()
510 )
511
512 fetch_opts = opts_for_long_thread(user)
513
514 public_context =
515 ActivityPub.fetch_activities_for_context(
516 public.data["context"],
517 Map.put(fetch_opts, "exclude_id", public.id)
518 )
519
520 private_context =
521 ActivityPub.fetch_activities_for_context(
522 private.data["context"],
523 Map.put(fetch_opts, "exclude_id", private.id)
524 )
525
526 Benchee.run(
527 %{
528 "render" => fn opts ->
529 StatusView.render("context.json", opts)
530 end
531 },
532 inputs: %{
533 "Public context" => %{user: user, activity: public_activity, activities: public_context},
534 "Private context" => %{
535 user: user,
536 activity: private_activity,
537 activities: private_context
538 }
539 },
540 formatters: formatters()
541 )
542 end
543
544 defp fetch_timelines_with_reply_filtering(user) do
545 public_params = opts_for_public_timeline(user)
546
547 Benchee.run(
548 %{
549 "Public timeline without reply filtering" => fn ->
550 ActivityPub.fetch_public_activities(public_params)
551 end,
552 "Public timeline with reply filtering - following" => fn ->
553 public_params
554 |> Map.put("reply_visibility", "following")
555 |> Map.put("reply_filtering_user", user)
556 |> ActivityPub.fetch_public_activities()
557 end,
558 "Public timeline with reply filtering - self" => fn ->
559 public_params
560 |> Map.put("reply_visibility", "self")
561 |> Map.put("reply_filtering_user", user)
562 |> ActivityPub.fetch_public_activities()
563 end
564 },
565 formatters: formatters()
566 )
567
568 private_params = opts_for_home_timeline(user)
569
570 recipients = [user.ap_id | User.following(user)]
571
572 Benchee.run(
573 %{
574 "Home timeline without reply filtering" => fn ->
575 ActivityPub.fetch_activities(recipients, private_params)
576 end,
577 "Home timeline with reply filtering - following" => fn ->
578 private_params =
579 private_params
580 |> Map.put("reply_filtering_user", user)
581 |> Map.put("reply_visibility", "following")
582
583 ActivityPub.fetch_activities(recipients, private_params)
584 end,
585 "Home timeline with reply filtering - self" => fn ->
586 private_params =
587 private_params
588 |> Map.put("reply_filtering_user", user)
589 |> Map.put("reply_visibility", "self")
590
591 ActivityPub.fetch_activities(recipients, private_params)
592 end
593 },
594 formatters: formatters()
595 )
596 end
597 end