Merge branch 'develop' into issue/1276
[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, :local)
40 fetch_public_timeline(user, :tag)
41 fetch_notifications(user)
42 fetch_favourites(user)
43 fetch_long_thread(user)
44 end
45
46 defp render_views(user) do
47 render_timelines(user)
48 render_long_thread(user)
49 end
50
51 defp opts_for_home_timeline(user) do
52 %{
53 "blocking_user" => user,
54 "count" => "20",
55 "muting_user" => user,
56 "type" => ["Create", "Announce"],
57 "user" => user,
58 "with_muted" => "true"
59 }
60 end
61
62 defp fetch_home_timeline(user) do
63 opts = opts_for_home_timeline(user)
64
65 recipients = [user.ap_id | User.following(user)]
66
67 first_page_last =
68 ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
69
70 second_page_last =
71 ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", first_page_last.id))
72 |> Enum.reverse()
73 |> List.last()
74
75 third_page_last =
76 ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", second_page_last.id))
77 |> Enum.reverse()
78 |> List.last()
79
80 forth_page_last =
81 ActivityPub.fetch_activities(recipients, Map.put(opts, "max_id", third_page_last.id))
82 |> Enum.reverse()
83 |> List.last()
84
85 Benchee.run(
86 %{
87 "home timeline" => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
88 },
89 inputs: %{
90 "1 page" => opts,
91 "2 page" => Map.put(opts, "max_id", first_page_last.id),
92 "3 page" => Map.put(opts, "max_id", second_page_last.id),
93 "4 page" => Map.put(opts, "max_id", third_page_last.id),
94 "5 page" => Map.put(opts, "max_id", forth_page_last.id),
95 "1 page only media" => Map.put(opts, "only_media", "true"),
96 "2 page only media" =>
97 Map.put(opts, "max_id", first_page_last.id) |> Map.put("only_media", "true"),
98 "3 page only media" =>
99 Map.put(opts, "max_id", second_page_last.id) |> Map.put("only_media", "true"),
100 "4 page only media" =>
101 Map.put(opts, "max_id", third_page_last.id) |> Map.put("only_media", "true"),
102 "5 page only media" =>
103 Map.put(opts, "max_id", forth_page_last.id) |> Map.put("only_media", "true")
104 },
105 formatters: formatters()
106 )
107 end
108
109 defp opts_for_direct_timeline(user) do
110 %{
111 :visibility => "direct",
112 "blocking_user" => user,
113 "count" => "20",
114 "type" => "Create",
115 "user" => user,
116 "with_muted" => "true"
117 }
118 end
119
120 defp fetch_direct_timeline(user) do
121 recipients = [user.ap_id]
122
123 opts = opts_for_direct_timeline(user)
124
125 first_page_last =
126 recipients
127 |> ActivityPub.fetch_activities_query(opts)
128 |> Pagination.fetch_paginated(opts)
129 |> List.last()
130
131 opts2 = Map.put(opts, "max_id", first_page_last.id)
132
133 second_page_last =
134 recipients
135 |> ActivityPub.fetch_activities_query(opts2)
136 |> Pagination.fetch_paginated(opts2)
137 |> List.last()
138
139 opts3 = Map.put(opts, "max_id", second_page_last.id)
140
141 third_page_last =
142 recipients
143 |> ActivityPub.fetch_activities_query(opts3)
144 |> Pagination.fetch_paginated(opts3)
145 |> List.last()
146
147 opts4 = Map.put(opts, "max_id", third_page_last.id)
148
149 forth_page_last =
150 recipients
151 |> ActivityPub.fetch_activities_query(opts4)
152 |> Pagination.fetch_paginated(opts4)
153 |> List.last()
154
155 Benchee.run(
156 %{
157 "direct timeline" => fn opts ->
158 ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
159 end
160 },
161 inputs: %{
162 "1 page" => opts,
163 "2 page" => opts2,
164 "3 page" => opts3,
165 "4 page" => opts4,
166 "5 page" => Map.put(opts4, "max_id", forth_page_last.id)
167 },
168 formatters: formatters()
169 )
170 end
171
172 defp opts_for_public_timeline(user) do
173 %{
174 "type" => ["Create", "Announce"],
175 "local_only" => false,
176 "blocking_user" => user,
177 "muting_user" => user
178 }
179 end
180
181 defp opts_for_public_timeline(user, :local) do
182 %{
183 "type" => ["Create", "Announce"],
184 "local_only" => true,
185 "blocking_user" => user,
186 "muting_user" => user
187 }
188 end
189
190 defp opts_for_public_timeline(user, :tag) do
191 %{
192 "blocking_user" => user,
193 "count" => "20",
194 "local_only" => nil,
195 "muting_user" => user,
196 "tag" => ["tag"],
197 "tag_all" => [],
198 "tag_reject" => [],
199 "type" => "Create",
200 "user" => user,
201 "with_muted" => "true"
202 }
203 end
204
205 defp fetch_public_timeline(user) do
206 opts = opts_for_public_timeline(user)
207
208 fetch_public_timeline(opts, "public timeline")
209 end
210
211 defp fetch_public_timeline(user, :local) do
212 opts = opts_for_public_timeline(user, :local)
213
214 fetch_public_timeline(opts, "public timeline only local")
215 end
216
217 defp fetch_public_timeline(user, :tag) do
218 opts = opts_for_public_timeline(user, :tag)
219
220 fetch_public_timeline(opts, "hashtag timeline")
221 end
222
223 defp fetch_public_timeline(user, :only_media) do
224 opts = opts_for_public_timeline(user) |> Map.put("only_media", "true")
225
226 fetch_public_timeline(opts, "public timeline only media")
227 end
228
229 defp fetch_public_timeline(opts, title) when is_binary(title) do
230 first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
231
232 second_page_last =
233 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", first_page_last.id))
234 |> List.last()
235
236 third_page_last =
237 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", second_page_last.id))
238 |> List.last()
239
240 forth_page_last =
241 ActivityPub.fetch_public_activities(Map.put(opts, "max_id", third_page_last.id))
242 |> List.last()
243
244 Benchee.run(
245 %{
246 title => fn opts ->
247 ActivityPub.fetch_public_activities(opts)
248 end
249 },
250 inputs: %{
251 "1 page" => opts,
252 "2 page" => Map.put(opts, "max_id", first_page_last.id),
253 "3 page" => Map.put(opts, "max_id", second_page_last.id),
254 "4 page" => Map.put(opts, "max_id", third_page_last.id),
255 "5 page" => Map.put(opts, "max_id", forth_page_last.id)
256 },
257 formatters: formatters()
258 )
259 end
260
261 defp opts_for_notifications do
262 %{"count" => "20", "with_muted" => "true"}
263 end
264
265 defp fetch_notifications(user) do
266 opts = opts_for_notifications()
267
268 first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
269
270 second_page_last =
271 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", first_page_last.id))
272 |> List.last()
273
274 third_page_last =
275 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", second_page_last.id))
276 |> List.last()
277
278 forth_page_last =
279 MastodonAPI.get_notifications(user, Map.put(opts, "max_id", third_page_last.id))
280 |> List.last()
281
282 Benchee.run(
283 %{
284 "Notifications" => fn opts ->
285 MastodonAPI.get_notifications(user, opts)
286 end
287 },
288 inputs: %{
289 "1 page" => opts,
290 "2 page" => Map.put(opts, "max_id", first_page_last.id),
291 "3 page" => Map.put(opts, "max_id", second_page_last.id),
292 "4 page" => Map.put(opts, "max_id", third_page_last.id),
293 "5 page" => Map.put(opts, "max_id", forth_page_last.id)
294 },
295 formatters: formatters()
296 )
297 end
298
299 defp fetch_favourites(user) do
300 first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
301
302 second_page_last =
303 ActivityPub.fetch_favourites(user, %{"max_id" => first_page_last.id}) |> List.last()
304
305 third_page_last =
306 ActivityPub.fetch_favourites(user, %{"max_id" => second_page_last.id}) |> List.last()
307
308 forth_page_last =
309 ActivityPub.fetch_favourites(user, %{"max_id" => third_page_last.id}) |> List.last()
310
311 Benchee.run(
312 %{
313 "Favourites" => fn opts ->
314 ActivityPub.fetch_favourites(user, opts)
315 end
316 },
317 inputs: %{
318 "1 page" => %{},
319 "2 page" => %{"max_id" => first_page_last.id},
320 "3 page" => %{"max_id" => second_page_last.id},
321 "4 page" => %{"max_id" => third_page_last.id},
322 "5 page" => %{"max_id" => forth_page_last.id}
323 },
324 formatters: formatters()
325 )
326 end
327
328 defp opts_for_long_thread(user) do
329 %{
330 "blocking_user" => user,
331 "user" => user
332 }
333 end
334
335 defp fetch_long_thread(user) do
336 %{public_thread: public, private_thread: private} =
337 Agent.get(:benchmark_state, fn state -> state end)
338
339 opts = opts_for_long_thread(user)
340
341 private_input = {private.data["context"], Map.put(opts, "exclude_id", private.id)}
342
343 public_input = {public.data["context"], Map.put(opts, "exclude_id", public.id)}
344
345 Benchee.run(
346 %{
347 "fetch context" => fn {context, opts} ->
348 ActivityPub.fetch_activities_for_context(context, opts)
349 end
350 },
351 inputs: %{
352 "Private long thread" => private_input,
353 "Public long thread" => public_input
354 },
355 formatters: formatters()
356 )
357 end
358
359 defp render_timelines(user) do
360 opts = opts_for_home_timeline(user)
361
362 recipients = [user.ap_id | User.following(user)]
363
364 home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
365
366 recipients = [user.ap_id]
367
368 opts = opts_for_direct_timeline(user)
369
370 direct_activities =
371 recipients
372 |> ActivityPub.fetch_activities_query(opts)
373 |> Pagination.fetch_paginated(opts)
374
375 opts = opts_for_public_timeline(user)
376
377 public_activities = ActivityPub.fetch_public_activities(opts)
378
379 opts = opts_for_public_timeline(user, :tag)
380
381 tag_activities = ActivityPub.fetch_public_activities(opts)
382
383 opts = opts_for_notifications()
384
385 notifications = MastodonAPI.get_notifications(user, opts)
386
387 favourites = ActivityPub.fetch_favourites(user)
388
389 Benchee.run(
390 %{
391 "Rendering home timeline" => fn ->
392 StatusView.render("index.json", %{
393 activities: home_activities,
394 for: user,
395 as: :activity
396 })
397 end,
398 "Rendering direct timeline" => fn ->
399 StatusView.render("index.json", %{
400 activities: direct_activities,
401 for: user,
402 as: :activity
403 })
404 end,
405 "Rendering public timeline" => fn ->
406 StatusView.render("index.json", %{
407 activities: public_activities,
408 for: user,
409 as: :activity
410 })
411 end,
412 "Rendering tag timeline" => fn ->
413 StatusView.render("index.json", %{
414 activities: tag_activities,
415 for: user,
416 as: :activity
417 })
418 end,
419 "Rendering notifications" => fn ->
420 Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
421 notifications: notifications,
422 for: user
423 })
424 end,
425 "Rendering favourites timeline" => fn ->
426 StatusView.render("index.json", %{
427 activities: favourites,
428 for: user,
429 as: :activity
430 })
431 end
432 },
433 formatters: formatters()
434 )
435 end
436
437 defp render_long_thread(user) do
438 %{public_thread: public, private_thread: private} =
439 Agent.get(:benchmark_state, fn state -> state end)
440
441 opts = %{for: user}
442 public_activity = Activity.get_by_id_with_object(public.id)
443 private_activity = Activity.get_by_id_with_object(private.id)
444
445 Benchee.run(
446 %{
447 "render" => fn opts ->
448 StatusView.render("show.json", opts)
449 end
450 },
451 inputs: %{
452 "Public root" => Map.put(opts, :activity, public_activity),
453 "Private root" => Map.put(opts, :activity, private_activity)
454 },
455 formatters: formatters()
456 )
457
458 fetch_opts = opts_for_long_thread(user)
459
460 public_context =
461 ActivityPub.fetch_activities_for_context(
462 public.data["context"],
463 Map.put(fetch_opts, "exclude_id", public.id)
464 )
465
466 private_context =
467 ActivityPub.fetch_activities_for_context(
468 private.data["context"],
469 Map.put(fetch_opts, "exclude_id", private.id)
470 )
471
472 Benchee.run(
473 %{
474 "render" => fn opts ->
475 StatusView.render("context.json", opts)
476 end
477 },
478 inputs: %{
479 "Public context" => %{user: user, activity: public_activity, activities: public_context},
480 "Private context" => %{
481 user: user,
482 activity: private_activity,
483 activities: private_context
484 }
485 },
486 formatters: formatters()
487 )
488 end
489 end