fix(retention): only delete objects that belong to an evicted thread
Eviction took the object ids of every activity in a thread. A Like or Announce whose context differs from its object's then deleted a post in another, possibly pinned thread. Found on a copy of a real instance: 593 posts from threads with local activity in the first 2M objects.
This commit is contained in:
parent
5a4ada881f
commit
5d1e331589
2 changed files with 35 additions and 3 deletions
|
|
@ -161,6 +161,7 @@ defmodule Pleroma.Retention do
|
|||
deadline
|
||||
|> evictable_contexts_query(Keyword.put(opts, :contexts, contexts))
|
||||
|> select([a], %{
|
||||
context: fragment("? ->> 'context'", a.data),
|
||||
activity_ids: type(fragment("array_agg(?)", a.id), {:array, FlakeId.Ecto.CompatType}),
|
||||
object_ids: fragment("array_agg(associated_object_id(?))", a.data)
|
||||
})
|
||||
|
|
@ -321,15 +322,23 @@ defmodule Pleroma.Retention do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Deletes the given threads. Each entry carries the activity ids and object
|
||||
ids collected per thread, so deletion uses primary key and ap_id indexes only.
|
||||
Deletes the given threads. Each entry carries the context, activity ids and
|
||||
object ids collected per thread, so deletion uses primary key and ap_id
|
||||
indexes only.
|
||||
|
||||
An object is only deleted if it belongs to one of the threads itself: a
|
||||
Like or Announce may carry a context other than its object's, and that
|
||||
object's own thread can be pinned.
|
||||
"""
|
||||
@spec evict([%{activity_ids: [String.t()], object_ids: [String.t() | nil]}]) :: stats()
|
||||
@spec evict([
|
||||
%{context: String.t(), activity_ids: [String.t()], object_ids: [String.t() | nil]}
|
||||
]) :: stats()
|
||||
def evict([]), do: @empty_stats
|
||||
|
||||
def evict(threads) do
|
||||
activity_ids = Enum.flat_map(threads, & &1.activity_ids)
|
||||
object_ids = threads |> Enum.flat_map(& &1.object_ids) |> Enum.reject(&is_nil/1)
|
||||
contexts = Enum.map(threads, & &1.context)
|
||||
|
||||
{:ok, stats} =
|
||||
Repo.transaction(
|
||||
|
|
@ -340,6 +349,7 @@ defmodule Pleroma.Retention do
|
|||
join: o in Object,
|
||||
on: o.id == ho.object_id,
|
||||
where: fragment("? ->> 'id'", o.data) in ^object_ids,
|
||||
where: fragment("? ->> 'context'", o.data) in ^contexts,
|
||||
distinct: true,
|
||||
select: ho.hashtag_id
|
||||
)
|
||||
|
|
@ -348,6 +358,7 @@ defmodule Pleroma.Retention do
|
|||
{objects, deleted_ap_ids} =
|
||||
Object
|
||||
|> where([o], fragment("? ->> 'id'", o.data) in ^object_ids)
|
||||
|> where([o], fragment("? ->> 'context'", o.data) in ^contexts)
|
||||
|> where(
|
||||
[o],
|
||||
fragment(
|
||||
|
|
|
|||
|
|
@ -196,6 +196,27 @@ defmodule Pleroma.RetentionTest do
|
|||
assert Object.get_by_ap_id(mention.data["object"])
|
||||
end
|
||||
|
||||
test "keeps a pinned post that an evicted thread's activity points at", %{
|
||||
old_date: old_date
|
||||
} do
|
||||
remote_user = insert(:user, local: false)
|
||||
remote_user2 = insert(:user, local: false)
|
||||
local_user = insert(:user)
|
||||
|
||||
post = old_remote_post(remote_user, old_date)
|
||||
{:ok, local_like} = CommonAPI.favorite(post.id, local_user)
|
||||
make_old(local_like, old_date)
|
||||
|
||||
# A remote like that arrived with a context of its own
|
||||
{:ok, like} = CommonAPI.favorite(post.id, remote_user2)
|
||||
other_context = "https://remote.example/contexts/#{Ecto.UUID.generate()}"
|
||||
age(like, old_date, %{local: false, data: Map.put(like.data, "context", other_context)})
|
||||
|
||||
assert %{contexts: 1, objects: 0} = Retention.run()
|
||||
assert Object.get_by_ap_id(post.data["object"])
|
||||
assert Activity.get_by_id(post.id)
|
||||
end
|
||||
|
||||
test "keeps an old remote post that was reported", %{old_date: old_date} do
|
||||
remote_user = insert(:user, local: false)
|
||||
local_user = insert(:user)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue