kamaroly

kamaroly

What's the best approach to use Cachex with Ash framework for read query?

Is there an elegant way of using Cachex and Ash Framework?

I have a read query that I need to cache results for, because of the many relationships attached to it. I wonder if there’s a better way of caching results while reading in Ash Framework.

Here’s how I am doing it from the domain module.

  @doc """
  Get cached person if exists, otherwise fetch from the DB and cache for future fast-reading
  """
  def get_cached_person_first(person_id, context) do
    case Cachex.get(:person, person_id) do
      {:ok, %{} = person} ->
        person

      {:ok, nil} ->
        Hr.People.get_loaded_person!(person_id, context)
        |> cache_person()
    end
  end

Marked As Solved

barnabasJ

barnabasJ

Ash Core Team

There is no Cachex extension or anything like that at the moment.

A more Ash-centric approach would be to check the cache in the preparation of the read action, and if so, you can set the data on the query, and Ash won’t do the roundtrip to the database.

You can add an after_action hook into the read action to populate the cache

I would create a specific read action for this.

You can also use a global change that runs on updates and destroys to invalidate the cache.

Caching involves a lot of complexities, make sure you really need it :wink:

Also Liked

srikanthkyatham

srikanthkyatham

I am attaching a code, for the sake of reference. Combing through different resources. To come up with possible answer.

action

    read :read do
      primary? true
      pagination offset?: true, default_limit: 10
      argument :check_cache, :boolean do
        default true
      end
      prepare Preparations.CheckCache
    end


defmodule Preparations.CheckCache do
  @moduledoc """
  Checks a simple agent cache for libraries
  """
  use Ash.Resource.Preparation

  def prepare(query, _, _) when query.arguments.check_cache == true do
    CacheHelper.attach_cache(query)
  end

  def prepare(query, _, _) do
    query
  end
end
  def attach_cache(%Ash.Query{} = query) do
    query
    |> before_action()
    |> Ash.Query.after_action(fn query, records -> after_action(query, records) end)
  end

  def before_action(%Ash.Query{} = query) do
    cache_name = ReserveCache.name()

    key = query_filter_to_key(query)
    # take arguments to key as well

    updated_query =
      case Cachex.exists?(cache_name, key) do
        {:ok, true} ->
          {:ok, results} = Cachex.get(cache_name, key)
          Ash.Query.set_result(query, results)

        _ ->
          query
      end

    updated_query
  end

BartOtten

BartOtten

Every extra layer adds complexity and caching is one of those layers that tend to become complex. Most databases can be tweaked to cache more (they already do, but default settings are generally quite low) and if tweaking is enough, the complexity is completely handled by the database. Let the database do the heavy lifting for you (also research ‘materialized tables’)

That being said: let’s continue with the original question below.

jimsynz

jimsynz

Ash Core Team

Well I guess the first question is to ask whether you have a measured performance issue or are just assuming that there will be one?

Postgres is pretty damn good at planning and executing queries efficiently so I’d probably start by analysing the queries that Ash is generating to make sure that the appropriate indexes are in place and being utilised. Second if Ash is generating a pathological query I’d open an issue on the ash_postgres repo to see if it can be made more efficient. Only after I had exhausted the “just use the database” options would I seriously consider the complexity of caching or denormalising the data.

If the data is very fast moving then I might start considering my options re keeping some of it in RAM; caching is one method, the other is an ETS backed resource that works like a read through cache or is populated by changes to the underlying resource.

Otherwise, given that storage is usually cheaper and more abundant than memory I would consider denormalising the data into a JSON attribute somewhere and using that for reads. Probably populated by after action hooks or background jobs.

Where Next?

Popular in Questions Top

New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement