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