db: convert indexes to partial ones where appropriate

This reduces both the size the indexes take on the disk
and the overhead of maintaining it since it now only
needs to be updated for some entries.

For the email index restriction queries needed to be updated,
the last_active_at index is used only by one query which already
also restricted by "local" and for the remaining restrictions
no query adaptations are needed.
This commit is contained in:
Oneric 2025-11-11 00:00:00 +00:00
commit aac6086ca6
3 changed files with 58 additions and 0 deletions

View file

@ -43,6 +43,7 @@ defmodule Mix.Tasks.Pleroma.NotificationSettings do
defp build_query(hide_notification_contents, options) do
query =
from(u in Pleroma.User,
where: u.local,
update: [
set: [
notification_settings:

View file

@ -31,6 +31,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do
from(u in inactive_users_query,
where: fragment(~s(? ->'digest' @> 'true'), u.email_notifications),
where: u.local,
where: not is_nil(u.email),
where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"),
select: u

View file

@ -0,0 +1,56 @@
defmodule Pleroma.Repo.Migrations.RestrictEligibleUserIndexes do
use Ecto.Migration
@old_indexes [
index(:users, [:email], unique: true),
index(:users, [:is_admin]),
index(:users, [:is_moderator]),
index(:users, [:is_suggested]),
index(:users, [:last_active_at])
]
@new_indexes [
# We use this to send out emails to local users, i.e. we only care about
# _local_ users who also set an email to begin with
# (and to ensure no two local users claim the same email address;
# though if we somehow get an email for a remote user, we don't care about collisions)
index(:users, [:email], unique: true, where: "email IS NOT NULL AND local"),
# Just used to quickly retrieve all suggested useres
# (this perhaps should have been a separate table to begin with).
# This MUST use BTREE, a HASH index will not be used when querying all suggested users!
index(:users, [:id], where: "is_suggested", using: :btree, name: :users_where_suggested_index),
# Only _local_ users can be admins or moderators and in practice
# this criteria is only used to query for a "true" setting.
# According to EXPLAIN, restricting just by the "true" state even performs _slightly_ better
# and requires less to keep in mind when contructing queries
index(:users, [:is_admin], where: "is_admin"),
index(:users, [:is_moderator], where: "is_moderator"),
# The following is only set and used for _local_ users
index(:users, [:last_active_at], where: "local")
]
defp drop_all(indexes) do
for idx <- indexes do
drop_if_exists(idx)
end
end
defp create_all(indexes) do
for idx <- indexes do
create_if_not_exists(idx)
end
end
def up() do
drop_all(@old_indexes)
create_all(@new_indexes)
end
def down() do
drop_all(@new_indexes)
create_all(@old_indexes)
end
end