Clear all ics/feed caches when modifying events/posts/actors

Closes #1059

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2022-03-24 12:51:23 +01:00
parent 117052fb91
commit 019d694d2a
6 changed files with 132 additions and 12 deletions

View File

@@ -0,0 +1,23 @@
defmodule Mobilizon.Service.Export.Cachable do
@moduledoc """
Behavior that export modules that use caching should implement
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Events.Event
alias Mobilizon.Posts.Post
alias Mobilizon.Service.Export.{Feed, ICalendar}
@callback create_cache(String.t()) :: any()
@callback clear_caches(Event.t() | Post.t() | Actor.t()) :: any()
@spec clear_all_caches(%{
:__struct__ => Mobilizon.Actors.Actor | Mobilizon.Events.Event | Mobilizon.Posts.Post,
optional(any) => any
}) :: {:error, boolean} | {:ok, boolean}
def clear_all_caches(entity) do
Feed.clear_caches(entity)
ICalendar.clear_caches(entity)
end
end

View File

@@ -11,7 +11,7 @@ defmodule Mobilizon.Service.Export.Feed do
alias Mobilizon.Config
alias Mobilizon.Events.Event
alias Mobilizon.Posts.Post
alias Mobilizon.Service.Export.Common
alias Mobilizon.Service.Export.{Cachable, Common}
alias Mobilizon.Users.User
alias Mobilizon.Web.Endpoint
@@ -19,11 +19,14 @@ defmodule Mobilizon.Service.Export.Feed do
require Logger
@behaviour Cachable
@item_limit 500
@spec version :: String.t()
defp version, do: Config.instance_version()
@impl Cachable
@spec create_cache(String.t()) ::
{:commit, String.t()}
| {:ignore, :actor_not_found | :actor_not_public | :bad_token | :token_not_found}
@@ -37,6 +40,7 @@ defmodule Mobilizon.Service.Export.Feed do
end
end
@impl Cachable
def create_cache("token_" <> token) do
case fetch_events_from_token(token) do
{:ok, res} ->
@@ -47,6 +51,7 @@ defmodule Mobilizon.Service.Export.Feed do
end
end
@impl Cachable
def create_cache("instance") do
{:ok, res} = fetch_instance_feed()
{:commit, res}
@@ -227,4 +232,44 @@ defmodule Mobilizon.Service.Export.Feed do
|> Feed.build()
|> Atomex.generate_document()
end
@impl Cachable
def clear_caches(%Event{attributed_to: %Actor{} = actor} = event) do
clear_actor_feed(actor)
clear_caches(%{event | attributed_to: nil})
end
@impl Cachable
def clear_caches(%Event{}) do
# TODO: It would be nice to clear feed token cache based on participations as well,
# but that's harder, as it would require loading all participations
clear_instance()
end
@impl Cachable
def clear_caches(%Post{attributed_to: %Actor{} = actor} = post) do
clear_actor_feed(actor)
clear_caches(%{post | attributed_to: nil})
end
@impl Cachable
def clear_caches(%Post{}) do
clear_instance()
end
@impl Cachable
def clear_caches(%Actor{} = actor) do
clear_actor_feed(actor)
clear_instance()
end
defp clear_instance do
Cachex.del(:feed, "instance")
end
defp clear_actor_feed(%Actor{preferred_username: preferred_username} = actor) do
if Actor.is_public_visibility?(actor) do
Cachex.del(:feed, "actor_#{preferred_username}")
end
end
end

View File

@@ -7,14 +7,17 @@ defmodule Mobilizon.Service.Export.ICalendar do
alias Mobilizon.Addresses.Address
alias Mobilizon.{Config, Events}
alias Mobilizon.Events.{Event, EventOptions}
alias Mobilizon.Service.Export.Common
alias Mobilizon.Service.Export.{Cachable, Common}
alias Mobilizon.Service.Formatter.HTML
@behaviour Cachable
@item_limit 500
@doc """
Create cache for an actor, an event or an user token
"""
@impl Cachable
@spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, atom()}
def create_cache("actor_" <> name) do
case export_public_actor(name) do
@@ -26,6 +29,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
end
end
@impl Cachable
def create_cache("event_" <> uuid) do
with %Event{} = event <- Events.get_public_event_by_uuid_with_preload(uuid),
{:ok, res} <- export_public_event(event) do
@@ -39,6 +43,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
end
end
@impl Cachable
def create_cache("token_" <> token) do
case fetch_events_from_token(token) do
{:ok, res} ->
@@ -49,6 +54,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
end
end
@impl Cachable
def create_cache("instance") do
{:ok, res} = fetch_instance_feed()
{:commit, res}
@@ -172,4 +178,45 @@ defmodule Mobilizon.Service.Export.ICalendar do
defp organizer(%Event{organizer_actor: %Actor{} = profile}) do
Actor.display_name(profile)
end
@impl Cachable
@spec clear_caches(%{
:__struct__ => Mobilizon.Actors.Actor | Mobilizon.Events.Event | Mobilizon.Posts.Post,
optional(any) => any
}) :: {:error, boolean} | {:ok, boolean}
def clear_caches(%Event{attributed_to: %Actor{} = actor} = event) do
clear_actor_feed(actor)
clear_caches(%{event | attributed_to: nil})
end
@impl Cachable
def clear_caches(%Event{uuid: uuid}) do
# TODO: It would be nice to clear feed token cache based on participations as well,
# but that's harder, as it would require loading all participations
Cachex.del(:ics, "event_#{uuid}")
clear_instance()
end
# Not applicable for posts
@impl Cachable
def clear_caches(%Mobilizon.Posts.Post{}) do
{:ok, true}
end
@impl Cachable
def clear_caches(%Actor{} = actor) do
clear_actor_feed(actor)
clear_instance()
end
defp clear_instance do
Cachex.del(:ics, "instance")
end
defp clear_actor_feed(%Actor{preferred_username: preferred_username} = actor) do
if Actor.is_public_visibility?(actor) do
Cachex.del(:ics, "actor_#{preferred_username}")
end
end
end