From 9717166d105cec6f9c878653aec2d409265e80a6 Mon Sep 17 00:00:00 2001
From: eal <eal@waifu.club>
Date: Sat, 13 Jan 2018 18:24:16 +0200
Subject: [PATCH] Add a stats agent for storing data from expensive queries.

---
 lib/pleroma/application.ex                    |  1 +
 lib/pleroma/stats.ex                          | 38 +++++++++++++++++++
 .../mastodon_api/mastodon_api_controller.ex   | 12 ++++--
 lib/pleroma/web/router.ex                     |  1 +
 4 files changed, 49 insertions(+), 3 deletions(-)
 create mode 100644 lib/pleroma/stats.ex

diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 2969ca3c4..cdfca8b1a 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -21,6 +21,7 @@ def start(_type, _args) do
                        ]]),
       worker(Pleroma.Web.Federator, []),
       worker(Pleroma.Web.ChatChannel.ChatChannelState, []),
+      worker(Pleroma.Stats, []),
     ]
     ++ if Mix.env == :test, do: [], else: [worker(Pleroma.Web.Streamer, [])]
 
diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex
new file mode 100644
index 000000000..45fa27b55
--- /dev/null
+++ b/lib/pleroma/stats.ex
@@ -0,0 +1,38 @@
+defmodule Pleroma.Stats do
+  use Agent
+  import Ecto.Query
+  alias Pleroma.{User, Repo, Activity}
+
+  def start_link do
+    agent = Agent.start_link(fn -> %{} end, name: __MODULE__)
+    schedule_update()
+    agent
+  end
+
+  def get do
+    Agent.get(__MODULE__, fn stats -> stats end)
+  end
+
+  def schedule_update do
+    update_stats()
+    spawn(fn ->
+      Process.sleep(1000 * 60 * 60 * 1) # 1 hour
+      schedule_update()
+    end)
+  end
+
+  def update_stats do
+    peers = from(u in Pleroma.User,
+      select: fragment("?->'host'", u.info),
+      where: u.local != ^true)
+    |> Repo.all() |> Enum.uniq()
+    domain_count = Enum.count(peers)
+    status_query = from p in Activity,
+      where: p.local == ^true,
+      where: fragment("?->'object'->>'type' = ?", p.data, ^"Note")
+    status_count = Repo.aggregate(status_query, :count, :id)
+    Agent.update(__MODULE__, fn _ ->
+      %{peers: peers, domain_count: domain_count, status_count: status_count}
+    end)
+  end
+end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index ffa6f42c8..6ca7a6076 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -1,6 +1,6 @@
 defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   use Pleroma.Web, :controller
-  alias Pleroma.{Repo, Activity, User, Notification}
+  alias Pleroma.{Repo, Activity, User, Notification, Stats}
   alias Pleroma.Web
   alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView}
   alias Pleroma.Web.ActivityPub.ActivityPub
@@ -94,6 +94,7 @@ def user(conn, %{"id" => id}) do
 
   def masto_instance(conn, _params) do
     user_count = Repo.aggregate(User.local_user_query, :count, :id)
+    %{domain_count: domain_count, status_count: status_count} = Stats.get()
     response = %{
       uri: Web.base_url,
       title: Keyword.get(@instance, :name),
@@ -104,9 +105,9 @@ def masto_instance(conn, _params) do
         streaming_api: String.replace(Web.base_url, ["http","https"], "wss")
       },
       stats: %{
-        status_count: 2,
+        status_count: status_count,
         user_count: user_count,
-        domain_count: 3
+        domain_count: domain_count
       },
       max_toot_chars: Keyword.get(@instance, :limit)
     }
@@ -114,6 +115,11 @@ def masto_instance(conn, _params) do
     json(conn, response)
   end
 
+  def peers(conn, _params) do
+    %{peers: peers} = Stats.get()
+    json(conn, peers)
+  end
+
   defp mastodonized_emoji do
     Pleroma.Formatter.get_custom_emoji()
     |> Enum.map(fn {shortcode, relative_url} ->
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 8e6681e68..09104fc86 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -106,6 +106,7 @@ def user_fetcher(username) do
   scope "/api/v1", Pleroma.Web.MastodonAPI do
     pipe_through :api
     get "/instance", MastodonAPIController, :masto_instance
+    get "/instance/peers", MastodonAPIController, :peers
     post "/apps", MastodonAPIController, :create_app
     get "/custom_emojis", MastodonAPIController, :custom_emojis