Skip to content

Commit

Permalink
Add caching at the HTTP layer
Browse files Browse the repository at this point in the history
Pretty rudimentary at the moment, with a short lifetime.
Doesn't check for modification times.
  • Loading branch information
z0w0 committed Jul 30, 2020
1 parent 0c3685a commit e30edfe
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 35 deletions.
5 changes: 5 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ config :imagism,
s3_bucket: System.get_env("IMAGISM_S3_BUCKET"),
s3_region: System.get_env("IMAGISM_S3_REGION")

config :imagism, Imagism.Cache,
gc_interval: 1800,
allocated_memory: 1_000_000_000,
backend: :shards

config :logger, :console,
metadata: [:resize, :fit, :w, :h, :crop, :brighten, :blur, :rotate, :flip]
3 changes: 2 additions & 1 deletion lib/imagism.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ defmodule Imagism do
port = Application.fetch_env!(:imagism, :port)

children = [
{Plug.Cowboy, scheme: :http, plug: {Imagism.Plug, adapter}, options: [port: port]}
{Plug.Cowboy, scheme: :http, plug: {Imagism.Plug, adapter}, options: [port: port]},
{Imagism.Cache, []}
]

Logger.info("Listening on port #{port}")
Expand Down
22 changes: 17 additions & 5 deletions lib/imagism/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,28 @@ defmodule Imagism.Adapter do
def open(adapter, path) do
case adapter.type do
:file ->
Imagism.Image.open(Path.join(adapter.file_path, path))
case File.read(Path.join(adapter.file_path, path)) do
{:ok, file_data} ->
Imagism.Image.decode(file_data)

err ->
err
end

:s3 ->
res =
ExAws.S3.get_object(adapter.s3_bucket, path) |> ExAws.request(region: adapter.s3_region)
ExAws.S3.get_object(adapter.s3_bucket, path)
|> ExAws.request(region: adapter.s3_region)

case res do
{:ok, %{body: body}} -> Imagism.Image.decode(body)
{:error, {:http_error, 404, _}} -> {:error, :enoent}
{:error, err} -> {:error, err}
{:ok, %{body: s3_data}} ->
Imagism.Image.decode(s3_data)

{:error, {:http_error, 404, _}} ->
{:error, :enoent}

err ->
err
end
end
end
Expand Down
5 changes: 5 additions & 0 deletions lib/imagism/cache.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule Imagism.Cache do
use Nebulex.Cache,
otp_app: :imagism,
adapter: Nebulex.Adapters.Local
end
69 changes: 41 additions & 28 deletions lib/imagism/plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ defmodule Imagism.Plug do
Processes `image` using the provided `params` and then
responds to `conn`.
"""
@spec process_image(Plug.Conn.t(), Imagism.Image.t(), Imagism.Params.t()) :: Plug.Conn.t()
def process_image(conn, image, params) do
@spec process_image(Imagism.Image.t(), Imagism.Params.t()) ::
{:ok, {binary(), bitstring()}} | {:error, any}
def process_image(image, params) do
Logger.metadata(Map.to_list(Map.from_struct(params)))

content_type = Imagism.Image.content_type(image)
Expand Down Expand Up @@ -133,15 +134,8 @@ defmodule Imagism.Plug do
)

case processed_image do
{:ok, binary} ->
conn
|> put_resp_content_type(content_type)
|> send_resp(200, binary)

{:error, error} ->
conn
|> put_resp_content_type("text/plain")
|> send_resp(500, "Process error: #{error}")
{:ok, data} -> {:ok, {content_type, data}}
err -> err
end
end

Expand All @@ -152,23 +146,42 @@ defmodule Imagism.Plug do
"""
@spec open_image(Plug.Conn.t(), Imagism.Adapter.t()) :: Plug.Conn.t()
def open_image(conn, adapter) do
case Imagism.Adapter.open(adapter, conn.request_path) do
{:ok, image} ->
Imagism.Plug.process_image(
conn,
image,
Imagism.Params.new(Plug.Conn.fetch_query_params(conn).query_params)
)

{:error, :enoent} ->
conn
|> put_resp_content_type("text/plain")
|> send_resp(404, "Not found")

{:error, error} ->
conn
|> put_resp_content_type("text/plain")
|> send_resp(500, "Adapter error: #{error}")
path = conn.request_path
params = Imagism.Params.new(Plug.Conn.fetch_query_params(conn).query_params)
key = {adapter.type, path, params}
cached = Imagism.Cache.get(key)

