persist mockup variants on fly volume across deploys
All checks were successful
deploy / deploy (push) Successful in 3m46s
All checks were successful
deploy / deploy (push) Successful in 3m46s
Source mockup WebPs are copied from the release to /data/mockups/ on startup, and variants are generated there. This eliminates the 182-job storm on every deploy that was saturating the CPU and causing SQLite locking. After the first successful run, subsequent deploys find all variants intact. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8159a312ae
commit
5e70c07b60
@ -19,8 +19,9 @@ config :logger, level: :info
|
||||
# Structured JSON logs for production (machine-parseable by fly logs, journalctl, Loki, etc.)
|
||||
config :logger, :default_handler, formatter: {LoggerJSON.Formatters.Basic, []}
|
||||
|
||||
# Persistent image cache on the Fly volume (survives deploys)
|
||||
# Persistent image cache and mockup variants on the Fly volume (survives deploys)
|
||||
config :berrypod, :image_cache_dir, "/data/image_cache"
|
||||
config :berrypod, :mockup_dir, "/data/mockups"
|
||||
|
||||
# Runtime production configuration, including reading
|
||||
# of environment variables, is done on config/runtime.exs.
|
||||
|
||||
@ -19,7 +19,13 @@ defmodule Berrypod.Images.VariantCache do
|
||||
alias Berrypod.Sync.ImageDownloadWorker
|
||||
import Ecto.Query
|
||||
|
||||
defp mockup_dir, do: Application.app_dir(:berrypod, "priv/static/mockups")
|
||||
# Source mockups bundled in the release
|
||||
defp release_mockup_dir, do: Application.app_dir(:berrypod, "priv/static/mockups")
|
||||
|
||||
# Where mockup variants live — persistent volume in prod, release dir in dev
|
||||
defp mockup_dir do
|
||||
Application.get_env(:berrypod, :mockup_dir) || release_mockup_dir()
|
||||
end
|
||||
|
||||
def start_link(opts) do
|
||||
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
@ -84,9 +90,25 @@ defmodule Berrypod.Images.VariantCache do
|
||||
end
|
||||
|
||||
defp ensure_mockup_variants do
|
||||
if File.dir?(mockup_dir()) do
|
||||
output = mockup_dir()
|
||||
release = release_mockup_dir()
|
||||
|
||||
# When using a persistent volume, copy source WebPs from the release
|
||||
# so variants can be generated there (and survive deploys).
|
||||
if output != release and File.dir?(release) do
|
||||
File.mkdir_p!(output)
|
||||
|
||||
Path.wildcard(Path.join(release, "*.webp"))
|
||||
|> Enum.reject(&is_variant?/1)
|
||||
|> Enum.each(fn src ->
|
||||
dest = Path.join(output, Path.basename(src))
|
||||
unless File.exists?(dest), do: File.cp!(src, dest)
|
||||
end)
|
||||
end
|
||||
|
||||
if File.dir?(output) do
|
||||
sources =
|
||||
Path.wildcard(Path.join(mockup_dir(), "*.webp"))
|
||||
Path.wildcard(Path.join(output, "*.webp"))
|
||||
|> Enum.reject(&is_variant?/1)
|
||||
|
||||
missing = Enum.reject(sources, &mockup_variants_exist?/1)
|
||||
|
||||
@ -16,7 +16,7 @@ defmodule BerrypodWeb.Endpoint do
|
||||
websocket: [connect_info: [session: @session_options]],
|
||||
longpoll: [connect_info: [session: @session_options]]
|
||||
|
||||
# In prod, image variants live on the persistent volume (/data/image_cache)
|
||||
# In prod, image variants and mockups live on the persistent volume
|
||||
# rather than inside the ephemeral release directory.
|
||||
if image_cache_dir = Application.compile_env(:berrypod, :image_cache_dir) do
|
||||
plug Plug.Static,
|
||||
@ -25,6 +25,13 @@ defmodule BerrypodWeb.Endpoint do
|
||||
cache_control_for_etags: "public, max-age=31536000, immutable"
|
||||
end
|
||||
|
||||
if mockup_dir = Application.compile_env(:berrypod, :mockup_dir) do
|
||||
plug Plug.Static,
|
||||
at: "/mockups",
|
||||
from: mockup_dir,
|
||||
cache_control_for_etags: "public, max-age=31536000, immutable"
|
||||
end
|
||||
|
||||
# Serve at "/" the static files from "priv/static" directory.
|
||||
# gzip only in prod — avoids stale .gz files from mix assets.deploy
|
||||
# shadowing freshly-built dev assets.
|
||||
|
||||
@ -35,6 +35,7 @@ defmodule BerrypodWeb.Plugs.BrokenUrlTracker do
|
||||
String.starts_with?(path, "/assets/") or
|
||||
String.starts_with?(path, "/images/") or
|
||||
String.starts_with?(path, "/image_cache/") or
|
||||
String.starts_with?(path, "/mockups/") or
|
||||
String.starts_with?(path, "/favicon")
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user