logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

generator.ex (11322B)


      1 defmodule Pleroma.LoadTesting.Generator do
      2   use Pleroma.LoadTesting.Helper
      3   alias Pleroma.Web.CommonAPI
      4 
      5   def generate_like_activities(user, posts) do
      6     count_likes = Kernel.trunc(length(posts) / 4)
      7     IO.puts("Starting generating #{count_likes} like activities...")
      8 
      9     {time, _} =
     10       :timer.tc(fn ->
     11         Task.async_stream(
     12           Enum.take_random(posts, count_likes),
     13           fn post -> {:ok, _, _} = CommonAPI.favorite(post.id, user) end,
     14           max_concurrency: 10,
     15           timeout: 30_000
     16         )
     17         |> Stream.run()
     18       end)
     19 
     20     IO.puts("Inserting like activities take #{to_sec(time)} sec.\n")
     21   end
     22 
     23   def generate_users(opts) do
     24     IO.puts("Starting generating #{opts[:users_max]} users...")
     25     {time, _} = :timer.tc(fn -> do_generate_users(opts) end)
     26 
     27     IO.puts("Inserting users take #{to_sec(time)} sec.\n")
     28   end
     29 
     30   defp do_generate_users(opts) do
     31     max = Keyword.get(opts, :users_max)
     32 
     33     Task.async_stream(
     34       1..max,
     35       &generate_user_data(&1),
     36       max_concurrency: 10,
     37       timeout: 30_000
     38     )
     39     |> Enum.to_list()
     40   end
     41 
     42   defp generate_user_data(i) do
     43     remote = Enum.random([true, false])
     44 
     45     user = %User{
     46       name: "Test ใƒ†ใ‚นใƒˆ User #{i}",
     47       email: "user#{i}@example.com",
     48       nickname: "nick#{i}",
     49       password_hash:
     50         "$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
     51       bio: "Tester Number #{i}",
     52       local: remote
     53     }
     54 
     55     user_urls =
     56       if remote do
     57         base_url =
     58           Enum.random(["https://domain1.com", "https://domain2.com", "https://domain3.com"])
     59 
     60         ap_id = "#{base_url}/users/#{user.nickname}"
     61 
     62         %{
     63           ap_id: ap_id,
     64           follower_address: ap_id <> "/followers",
     65           following_address: ap_id <> "/following"
     66         }
     67       else
     68         %{
     69           ap_id: User.ap_id(user),
     70           follower_address: User.ap_followers(user),
     71           following_address: User.ap_following(user)
     72         }
     73       end
     74 
     75     user = Map.merge(user, user_urls)
     76 
     77     Repo.insert!(user)
     78   end
     79 
     80   def generate_activities(user, users) do
     81     do_generate_activities(user, users)
     82   end
     83 
     84   defp do_generate_activities(user, users) do
     85     IO.puts("Starting generating 20000 common activities...")
     86 
     87     {time, _} =
     88       :timer.tc(fn ->
     89         Task.async_stream(
     90           1..20_000,
     91           fn _ ->
     92             do_generate_activity([user | users])
     93           end,
     94           max_concurrency: 10,
     95           timeout: 30_000
     96         )
     97         |> Stream.run()
     98       end)
     99 
    100     IO.puts("Inserting common activities take #{to_sec(time)} sec.\n")
    101 
    102     IO.puts("Starting generating 20000 activities with mentions...")
    103 
    104     {time, _} =
    105       :timer.tc(fn ->
    106         Task.async_stream(
    107           1..20_000,
    108           fn _ ->
    109             do_generate_activity_with_mention(user, users)
    110           end,
    111           max_concurrency: 10,
    112           timeout: 30_000
    113         )
    114         |> Stream.run()
    115       end)
    116 
    117     IO.puts("Inserting activities with menthions take #{to_sec(time)} sec.\n")
    118 
    119     IO.puts("Starting generating 10000 activities with threads...")
    120 
    121     {time, _} =
    122       :timer.tc(fn ->
    123         Task.async_stream(
    124           1..10_000,
    125           fn _ ->
    126             do_generate_threads([user | users])
    127           end,
    128           max_concurrency: 10,
    129           timeout: 30_000
    130         )
    131         |> Stream.run()
    132       end)
    133 
    134     IO.puts("Inserting activities with threads take #{to_sec(time)} sec.\n")
    135   end
    136 
    137   defp do_generate_activity(users) do
    138     post = %{
    139       "status" => "Some status without mention with random user"
    140     }
    141 
    142     CommonAPI.post(Enum.random(users), post)
    143   end
    144 
    145   def generate_power_intervals(opts \\ []) do
    146     count = Keyword.get(opts, :count, 20)
    147     power = Keyword.get(opts, :power, 2)
    148     IO.puts("Generating #{count} intervals for a power #{power} series...")
    149     counts = Enum.map(1..count, fn n -> :math.pow(n, power) end)
    150     sum = Enum.sum(counts)
    151 
    152     densities =
    153       Enum.map(counts, fn c ->
    154         c / sum
    155       end)
    156 
    157     densities
    158     |> Enum.reduce(0, fn density, acc ->
    159       if acc == 0 do
    160         [{0, density}]
    161       else
    162         [{_, lower} | _] = acc
    163         [{lower, lower + density} | acc]
    164       end
    165     end)
    166     |> Enum.reverse()
    167   end
    168 
    169   def generate_tagged_activities(opts \\ []) do
    170     tag_count = Keyword.get(opts, :tag_count, 20)
    171     users = Keyword.get(opts, :users, Repo.all(User))
    172     activity_count = Keyword.get(opts, :count, 200_000)
    173 
    174     intervals = generate_power_intervals(count: tag_count)
    175 
    176     IO.puts(
    177       "Generating #{activity_count} activities using #{tag_count} different tags of format `tag_n`, starting at tag_0"
    178     )
    179 
    180     Enum.each(1..activity_count, fn _ ->
    181       random = :rand.uniform()
    182       i = Enum.find_index(intervals, fn {lower, upper} -> lower <= random && upper > random end)
    183       CommonAPI.post(Enum.random(users), %{"status" => "a post with the tag #tag_#{i}"})
    184     end)
    185   end
    186 
    187   defp do_generate_activity_with_mention(user, users) do
    188     mentions_cnt = Enum.random([2, 3, 4, 5])
    189     with_user = Enum.random([true, false])
    190     users = Enum.shuffle(users)
    191     mentions_users = Enum.take(users, mentions_cnt)
    192     mentions_users = if with_user, do: [user | mentions_users], else: mentions_users
    193 
    194     mentions_str =
    195       Enum.map(mentions_users, fn user -> "@" <> user.nickname end) |> Enum.join(", ")
    196 
    197     post = %{
    198       "status" => mentions_str <> "some status with mentions random users"
    199     }
    200 
    201     CommonAPI.post(Enum.random(users), post)
    202   end
    203 
    204   defp do_generate_threads(users) do
    205     thread_length = Enum.random([2, 3, 4, 5])
    206     actor = Enum.random(users)
    207 
    208     post = %{
    209       "status" => "Start of the thread"
    210     }
    211 
    212     {:ok, activity} = CommonAPI.post(actor, post)
    213 
    214     Enum.each(1..thread_length, fn _ ->
    215       user = Enum.random(users)
    216 
    217       post = %{
    218         "status" => "@#{actor.nickname} reply to thread",
    219         "in_reply_to_status_id" => activity.id
    220       }
    221 
    222       CommonAPI.post(user, post)
    223     end)
    224   end
    225 
    226   def generate_remote_activities(user, users) do
    227     do_generate_remote_activities(user, users)
    228   end
    229 
    230   defp do_generate_remote_activities(user, users) do
    231     IO.puts("Starting generating 10000 remote activities...")
    232 
    233     {time, _} =
    234       :timer.tc(fn ->
    235         Task.async_stream(
    236           1..10_000,
    237           fn i ->
    238             do_generate_remote_activity(i, user, users)
    239           end,
    240           max_concurrency: 10,
    241           timeout: 30_000
    242         )
    243         |> Stream.run()
    244       end)
    245 
    246     IO.puts("Inserting remote activities take #{to_sec(time)} sec.\n")
    247   end
    248 
    249   defp do_generate_remote_activity(i, user, users) do
    250     actor = Enum.random(users)
    251     %{host: host} = URI.parse(actor.ap_id)
    252     date = Date.utc_today()
    253     datetime = DateTime.utc_now()
    254 
    255     map = %{
    256       "actor" => actor.ap_id,
    257       "cc" => [actor.follower_address, user.ap_id],
    258       "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
    259       "id" => actor.ap_id <> "/statuses/#{i}/activity",
    260       "object" => %{
    261         "actor" => actor.ap_id,
    262         "atomUri" => actor.ap_id <> "/statuses/#{i}",
    263         "attachment" => [],
    264         "attributedTo" => actor.ap_id,
    265         "bcc" => [],
    266         "bto" => [],
    267         "cc" => [actor.follower_address, user.ap_id],
    268         "content" =>
    269           "<p><span class=\"h-card\"><a href=\"" <>
    270             user.ap_id <>
    271             "\" class=\"u-url mention\">@<span>" <> user.nickname <> "</span></a></span></p>",
    272         "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
    273         "conversation" =>
    274           "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
    275         "emoji" => %{},
    276         "id" => actor.ap_id <> "/statuses/#{i}",
    277         "inReplyTo" => nil,
    278         "inReplyToAtomUri" => nil,
    279         "published" => datetime,
    280         "sensitive" => true,
    281         "summary" => "cw",
    282         "tag" => [
    283           %{
    284             "href" => user.ap_id,
    285             "name" => "@#{user.nickname}@#{host}",
    286             "type" => "Mention"
    287           }
    288         ],
    289         "to" => ["https://www.w3.org/ns/activitystreams#Public"],
    290         "type" => "Note",
    291         "url" => "http://#{host}/@#{actor.nickname}/#{i}"
    292       },
    293       "published" => datetime,
    294       "to" => ["https://www.w3.org/ns/activitystreams#Public"],
    295       "type" => "Create"
    296     }
    297 
    298     Pleroma.Web.ActivityPub.ActivityPub.insert(map, false)
    299   end
    300 
    301   def generate_dms(user, users, opts) do
    302     IO.puts("Starting generating #{opts[:dms_max]} DMs")
    303     {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end)
    304     IO.puts("Inserting dms take #{to_sec(time)} sec.\n")
    305   end
    306 
    307   defp do_generate_dms(user, users, opts) do
    308     Task.async_stream(
    309       1..opts[:dms_max],
    310       fn _ ->
    311         do_generate_dm(user, users)
    312       end,
    313       max_concurrency: 10,
    314       timeout: 30_000
    315     )
    316     |> Stream.run()
    317   end
    318 
    319   defp do_generate_dm(user, users) do
    320     post = %{
    321       "status" => "@#{user.nickname} some direct message",
    322       "visibility" => "direct"
    323     }
    324 
    325     CommonAPI.post(Enum.random(users), post)
    326   end
    327 
    328   def generate_long_thread(user, users, opts) do
    329     IO.puts("Starting generating long thread with #{opts[:thread_length]} replies")
    330     {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end)
    331     IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n")
    332     {:ok, activity}
    333   end
    334 
    335   defp do_generate_long_thread(user, users, opts) do
    336     {:ok, %{id: id} = activity} = CommonAPI.post(user, %{"status" => "Start of long thread"})
    337 
    338     Task.async_stream(
    339       1..opts[:thread_length],
    340       fn _ -> do_generate_thread(users, id) end,
    341       max_concurrency: 10,
    342       timeout: 30_000
    343     )
    344     |> Stream.run()
    345 
    346     activity
    347   end
    348 
    349   defp do_generate_thread(users, activity_id) do
    350     CommonAPI.post(Enum.random(users), %{
    351       "status" => "reply to main post",
    352       "in_reply_to_status_id" => activity_id
    353     })
    354   end
    355 
    356   def generate_non_visible_message(user, users) do
    357     IO.puts("Starting generating 1000 non visible posts")
    358 
    359     {time, _} =
    360       :timer.tc(fn ->
    361         do_generate_non_visible_posts(user, users)
    362       end)
    363 
    364     IO.puts("Inserting non visible posts take #{to_sec(time)} sec.\n")
    365   end
    366 
    367   defp do_generate_non_visible_posts(user, users) do
    368     [not_friend | users] = users
    369 
    370     make_friends(user, users)
    371 
    372     Task.async_stream(1..1000, fn _ -> do_generate_non_visible_post(not_friend, users) end,
    373       max_concurrency: 10,
    374       timeout: 30_000
    375     )
    376     |> Stream.run()
    377   end
    378 
    379   defp make_friends(_user, []), do: nil
    380 
    381   defp make_friends(user, [friend | users]) do
    382     {:ok, _} = User.follow(user, friend)
    383     {:ok, _} = User.follow(friend, user)
    384     make_friends(user, users)
    385   end
    386 
    387   defp do_generate_non_visible_post(not_friend, users) do
    388     post = %{
    389       "status" => "some non visible post",
    390       "visibility" => "private"
    391     }
    392 
    393     {:ok, activity} = CommonAPI.post(not_friend, post)
    394 
    395     thread_length = Enum.random([2, 3, 4, 5])
    396 
    397     Enum.each(1..thread_length, fn _ ->
    398       user = Enum.random(users)
    399 
    400       post = %{
    401         "status" => "@#{not_friend.nickname} reply to non visible post",
    402         "in_reply_to_status_id" => activity.id,
    403         "visibility" => "private"
    404       }
    405 
    406       CommonAPI.post(user, post)
    407     end)
    408   end
    409 end