summaryrefslogtreecommitdiff
path: root/benchmarks/load_testing/users.ex
diff options
context:
space:
mode:
authorlain <lain@soykaf.club>2020-08-28 12:17:19 +0000
committerlain <lain@soykaf.club>2020-08-28 12:17:19 +0000
commit73dd5bdb7dcdf804bdbabcf632671d4de5042ebc (patch)
treeefcb0b8e68f86d067de98a23f40a24c7dab79d2f /benchmarks/load_testing/users.ex
parentf891e2b2f1d1daa122b9856e4b660be394d31e34 (diff)
parentb141e35d641e733dffe7bd6a45a5bbcafe586c56 (diff)
Merge branch 'release/2.1.0' into 'stable'v2.1.0
Release/2.1.0 See merge request pleroma/pleroma!2927
Diffstat (limited to 'benchmarks/load_testing/users.ex')
-rw-r--r--benchmarks/load_testing/users.ex189
1 files changed, 189 insertions, 0 deletions
diff --git a/benchmarks/load_testing/users.ex b/benchmarks/load_testing/users.ex
new file mode 100644
index 000000000..6cf3958c1
--- /dev/null
+++ b/benchmarks/load_testing/users.ex
@@ -0,0 +1,189 @@
+defmodule Pleroma.LoadTesting.Users do
+ @moduledoc """
+ Module for generating users with friends.
+ """
+ import Ecto.Query
+ import Pleroma.LoadTesting.Helper, only: [to_sec: 1]
+
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.User.Query
+
+ @defaults [
+ users: 20_000,
+ friends: 100
+ ]
+
+ @max_concurrency 10
+
+ @spec generate(keyword()) :: User.t()
+ def generate(opts \\ []) do
+ opts = Keyword.merge(@defaults, opts)
+
+ generate_users(opts[:users])
+
+ main_user =
+ Repo.one(from(u in User, where: u.local == true, order_by: fragment("RANDOM()"), limit: 1))
+
+ make_friends(main_user, opts[:friends])
+
+ User.get_by_id(main_user.id)
+ end
+
+ def generate_users(max) do
+ IO.puts("Starting generating #{max} users...")
+
+ {time, users} =
+ :timer.tc(fn ->
+ Task.async_stream(
+ 1..max,
+ &generate_user(&1),
+ max_concurrency: @max_concurrency,
+ timeout: 30_000
+ )
+ |> Enum.to_list()
+ end)
+
+ IO.puts("Generating users took #{to_sec(time)} sec.\n")
+ users
+ end
+
+ defp generate_user(i) do
+ remote = Enum.random([true, false])
+
+ %User{
+ name: "Test ใƒ†ใ‚นใƒˆ User #{i}",
+ email: "user#{i}@example.com",
+ nickname: "nick#{i}",
+ password_hash: Pbkdf2.hash_pwd_salt("test"),
+ bio: "Tester Number #{i}",
+ local: !remote
+ }
+ |> user_urls()
+ |> Repo.insert!()
+ end
+
+ defp user_urls(%{local: true} = user) do
+ urls = %{
+ ap_id: User.ap_id(user),
+ follower_address: User.ap_followers(user),
+ following_address: User.ap_following(user)
+ }
+
+ Map.merge(user, urls)
+ end
+
+ defp user_urls(%{local: false} = user) do
+ base_domain = Enum.random(["domain1.com", "domain2.com", "domain3.com"])
+
+ ap_id = "https://#{base_domain}/users/#{user.nickname}"
+
+ urls = %{
+ ap_id: ap_id,
+ follower_address: ap_id <> "/followers",
+ following_address: ap_id <> "/following"
+ }
+
+ Map.merge(user, urls)
+ end
+
+ def make_friends(main_user, max) when is_integer(max) do
+ IO.puts("Starting making friends for #{max} users...")
+
+ {time, _} =
+ :timer.tc(fn ->
+ number_of_users =
+ (max / 2)
+ |> Kernel.trunc()
+
+ main_user
+ |> get_users(%{limit: number_of_users, local: :local})
+ |> run_stream(main_user)
+
+ main_user
+ |> get_users(%{limit: number_of_users, local: :external})
+ |> run_stream(main_user)
+ end)
+
+ IO.puts("Making friends took #{to_sec(time)} sec.\n")
+ end
+
+ def make_friends(%User{} = main_user, %User{} = user) do
+ {:ok, _} = User.follow(main_user, user)
+ {:ok, _} = User.follow(user, main_user)
+ end
+
+ @spec get_users(User.t(), keyword()) :: [User.t()]
+ def get_users(user, opts) do
+ criteria = %{limit: opts[:limit]}
+
+ criteria =
+ if opts[:local] do
+ Map.put(criteria, opts[:local], true)
+ else
+ criteria
+ end
+
+ criteria =
+ if opts[:friends?] do
+ Map.put(criteria, :friends, user)
+ else
+ criteria
+ end
+
+ query =
+ criteria
+ |> Query.build()
+ |> random_without_user(user)
+
+ query =
+ if opts[:friends?] == false do
+ friends_ids =
+ %{friends: user}
+ |> Query.build()
+ |> Repo.all()
+ |> Enum.map(& &1.id)
+
+ from(u in query, where: u.id not in ^friends_ids)
+ else
+ query
+ end
+
+ Repo.all(query)
+ end
+
+ defp random_without_user(query, user) do
+ from(u in query,
+ where: u.id != ^user.id,
+ order_by: fragment("RANDOM()")
+ )
+ end
+
+ defp run_stream(users, main_user) do
+ Task.async_stream(users, &make_friends(main_user, &1),
+ max_concurrency: @max_concurrency,
+ timeout: 30_000
+ )
+ |> Stream.run()
+ end
+
+ @spec prepare_users(User.t(), keyword()) :: map()
+ def prepare_users(user, opts) do
+ friends_limit = opts[:friends_used]
+ non_friends_limit = opts[:non_friends_used]
+
+ %{
+ user: user,
+ friends_local: fetch_users(user, friends_limit, :local, true),
+ friends_remote: fetch_users(user, friends_limit, :external, true),
+ non_friends_local: fetch_users(user, non_friends_limit, :local, false),
+ non_friends_remote: fetch_users(user, non_friends_limit, :external, false)
+ }
+ end
+
+ defp fetch_users(user, limit, local, friends?) do
+ user
+ |> get_users(limit: limit, local: local, friends?: friends?)
+ |> Enum.shuffle()
+ end
+end