logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

fetcher.ex (17162B)


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