Caching in Phoenix/Elixir with Cachex

A quick and dumb-down explanation of how to set up cachex in phoenix/elixir and an optional cache table/service management for awesomeness 😊 . No more chitchat on why caching is important blah blah blah, let’s go straight to coding.

Cachex Installation:

Add cachex to your list of dependencies:mix.exs

def deps do
[{:cachex, "~> 3.3"}]
end

Ensure cachex is started before your application:mix.exs

def application do
[applications: [:cachex]]
end

Starting your cache:

In your application.ex

def start(_type, _args) do
import Supervisor.Spec
children = [
MyApp.Cache
]
end

Setup child_spec for your cache:

Requirements:
- must be of type :supervisor , not a :worker
- must have a unique id

defmodule MyApp.Cache do
@moduledoc """
Cache
"""
@cache_table :my_app_cache def child_spec(_init_arg) do
%{
id: @cache_table,
type: :supervisor,
start:
{Cachex, :start_link,
[
@cache_table,
[
warmers: [
warmer(module: MyApp.CacheWarmer, state: "")
]
]
]}
}
end
end

Setting up cache warmer:

defmodule MyApp.CacheWarmer do
@moduledoc """
Cache Warmer
"""

use Cachex.Warmer
alias MyApp.CacheModule

def interval, do: :timer.minutes(60)
def execute(_args) do
with {:ok, data} <- CacheModule.get_data_somewhere() do
{:ok, data, [ttl: :timer.minutes(60)]} end
end
end

interval : Returns the interval this warmer will execute on. (Basically when you want the warmer to rerun)

This must be an integer representing a count of milliseconds to wait before the next execution of the warmer. Anything else will cause either invalidation errors on cache startup, or crashes at runtime.

execute : Executes actions to warm a cache instance on interval.

This can either return values to set in the cache or an atom :ignore to signal that there's nothing to be set at this point in time.

ttl : Time to live, expiration for an entry in a cache.

This is a millisecond value (if set) representing the time a cache entry has left to live in a cache. It can return nil if the entry does not have a set expiration.

Handling cache on one table (optional):

If you want all your services to share just one cache table, you should parse your data before returning it to the warmer.

defmodule MyApp.CacheModule do
@moduledoc """
Getting Cache Data
"""
alias MyApp.Repo
alias MyApp.Schemas.{
User,
Movie
}
def get_data_somewhere do
with users <- get_data(User, :user),
movies <- get_data(Movie, :movie),
data <- users ++ movies do
{:ok, data}
end
end
defp get_data(struct, type) do
struct
|> Repo.all()
|> parse_cache(type)
end
defp parse_cache(data, type) do
data
|> Enum.map(fn f ->
{{type, f.id}, f}
end)
end
end

In the parse_cachemethod, you want to structure the data like this {type, f.id}, f , you’ll see its use later on in the example.

type: is the cache type, in our example, the :user or :movie

f.id : is the cache key

f : is the actual cache data

Examples (if you set up one cache table):

Since we set our application to only have one cache table for all our services/data, using cachex functions will be a little different, but I’m sure you can get it easily

Retrieves an entry from a cache

In cachex documentation:

Cachex.get(:my_cache, "key")

In our app:

Cachex.get(:my_cache, {type, "key"})

Example:

Cachex.get(:my_cache, {:user, user.id}){:ok, %User{...}}

Placing an entry in a cache

In cachex documentation:

Cachex.put(:my_cache, "key", "value")

In our app:

Cachex.put(:my_cache, {type, "key"}, "value")

Example:

Cachex.put(:my_cache, {:user, user.id}, user){ :ok, true }

And that is it, a quite short blog, but I hope you get the idea, you can have a look at cachex documentation for more details here: https://hexdocs.pm/cachex/getting-started.html, Thanks!

Happy Coding!!

--

--

--

www.alvinrapada.com

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Key learnings of Dok 2021 Survey Report

Developing Automated Tests with the Four-Phase Test Pattern

Convention over Configuration

Introducing OptionRoom

EKS Anywhere & Octant., a simple yet a very powerful UI to visualizing EKSA clusters

Complete Guide — Getting a Software Engineering internship at Google, Amazon, …

Testing with DynamoDB Toolbox

Structure, System, Form

Tools for the non-technical

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alvin Rapada

Alvin Rapada

www.alvinrapada.com

More from Medium

Prototype Pollution HIGH vulnerability in ‘mixme’ NPM package

RestfulAPI powered by AWS-Lambda

Getting Java-11 docker images to run on mac m1 (arm64) with adoptium/temurin

Keycloak, clients and roles: a tutorial