190 lines
4.4 KiB
Elixir
190 lines
4.4 KiB
Elixir
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: Pleroma.Password.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
|