Merge remote-tracking branch 'remotes/origin/develop' into 2168-media-preview-proxy
[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 "public timeline with user block" => fn ->
252 ActivityPub.fetch_public_activities(opts)
253 end
254 })
255
256 domains =
257 Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
258 {:ok, _user} = User.unblock(user, non_friend)
259 %{host: host} = URI.parse(non_friend.ap_id)
260 [host | domains]
261 end)
262
263 domains = Enum.uniq(domains)
264
265 Enum.each(domains, fn domain ->
266 {:ok, _} = User.block_domain(user, domain)
267 end)
268
269 user = User.get_by_id(user.id)
270 opts = Map.put(opts, :blocking_user, user)
271
272 Benchee.run(%{
273 "public timeline with domain block" => fn ->
274 ActivityPub.fetch_public_activities(opts)
275 end
276 })
277 end
278
279 defp fetch_public_timeline(opts, title) when is_binary(title) do
280 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
281
282 second_page_last =
283 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, first_page_last.id))
284 |> List.last()
285
286 third_page_last =
287 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, second_page_last.id))
288 |> List.last()
289
290 forth_page_last =
291 ActivityPub.fetch_public_activities(Map.put(opts, :max_id, third_page_last.id))
292 |> List.last()
293
294 Benchee.run(
295 %{
296 title => fn opts ->
297 ActivityPub.fetch_public_activities(opts)
298 end
299 },
300 inputs: %{
301 "1 page" => opts,
302 "2 page" => Map.put(opts, :max_id, first_page_last.id),
303 "3 page" => Map.put(opts, :max_id, second_page_last.id),
304 "4 page" => Map.put(opts, :max_id, third_page_last.id),
305 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
306 },
307 formatters: formatters()
308 )
309 end
310
311 defp opts_for_notifications do
312 %{count: "20", with_muted: true}
313 end
314
315 defp fetch_notifications(user) do
316 opts = opts_for_notifications()
317
318 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
319
320 second_page_last =
321 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, first_page_last.id))
322 |> List.last()
323
324 third_page_last =
325 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, second_page_last.id))
326 |> List.last()
327
328 forth_page_last =
329 MastodonAPI.get_notifications(user, Map.put(opts, :max_id, third_page_last.id))
330 |> List.last()
331
332 Benchee.run(
333 %{
334 "Notifications" => fn opts ->
335 MastodonAPI.get_notifications(user, opts)
336 end
337 },
338 inputs: %{
339 "1 page" => opts,
340 "2 page" => Map.put(opts, :max_id, first_page_last.id),
341 "3 page" => Map.put(opts, :max_id, second_page_last.id),
342 "4 page" => Map.put(opts, :max_id, third_page_last.id),
343 "5 page" => Map.put(opts, :max_id, forth_page_last.id)
344 },
345 formatters: formatters()
346 )
347 end
348
349 defp fetch_favourites(user) do
350 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
351
352 second_page_last =
353 ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
354
355 third_page_last =
356 ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
357
358 forth_page_last =
359 ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
360
361 Benchee.run(
362 %{
363 "Favourites" => fn opts ->
364 ActivityPub.fetch_favourites(user, opts)
365 end
366 },
367 inputs: %{
368 "1 page" => %{},
369 "2 page" => %{:max_id => first_page_last.id},
370 "3 page" => %{:max_id => second_page_last.id},
371 "4 page" => %{:max_id => third_page_last.id},
372 "5 page" => %{:max_id => forth_page_last.id}
373 },
374 formatters: formatters()
375 )
376 end
377
378 defp opts_for_long_thread(user) do
379 %{
380 blocking_user: user,
381 user: user
382 }
383 end
384
385 defp fetch_long_thread(user) do
386 %{public_thread: public, private_thread: private} =
387 Agent.get(:benchmark_state, fn state -> state end)
388
389 opts = opts_for_long_thread(user)
390
391 private_input = {private.data["context"], Map.put(opts, :exclude_id, private.id)}
392
393 public_input = {public.data["context"], Map.put(opts, :exclude_id, public.id)}
394
395 Benchee.run(
396 %{
397 "fetch context" => fn {context, opts} ->
398 ActivityPub.fetch_activities_for_context(context, opts)
399 end
400 },
401 inputs: %{
402 "Private long thread" => private_input,
403 "Public long thread" => public_input
404 },
405 formatters: formatters()
406 )
407 end
408
409 defp render_timelines(user) do
410 opts = opts_for_home_timeline(user)
411
412 recipients = [user.ap_id | User.following(user)]
413
414 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
415
416 recipients = [user.ap_id]
417
418 opts = opts_for_direct_timeline(user)
419
420 direct_activities =
421 recipients
422 |> ActivityPub.fetch_activities_query(opts)
423 |> Pagination.fetch_paginated(opts)
424
425 opts = opts_for_public_timeline(user)
426
427 public_activities = ActivityPub.fetch_public_activities(opts)
428
429 opts = opts_for_public_timeline(user, :tag)
430
431 tag_activities = ActivityPub.fetch_public_activities(opts)
432
433 opts = opts_for_notifications()
434
435 notifications = MastodonAPI.get_notifications(user, opts)
436
437 favourites = ActivityPub.fetch_favourites(user)
438
439 Benchee.run(
440 %{
441 "Rendering home timeline" => fn ->
442 StatusView.render("index.json", %{
443 activities: home_activities,
444 for: user,
445 as: :activity
446 })
447 end,
448 "Rendering direct timeline" => fn ->
449 StatusView.render("index.json", %{
450 activities: direct_activities,
451 for: user,
452 as: :activity
453 })
454 end,
455 "Rendering public timeline" => fn ->
456 StatusView.render("index.json", %{
457 activities: public_activities,
458 for: user,
459 as: :activity
460 })
461 end,
462 "Rendering tag timeline" => fn ->
463 StatusView.render("index.json", %{
464 activities: tag_activities,
465 for: user,
466 as: :activity
467 })
468 end,
469 "Rendering notifications" => fn ->
470 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
471 notifications: notifications,
472 for: user
473 })
474 end,
475 "Rendering favourites timeline" => fn ->
476 StatusView.render("index.json", %{
477 activities: favourites,
478 for: user,
479 as: :activity
480 })
481 end
482 },
483 formatters: formatters()
484 )
485 end
486
487 defp render_long_thread(user) do
488 %{public_thread: public, private_thread: private} =
489 Agent.get(:benchmark_state, fn state -> state end)
490
491 opts = %{for: user}
492 public_activity = Activity.get_by_id_with_object(public.id)
493 private_activity = Activity.get_by_id_with_object(private.id)
494
495 Benchee.run(
496 %{
497 "render" => fn opts ->
498 StatusView.render("show.json", opts)
499 end
500 },
501 inputs: %{
502 "Public root" => Map.put(opts, :activity, public_activity),
503 "Private root" => Map.put(opts, :activity, private_activity)
504 },
505 formatters: formatters()
506 )
507
508 fetch_opts = opts_for_long_thread(user)
509
510 public_context =
511 ActivityPub.fetch_activities_for_context(
512 public.data["context"],
513 Map.put(fetch_opts, :exclude_id, public.id)
514 )
515
516 private_context =
517 ActivityPub.fetch_activities_for_context(
518 private.data["context"],
519 Map.put(fetch_opts, :exclude_id, private.id)
520 )
521
522 Benchee.run(
523 %{
524 "render" => fn opts ->
525 StatusView.render("context.json", opts)
526 end
527 },
528 inputs: %{
529 "Public context" => %{user: user, activity: public_activity, activities: public_context},
530 "Private context" => %{
531 user: user,
532 activity: private_activity,
533 activities: private_context
534 }
535 },
536 formatters: formatters()
537 )
538 end
539
540 defp fetch_timelines_with_reply_filtering(user) do
541 public_params = opts_for_public_timeline(user)
542
543 Benchee.run(
544 %{
545 "Public timeline without reply filtering" => fn ->
546 ActivityPub.fetch_public_activities(public_params)
547 end,
548 "Public timeline with reply filtering - following" => fn ->
549 public_params
550 |> Map.put(:reply_visibility, "following")
551 |> Map.put(:reply_filtering_user, user)
552 |> ActivityPub.fetch_public_activities()
553 end,
554 "Public timeline with reply filtering - self" => fn ->
555 public_params
556 |> Map.put(:reply_visibility, "self")
557 |> Map.put(:reply_filtering_user, user)
558 |> ActivityPub.fetch_public_activities()
559 end
560 },
561 formatters: formatters()
562 )
563
564 private_params = opts_for_home_timeline(user)
565
566 recipients = [user.ap_id | User.following(user)]
567
568 Benchee.run(
569 %{
570 "Home timeline without reply filtering" => fn ->
571 ActivityPub.fetch_activities(recipients, private_params)
572 end,
573 "Home timeline with reply filtering - following" => fn ->
574 private_params =
575 private_params
576 |> Map.put(:reply_filtering_user, user)
577 |> Map.put(:reply_visibility, "following")
578
579 ActivityPub.fetch_activities(recipients, private_params)
580 end,
581 "Home timeline with reply filtering - self" => fn ->
582 private_params =
583 private_params
584 |> Map.put(:reply_filtering_user, user)
585 |> Map.put(:reply_visibility, "self")
586
587 ActivityPub.fetch_activities(recipients, private_params)
588 end
589 },
590 formatters: formatters()
591 )
592 end
593 end