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:
parent
117052fb91
commit
019d694d2a
|
@ -28,6 +28,7 @@ defmodule Mobilizon.Events do
|
||||||
Track
|
Track
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alias Mobilizon.Service.Export.Cachable
|
||||||
alias Mobilizon.Service.Workers.BuildSearch
|
alias Mobilizon.Service.Workers.BuildSearch
|
||||||
alias Mobilizon.Service.Workers.EventDelayedNotificationWorker
|
alias Mobilizon.Service.Workers.EventDelayedNotificationWorker
|
||||||
alias Mobilizon.Share
|
alias Mobilizon.Share
|
||||||
|
@ -301,7 +302,7 @@ defmodule Mobilizon.Events do
|
||||||
end)
|
end)
|
||||||
|> Repo.transaction(),
|
|> Repo.transaction(),
|
||||||
%Event{} = new_event <- Repo.preload(new_event, @event_preloads, force: true) do
|
%Event{} = new_event <- Repo.preload(new_event, @event_preloads, force: true) do
|
||||||
Cachex.del(:ics, "event_#{new_event.uuid}")
|
Cachable.clear_all_caches(new_event)
|
||||||
|
|
||||||
unless new_event.draft do
|
unless new_event.draft do
|
||||||
%{
|
%{
|
||||||
|
@ -355,14 +356,10 @@ defmodule Mobilizon.Events do
|
||||||
Deletes an event.
|
Deletes an event.
|
||||||
"""
|
"""
|
||||||
@spec delete_event(Event.t()) :: {:ok, Event.t()} | {:error, Changeset.t()}
|
@spec delete_event(Event.t()) :: {:ok, Event.t()} | {:error, Changeset.t()}
|
||||||
def delete_event(%Event{} = event), do: Repo.delete(event)
|
def delete_event(%Event{} = event) do
|
||||||
|
Cachable.clear_all_caches(event)
|
||||||
@doc """
|
Repo.delete(event)
|
||||||
Deletes an event.
|
end
|
||||||
Raises an exception if it fails.
|
|
||||||
"""
|
|
||||||
@spec delete_event!(Event.t()) :: Event.t()
|
|
||||||
def delete_event!(%Event{} = event), do: Repo.delete!(event)
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of events.
|
Returns the list of events.
|
||||||
|
|
|
@ -5,6 +5,7 @@ defmodule Mobilizon.Posts do
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Events.Tag
|
alias Mobilizon.Events.Tag
|
||||||
alias Mobilizon.Posts.Post
|
alias Mobilizon.Posts.Post
|
||||||
|
alias Mobilizon.Service.Export.Cachable
|
||||||
alias Mobilizon.Storage.{Page, Repo}
|
alias Mobilizon.Storage.{Page, Repo}
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -107,6 +108,8 @@ defmodule Mobilizon.Posts do
|
||||||
"""
|
"""
|
||||||
@spec update_post(Post.t(), map) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
|
@spec update_post(Post.t(), map) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
|
||||||
def update_post(%Post{} = post, attrs) do
|
def update_post(%Post{} = post, attrs) do
|
||||||
|
Cachable.clear_all_caches(post)
|
||||||
|
|
||||||
post
|
post
|
||||||
|> Repo.preload([:tags, :media])
|
|> Repo.preload([:tags, :media])
|
||||||
|> Post.changeset(attrs)
|
|> Post.changeset(attrs)
|
||||||
|
@ -117,7 +120,10 @@ defmodule Mobilizon.Posts do
|
||||||
Deletes a post
|
Deletes a post
|
||||||
"""
|
"""
|
||||||
@spec delete_post(Post.t()) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
|
@spec delete_post(Post.t()) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
|
||||||
def delete_post(%Post{} = post), do: Repo.delete(post)
|
def delete_post(%Post{} = post) do
|
||||||
|
Cachable.clear_all_caches(post)
|
||||||
|
Repo.delete(post)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of tags for the post.
|
Returns the list of tags for the post.
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Mobilizon.Service.ActorSuspension do
|
||||||
alias Mobilizon.Medias.File
|
alias Mobilizon.Medias.File
|
||||||
alias Mobilizon.Posts.Post
|
alias Mobilizon.Posts.Post
|
||||||
alias Mobilizon.Resources.Resource
|
alias Mobilizon.Resources.Resource
|
||||||
|
alias Mobilizon.Service.Export.Cachable
|
||||||
alias Mobilizon.Storage.Repo
|
alias Mobilizon.Storage.Repo
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
alias Mobilizon.Web.Email.Actor, as: ActorEmail
|
alias Mobilizon.Web.Email.Actor, as: ActorEmail
|
||||||
|
@ -66,6 +67,7 @@ defmodule Mobilizon.Service.ActorSuspension do
|
||||||
case Repo.transaction(multi) do
|
case Repo.transaction(multi) do
|
||||||
{:ok, %{actor: %Actor{} = actor}} ->
|
{:ok, %{actor: %Actor{} = actor}} ->
|
||||||
{:ok, true} = Cachex.del(:activity_pub, "actor_#{actor.preferred_username}")
|
{:ok, true} = Cachex.del(:activity_pub, "actor_#{actor.preferred_username}")
|
||||||
|
Cachable.clear_all_caches(actor)
|
||||||
Logger.info("Deleted actor #{actor.url}")
|
Logger.info("Deleted actor #{actor.url}")
|
||||||
{:ok, actor}
|
{:ok, actor}
|
||||||
|
|
||||||
|
|
23
lib/service/export/cachable.ex
Normal file
23
lib/service/export/cachable.ex
Normal 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
|
|
@ -11,7 +11,7 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
alias Mobilizon.Config
|
alias Mobilizon.Config
|
||||||
alias Mobilizon.Events.Event
|
alias Mobilizon.Events.Event
|
||||||
alias Mobilizon.Posts.Post
|
alias Mobilizon.Posts.Post
|
||||||
alias Mobilizon.Service.Export.Common
|
alias Mobilizon.Service.Export.{Cachable, Common}
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
|
||||||
alias Mobilizon.Web.Endpoint
|
alias Mobilizon.Web.Endpoint
|
||||||
|
@ -19,11 +19,14 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@behaviour Cachable
|
||||||
|
|
||||||
@item_limit 500
|
@item_limit 500
|
||||||
|
|
||||||
@spec version :: String.t()
|
@spec version :: String.t()
|
||||||
defp version, do: Config.instance_version()
|
defp version, do: Config.instance_version()
|
||||||
|
|
||||||
|
@impl Cachable
|
||||||
@spec create_cache(String.t()) ::
|
@spec create_cache(String.t()) ::
|
||||||
{:commit, String.t()}
|
{:commit, String.t()}
|
||||||
| {:ignore, :actor_not_found | :actor_not_public | :bad_token | :token_not_found}
|
| {:ignore, :actor_not_found | :actor_not_public | :bad_token | :token_not_found}
|
||||||
|
@ -37,6 +40,7 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl Cachable
|
||||||
def create_cache("token_" <> token) do
|
def create_cache("token_" <> token) do
|
||||||
case fetch_events_from_token(token) do
|
case fetch_events_from_token(token) do
|
||||||
{:ok, res} ->
|
{:ok, res} ->
|
||||||
|
@ -47,6 +51,7 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl Cachable
|
||||||
def create_cache("instance") do
|
def create_cache("instance") do
|
||||||
{:ok, res} = fetch_instance_feed()
|
{:ok, res} = fetch_instance_feed()
|
||||||
{:commit, res}
|
{:commit, res}
|
||||||
|
@ -227,4 +232,44 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
|> Feed.build()
|
|> Feed.build()
|
||||||
|> Atomex.generate_document()
|
|> Atomex.generate_document()
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -7,14 +7,17 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
alias Mobilizon.Addresses.Address
|
alias Mobilizon.Addresses.Address
|
||||||
alias Mobilizon.{Config, Events}
|
alias Mobilizon.{Config, Events}
|
||||||
alias Mobilizon.Events.{Event, EventOptions}
|
alias Mobilizon.Events.{Event, EventOptions}
|
||||||
alias Mobilizon.Service.Export.Common
|
alias Mobilizon.Service.Export.{Cachable, Common}
|
||||||
alias Mobilizon.Service.Formatter.HTML
|
alias Mobilizon.Service.Formatter.HTML
|
||||||
|
|
||||||
|
@behaviour Cachable
|
||||||
|
|
||||||
@item_limit 500
|
@item_limit 500
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Create cache for an actor, an event or an user token
|
Create cache for an actor, an event or an user token
|
||||||
"""
|
"""
|
||||||
|
@impl Cachable
|
||||||
@spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, atom()}
|
@spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, atom()}
|
||||||
def create_cache("actor_" <> name) do
|
def create_cache("actor_" <> name) do
|
||||||
case export_public_actor(name) do
|
case export_public_actor(name) do
|
||||||
|
@ -26,6 +29,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl Cachable
|
||||||
def create_cache("event_" <> uuid) do
|
def create_cache("event_" <> uuid) do
|
||||||
with %Event{} = event <- Events.get_public_event_by_uuid_with_preload(uuid),
|
with %Event{} = event <- Events.get_public_event_by_uuid_with_preload(uuid),
|
||||||
{:ok, res} <- export_public_event(event) do
|
{:ok, res} <- export_public_event(event) do
|
||||||
|
@ -39,6 +43,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl Cachable
|
||||||
def create_cache("token_" <> token) do
|
def create_cache("token_" <> token) do
|
||||||
case fetch_events_from_token(token) do
|
case fetch_events_from_token(token) do
|
||||||
{:ok, res} ->
|
{:ok, res} ->
|
||||||
|
@ -49,6 +54,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl Cachable
|
||||||
def create_cache("instance") do
|
def create_cache("instance") do
|
||||||
{:ok, res} = fetch_instance_feed()
|
{:ok, res} = fetch_instance_feed()
|
||||||
{:commit, res}
|
{:commit, res}
|
||||||
|
@ -172,4 +178,45 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
defp organizer(%Event{organizer_actor: %Actor{} = profile}) do
|
defp organizer(%Event{organizer_actor: %Actor{} = profile}) do
|
||||||
Actor.display_name(profile)
|
Actor.display_name(profile)
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue