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