Skip to content
This repository has been archived by the owner on Mar 10, 2021. It is now read-only.

Cache store #132

Closed
wants to merge 9 commits into from
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<li>Page meta title and description editing</li>
<li>Easily bring your own authentication system in one tiny function</li>
<li>Create new dynamic pages, delete dynamic pages</li>
<li>Content caching to avoid too many trips to the database</li>
</ul>
</td>
</tr>
Expand Down Expand Up @@ -108,6 +109,34 @@ as generate migrations and an authorization module in your `lib/thesis_auth.ex`.
```
$ mix ecto.migrate
```

##### Page content caching

Assuming you are using the provided Thesis.EctoStore to store, every page load will result in at
least 2 database queries. In practice, this is probably not a bottleneck for your application but
Thesis does support caching to reduce DB load. You can write your own cache (it needs to implement
the `Thesis.Store` behavior), or use the provided `Thesis.CacheStore`.

To enable content caching, add this in your config/config.exs:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe flesh out the paragraph here, give a little more background regarding why this is desirable.


```elixir
config :thesis, cache: Thesis.CacheStore
config :thesis, Thesis.CacheStore, cache_name: :thesis_cache # This is the default
```

If you are using `Thesis.CacheStore`, your application will also need to start ConCache, typically
from a supervisor like so:

```elixir
import Supervisor.Spec

Supervisor.start_link([
...
supervisor(ConCache, [[], [name: :thesis_cache]]),
...
])
```

<br/>

---
Expand Down
1 change: 1 addition & 0 deletions examples/example-phx-1_2/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ config :logger, :console,
# Configure thesis content editor
config :thesis,
store: Thesis.EctoStore,
cache: Thesis.CacheStore,
authorization: Example.ThesisAuth,
uploader: Thesis.RepoUploader

Expand Down
1 change: 1 addition & 0 deletions examples/example-phx-1_2/lib/example.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Example do
supervisor(Example.Endpoint, []),
# Start your own worker by calling: Example.Worker.start_link(arg1, arg2, arg3)
# worker(Example.Worker, [arg1, arg2, arg3]),
supervisor(ConCache, [[], [name: :thesis_cache]])
]

# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
Expand Down
2 changes: 2 additions & 0 deletions examples/example-phx-1_2/mix.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
%{"certifi": {:hex, :certifi, "1.2.1", "c3904f192bd5284e5b13f20db3ceac9626e14eeacfbb492e19583cf0e37b22be", [:rebar3], [], "hexpm"},
"con_cache": {:hex, :con_cache, "0.12.1", "7553dcd51ee86fd52bd9ea9aa4b33e71bebf0b5fc5ab60e63d2e0bcaa260f937", [], [{:exactor, "~> 2.2.0", [hex: :exactor, repo: "hexpm", optional: false]}], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.1", "ccc6fd304f9bb785f2c3cfd0ee8da6bad6544ab12ca5f7162b20a743d938417c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [], [], "hexpm"},
"file_system": {:hex, :file_system, "0.2.1", "c4bec8f187d2aabace4beb890f0d4e468f65ca051593db768e533a274d0df587", [], [], "hexpm"},
"gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.8.6", "21a725db3569b3fb11a6af17d5c5f654052ce9624219f1317e8639183de4a423", [:rebar3], [{:certifi, "1.2.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.0.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
Expand Down
1 change: 1 addition & 0 deletions examples/example-phx-1_3/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import_config "#{Mix.env}.exs"
# Configure thesis content editor
config :thesis,
store: Thesis.EctoStore,
cache: Thesis.CacheStore,
authorization: ExamplePhx.ThesisAuth,
uploader: Thesis.RepoUploader
# uploader: <MyApp>.<CustomUploaderModule>
Expand Down
1 change: 1 addition & 0 deletions examples/example-phx-1_3/lib/example_phx/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule ExamplePhx.Application do
supervisor(ExamplePhxWeb.Endpoint, []),
# Start your own worker by calling: ExamplePhx.Worker.start_link(arg1, arg2, arg3)
# worker(ExamplePhx.Worker, [arg1, arg2, arg3]),
supervisor(ConCache, [[], [name: :thesis_cache]])
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
2 changes: 2 additions & 0 deletions examples/example-phx-1_3/mix.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
%{"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [:rebar3], [], "hexpm"},
"con_cache": {:hex, :con_cache, "0.12.1", "7553dcd51ee86fd52bd9ea9aa4b33e71bebf0b5fc5ab60e63d2e0bcaa260f937", [], [{:exactor, "~> 2.2.0", [hex: :exactor, repo: "hexpm", optional: false]}], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.4", "defde3c8eca385bd86466d2e1491d19e77f9b79ad996dc8e89e4e107f3942f40", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [], [], "hexpm"},
"file_system": {:hex, :file_system, "0.2.1", "c4bec8f187d2aabace4beb890f0d4e468f65ca051593db768e533a274d0df587", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.9.0", "51c506afc0a365868469dcfc79a9d0b94d896ec741cfd5bd338f49a5ec515bfe", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
Expand Down
8 changes: 4 additions & 4 deletions lib/thesis/api_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ defmodule Thesis.ApiController do
def assets(conn, _params), do: conn

def update(conn, %{"contents" => contents, "page" => page}) do
{:ok, _page} = store().update(page, contents)
{:ok, _page} = cache().update(page, contents)
json conn, %{}
end

def delete(conn, %{"path" => path}) do
{:ok, _page} = store().delete(%{"slug" => path})
{:ok, _page} = cache().delete(%{"slug" => path})
json conn, %{}
end

def backups_for_page(conn, %{"page_slug" => page_slug}) do
backups =
page_slug
|> store().backups()
|> cache().backups()
|> Enum.map(&Backup.with_pretty_datetime/1)
|> Enum.map(fn b ->
%{
Expand All @@ -36,7 +36,7 @@ defmodule Thesis.ApiController do
end

def restore(conn, %{"backup_id" => backup_id}) do
backup = store().restore(String.to_integer(backup_id))
backup = cache().restore(String.to_integer(backup_id))
json conn, %{revision: backup.page_json}
end

Expand Down
4 changes: 4 additions & 0 deletions lib/thesis/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ defmodule Thesis.Config do
Application.get_env(:thesis, :store)
end

def cache do
Application.get_env(:thesis, :cache, store())
end

def dynamic_pages do
Application.get_env(:thesis, :dynamic_pages)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/thesis/controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ defmodule Thesis.Controller.Plug do

def call(conn, _opts) do
path = Thesis.Utilities.normalize_path(conn.request_path)
current_page = store().page(path)
page_contents = store().page_contents(current_page)
current_page = cache().page(path)
page_contents = cache().page_contents(current_page)

conn
|> assign(:thesis_dynamic_page, false) # Overridden in render_dynamic/2
Expand Down
73 changes: 73 additions & 0 deletions lib/thesis/stores/cache_store.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule Thesis.CacheStore do
@moduledoc """
Thesis.CacheStore is a proxy store that acts in conjunction with a proper
store like Thesis.EctoStore to provide caching and faster page load times.
It can be enabled by setting `Thesis.Config.enable_cache` is set to `true`.
"""

@behaviour Thesis.Store

import Thesis.Config

@default_config [cache_name: :thesis_cache]
@config Keyword.merge(Application.get_env(:thesis, __MODULE__, []), @default_config)

def page(slug) do
ConCache.get_or_store(@config[:cache_name], slug, fn() ->
store().page(slug)
end)
end

def page_contents(slug) when is_binary(slug) do
page_contents(page(slug))
end

def page_contents(nil) do
cache_get(:global, fn() ->
store().page_contents(nil)
end)
end

def page_contents(page = %{id: page_id}) do
cache_get(page_id, fn() ->
store().page_contents(page)
end)
end

def update(page_params = %{"slug" => slug}, contents_params) do
cache_delete(slug)
store().page_contents(slug) |> Enum.each(fn(%{page_id: page_id}) ->
cache_delete(page_id)
end)
store().update(page_params, contents_params)
end

def delete(params = %{"slug" => slug}) do
ConCache.delete(@config[:cache_name], slug)
store().delete(params)
end

def restore(id) do
store().restore(id)
end

def backups(page_slug_or_id) do
store().backups(page_slug_or_id)
end

def clear_cache do
@config[:cache_name]
|> ConCache.ets
|> :ets.tab2list
|> Keyword.keys
|> Enum.each(&cache_delete/1)
end

defp cache_get(key, fun) do
ConCache.get_or_store(@config[:cache_name], key, fun)
end

defp cache_delete(key) do
ConCache.delete(@config[:cache_name], key)
end
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defmodule Thesis.Mixfile do
end

def application do
[extra_applications: [:logger]]
[extra_applications: [:logger, :con_cache]]
end

defp deps do
Expand All @@ -28,6 +28,7 @@ defmodule Thesis.Mixfile do
{:plug, ">= 1.0.0"},
{:poison, ">= 1.0.0"},
{:httpoison, ">= 0.11.0"},
{:con_cache, "~> 0.12.1"},
{:html_sanitize_ex, ">= 1.3.0"},
{:lz_string, "~> 0.0.7"},
{:ex_doc, ">= 0.12.0", only: [:dev]},
Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [:rebar3], [], "hexpm"},
"con_cache": {:hex, :con_cache, "0.12.1", "7553dcd51ee86fd52bd9ea9aa4b33e71bebf0b5fc5ab60e63d2e0bcaa260f937", [], [{:exactor, "~> 2.2.0", [hex: :exactor, repo: "hexpm", optional: false]}], "hexpm"},
"credo": {:hex, :credo, "0.8.6", "335f723772d35da499b5ebfdaf6b426bfb73590b6fcbc8908d476b75f8cbca3f", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [:mix], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.3", "b1896b129db30d54073bedd5f3ba8a99dd6d64ebae3cf59057ae287060b46905", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.16.4", "4bf6b82d4f0a643b500366ed7134896e8cccdbab4d1a7a35524951b25b1ec9f0", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [], [], "hexpm"},
"hackney": {:hex, :hackney, "1.9.0", "51c506afc0a365868469dcfc79a9d0b94d896ec741cfd5bd338f49a5ec515bfe", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
Expand Down