update counter_cache logic
authorRoman Chvanikov <chvanikoff@pm.me>
Fri, 8 May 2020 22:20:50 +0000 (01:20 +0300)
committerRoman Chvanikov <chvanikoff@pm.me>
Fri, 8 May 2020 22:20:50 +0000 (01:20 +0300)
lib/mix/tasks/pleroma/refresh_counter_cache.ex
lib/pleroma/counter_cache.ex
lib/pleroma/stats.ex

index 15b4dbfa681c9e86bbde1d494121ebff0dc2d3bf..280201befcfd0ad0fbeabe6bac4aa51afaeeb8ba 100644 (file)
@@ -17,30 +17,46 @@ defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
   def run([]) do
     Mix.Pleroma.start_pleroma()
 
-    ["public", "unlisted", "private", "direct"]
-    |> Enum.each(fn visibility ->
-      count = status_visibility_count_query(visibility)
-      name = "status_visibility_#{visibility}"
-      CounterCache.set(name, count)
-      Mix.Pleroma.shell_info("Set #{name} to #{count}")
+    Activity
+    |> distinct([a], true)
+    |> select([a], fragment("split_part(?, '/', 3)", a.actor))
+    |> Repo.all()
+    |> Enum.each(fn instance ->
+      counters = instance_counters(instance)
+      CounterCache.set(instance, counters)
+      Mix.Pleroma.shell_info("Setting #{instance} counters: #{inspect(counters)}")
     end)
 
     Mix.Pleroma.shell_info("Done")
   end
 
-  defp status_visibility_count_query(visibility) do
+  defp instance_counters(instance) do
+    counters = %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
+
     Activity
-    |> where(
+    |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
+    |> where([a], like(a.actor, ^"%#{instance}%"))
+    |> select(
+      [a],
+      {fragment(
+         "activity_visibility(?, ?, ?)",
+         a.actor,
+         a.recipients,
+         a.data
+       ), count(a.id)}
+    )
+    |> group_by(
       [a],
       fragment(
-        "activity_visibility(?, ?, ?) = ?",
+        "activity_visibility(?, ?, ?)",
         a.actor,
         a.recipients,
-        a.data,
-        ^visibility
+        a.data
       )
     )
-    |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
-    |> Repo.aggregate(:count, :id, timeout: :timer.minutes(30))
+    |> Repo.all(timeout: :timer.minutes(30))
+    |> Enum.reduce(counters, fn {visibility, count}, acc ->
+      Map.put(acc, visibility, count)
+    end)
   end
 end
index 4d348a4134915a783d08547ed8fffae233721d5c..b469e7b50846da0fb64d194fd6389c8d36c1be42 100644 (file)
@@ -10,32 +10,70 @@ defmodule Pleroma.CounterCache do
   import Ecto.Query
 
   schema "counter_cache" do
-    field(:name, :string)
-    field(:count, :integer)
+    field(:instance, :string)
+    field(:public, :integer)
+    field(:unlisted, :integer)
+    field(:private, :integer)
+    field(:direct, :integer)
   end
 
   def changeset(struct, params) do
     struct
-    |> cast(params, [:name, :count])
-    |> validate_required([:name])
-    |> unique_constraint(:name)
+    |> cast(params, [:instance, :public, :unlisted, :private, :direct])
+    |> validate_required([:instance])
+    |> unique_constraint(:instance)
   end
 
-  def get_as_map(names) when is_list(names) do
+  def get_by_instance(instance) do
     CounterCache
-    |> where([cc], cc.name in ^names)
-    |> Repo.all()
-    |> Enum.group_by(& &1.name, & &1.count)
-    |> Map.new(fn {k, v} -> {k, hd(v)} end)
+    |> select([c], %{
+      "public" => c.public,
+      "unlisted" => c.unlisted,
+      "private" => c.private,
+      "direct" => c.direct
+    })
+    |> where([c], c.instance == ^instance)
+    |> Repo.one()
+    |> case do
+      nil -> %{"public" => 0, "unlisted" => 0, "private" => 0, "direct" => 0}
+      val -> val
+    end
   end
 
-  def set(name, count) do
+  def get_as_map() do
+    CounterCache
+    |> select([c], %{
+      "public" => sum(c.public),
+      "unlisted" => sum(c.unlisted),
+      "private" => sum(c.private),
+      "direct" => sum(c.direct)
+    })
+    |> Repo.one()
+  end
+
+  def set(instance, values) do
+    params =
+      Enum.reduce(
+        ["public", "private", "unlisted", "direct"],
+        %{"instance" => instance},
+        fn param, acc ->
+          Map.put_new(acc, param, Map.get(values, param, 0))
+        end
+      )
+
     %CounterCache{}
-    |> changeset(%{"name" => name, "count" => count})
+    |> changeset(params)
     |> Repo.insert(
-      on_conflict: [set: [count: count]],
+      on_conflict: [
+        set: [
+          public: params["public"],
+          private: params["private"],
+          unlisted: params["unlisted"],
+          direct: params["direct"]
+        ]
+      ],
       returning: true,
-      conflict_target: :name
+      conflict_target: :instance
     )
   end
 end
index 6b3a8a41f738801e8ffc0817d066cd3212d0ef74..4e355bd5ca631546e4b0f3672caeb09f628f2dff 100644 (file)
@@ -98,13 +98,7 @@ defmodule Pleroma.Stats do
   end
 
   def get_status_visibility_count do
-    counter_cache =
-      CounterCache.get_as_map([
-        "status_visibility_public",
-        "status_visibility_private",
-        "status_visibility_unlisted",
-        "status_visibility_direct"
-      ])
+    counter_cache = CounterCache.get_as_map()
 
     %{
       public: counter_cache["status_visibility_public"] || 0,