if cached != nil do
conn
|> put_resp_content_type(elem(cached, 0))
|> send_resp(200, elem(cached, 1))
else
case Imagism.Adapter.open(adapter, path) do
{:ok, image} ->
case Imagism.Plug.process_image(image, params) do
{:ok, {content_type, data}} ->
Imagism.Cache.put(key, {content_type, data})

conn
|> put_resp_content_type(content_type)
|> send_resp(200, data)

{:error, error} ->
conn
|> put_resp_content_type("text/plain")
|> send_resp(500, "Process error: #{error}")
end

{:error, :enoent} ->
conn
|> put_resp_content_type("text/plain")
|> send_resp(404, "Not found")

{:error, error} ->
conn
|> put_resp_content_type("text/plain")
|> send_resp(500, "Adapter error: #{error}")
end
end
end

Expand Down
4 changes: 3 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ defmodule Imagism.MixProject do
{:ex_aws, "~> 2.0"},
{:ex_aws_s3, "~> 2.0"},
{:jason, "~> 1.2"},
{:httpoison, "~> 1.6"}
{:httpoison, "~> 1.6"},
{:nebulex, "~> 2.0.0-rc.0"},
{:shards, "~> 0.6"}
]
end

Expand Down
7 changes: 7 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
%{
"cachex": {:hex, :cachex, "3.2.0", "a596476c781b0646e6cb5cd9751af2e2974c3e0d5498a8cab71807618b74fe2f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "aef93694067a43697ae0531727e097754a9e992a1e7946296f5969d6dd9ac986"},
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
"dotenv": {:hex, :dotenv, "3.0.0", "52a28976955070d8312a81d59105b57ecf5d6a755c728b49c70a7e2120e6cb40", [:mix], [], "hexpm", "f8a7d800b6b419a8d8a8bc5b5cd820a181c2b713aab7621794febe934f7bd84e"},
"envy": {:hex, :envy, "1.1.1", "0bc9bd654dec24fcdf203f7c5aa1b8f30620f12cfb28c589d5e9c38fe1b07475", [:mix], [], "hexpm", "7061eb1a47415fd757145d8dec10dc0b1e48344960265cb108f194c4252c3a89"},
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"},
"ex_aws": {:hex, :ex_aws, "2.1.3", "26b6f036f0127548706aade4a509978fc7c26bd5334b004fba9bfe2687a525df", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "0bdbe2aed9f326922fc5a6a80417e32f0c895f4b3b2b0b9676ebf23dd16c5da4"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.0.2", "c0258bbdfea55de4f98f0b2f0ca61fe402cc696f573815134beb1866e778f47b", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "0569f5b211b1a3b12b705fe2a9d0e237eb1360b9d76298028df2346cad13097a"},
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
"httpoison": {:hex, :httpoison, "1.7.0", "abba7d086233c2d8574726227b6c2c4f6e53c4deae7fe5f6de531162ce9929a0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "975cc87c845a103d3d1ea1ccfd68a2700c211a434d8428b10c323dc95dc5b980"},
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"nebulex": {:hex, :nebulex, "2.0.0-rc.0", "fd7c3c98373bcb64392bbbc6eb7af97effc34ae8ae9597becc7cd849836895bf", [:mix], [{:decorator, "~> 1.3", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 0.6", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "3ffbe0c4cddd15fa9ab201cc1aa69ad21db862313097025dee5c77b73ad91003"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"plug": {:hex, :plug, "1.10.3", "c9cebe917637d8db0e759039cc106adca069874e1a9034fd6e3fdd427fd3c283", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "01f9037a2a1de1d633b5a881101e6a444bcabb1d386ca1e00bb273a1f1d9d939"},
"plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"},
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
"rustler": {:hex, :rustler, "0.21.1", "5299980be32da997c54382e945bacaa015ed97a60745e1e639beaf6a7b278c65", [:mix], [{:toml, "~> 0.5.2", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "6ee1651e10645b2b2f3bb70502bf180341aa058709177e9bc28c105934094bc6"},
"shards": {:hex, :shards, "0.6.2", "e05d05537883220c3b8a8f9d40d5c8ba7ff6064c63ebb6b23046972f6863b2d1", [:make, :rebar3], [], "hexpm", "58afa3712f1f1256a2a15e39fa95b7cd758087aaa7a25beaf786daabd87890f0"},
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
"toml": {:hex, :toml, "0.5.2", "e471388a8726d1ce51a6b32f864b8228a1eb8edc907a0edf2bb50eab9321b526", [:mix], [], "hexpm", "f1e3dabef71fb510d015fad18c0e05e7c57281001141504c6b69d94e99750a07"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
}

0 comments on commit e30edfe

Please sign in to comment.