Use race-condition free mass follow.
authorlain <lain@soykaf.club>
Wed, 30 Jan 2019 18:33:25 +0000 (19:33 +0100)
committerlain <lain@soykaf.club>
Wed, 30 Jan 2019 18:33:25 +0000 (19:33 +0100)
lib/pleroma/user.ex
test/user_test.exs

index 62a4a3db194c8715984e4ca829b2efc7e62dd7d3..bd797db40fe4b2748e90cde36c78c5481867c4ee 100644 (file)
@@ -309,20 +309,21 @@ defmodule Pleroma.User do
   @doc "A mass follow for local users. Ignores blocks and has no side effects"
   @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
   def follow_all(follower, followeds) do
-    following =
-      (follower.following ++ Enum.map(followeds, fn %{follower_address: fa} -> fa end))
-      |> Enum.uniq()
+    followed_addresses = Enum.map(followeds, fn %{follower_address: fa} -> fa end)
+
+    q =
+      from(u in User,
+        where: u.id == ^follower.id,
+        update: [set: [following: fragment("array_cat(?, ?)", u.following, ^followed_addresses)]]
+      )
 
-    {:ok, follower} =
-      follower
-      |> follow_changeset(%{following: following})
-      |> update_and_set_cache
+    {1, [follower]} = Repo.update_all(q, [], returning: true)
 
     Enum.each(followeds, fn followed ->
       update_follower_count(followed)
     end)
 
-    {:ok, follower}
+    set_cache(follower)
   end
 
   def follow(%User{} = follower, %User{info: info} = followed) do
index a0657c7b63736d6d047fc18c17b1f0f84f76ead6..cd202e360e909f7927a8ef5f6d2eef5b30c22dcb 100644 (file)
@@ -50,13 +50,19 @@ defmodule Pleroma.UserTest do
 
   test "follow_all follows mutliple users" do
     user = insert(:user)
+    followed_zero = insert(:user)
     followed_one = insert(:user)
     followed_two = insert(:user)
+    not_followed = insert(:user)
+
+    {:ok, user} = User.follow(user, followed_zero)
 
     {:ok, user} = User.follow_all(user, [followed_one, followed_two])
 
     assert User.following?(user, followed_one)
     assert User.following?(user, followed_two)
+    assert User.following?(user, followed_zero)
+    refute User.following?(user, not_followed)
   end
 
   test "follow takes a user and another user" do