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