stats: estimate remote user count

This value is currently only used by Prometheus metrics
but (after optimisng the peer query inthe preceeding commit)
the most costly part of instance stats.
This commit is contained in:
Oneric 2024-12-11 03:03:14 +01:00
parent 138b1aea2f
commit 8e5defe6ca
3 changed files with 49 additions and 11 deletions

View file

@ -12,6 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Changed
- Dropped obsolete `ap_enabled` indicator from user table and associated buggy logic
- The remote user count in prometheus metrics is now an estimate instead of an exact number
since the latter proved unreasonably costly to obtain for a merely nice-to-have statistic
## 2025.01.01

View file

@ -79,24 +79,22 @@ def calculate_stat_data do
status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count)
users_query =
# there are few enough local users for postgres to use an index scan
# (also here an exact count is a bit more important)
user_count =
from(u in User,
where: u.is_active == true,
where: u.local == true,
where: not is_nil(u.nickname),
where: not u.invisible
)
|> Repo.aggregate(:count, :id)
remote_users_query =
from(u in User,
where: u.is_active == true,
where: u.local == false,
where: not is_nil(u.nickname),
where: not u.invisible
)
user_count = Repo.aggregate(users_query, :count, :id)
remote_user_count = Repo.aggregate(remote_users_query, :count, :id)
# but mostly numerous remote users leading to a full a full table scan
# (ecto currently doesn't allow building queries without explicit table)
%{rows: [[remote_user_count]]} =
"SELECT estimate_remote_user_count();"
|> Pleroma.Repo.query!()
%{
peers: peers,

View file

@ -0,0 +1,38 @@
# Akkoma: Magically expressive social media
# Copyright © 2024 Akkoma Authors <https://akkoma.dev/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Repo.Migrations.RemoteUserCountEstimateFunction do
use Ecto.Migration
@function_name "estimate_remote_user_count"
def up() do
# yep, this EXPLAIN (ab)use is blessed by the PostgreSQL wiki:
# https://wiki.postgresql.org/wiki/Count_estimate
"""
CREATE OR REPLACE FUNCTION #{@function_name}()
RETURNS integer
LANGUAGE plpgsql AS $$
DECLARE plan jsonb;
BEGIN
EXECUTE '
EXPLAIN (FORMAT JSON)
SELECT *
FROM public.users
WHERE local = false AND
is_active = true AND
invisible = false AND
nickname IS NOT NULL;
' INTO plan;
RETURN plan->0->'Plan'->'Plan Rows';
END;
$$;
"""
|> execute()
end
def down() do
execute("DROP FUNCTION IF EXISTS #{@function_name}()")
end
end