whitfin / cachex

A powerful caching library for Elixir with support for transactions, fallbacks and expirations
https://hexdocs.pm/cachex/
MIT License
1.59k stars 102 forks source link

Cachex guarantees #248

Closed sezaru closed 4 years ago

sezaru commented 4 years ago

Hello, I'm having some trouble understanding some guarantees of Cachex as a cache mechanism for databases (using Ecto with Postgres in my case).

For example, let's say I want to add a new value to the database and at the same time add it to the cache list, something like this:

{:ok, _} =  MyRepo.insert(value)

Cachex.get_and_update(:my_cache, "key", fn
  (nil) -> {:ignore, key}
  (list) -> {:commit, [value | list]}
end)

What I don't understand is if functions like get_and_update are guaranteed to succeed. If that is not guaranteed, it means that in the above example I can insert the value to the database, and then get_and_update fails for some reason making my cache outdated as it will never contain the value when I use get or fetch.

I guess I can make sure I don't have that problem by using an Ecto.transaction with Multi and adding the get_and_update as the last step of Multi. that way the database write would rollback if Cachex call failed.

Is that necessary or I can safely use something like the above example as long as my fn for the third parameter is not broken?

whitfin commented 4 years ago

The best way to say this is that it's undefined. In the current codebase, these things are basically guaranteed - but you should react to the result that is returned and handle it accordingly. This ensures that you are protected in case Cachex ever changes the underlying implementation such that it's no longer guaranteed.

Doing what you said with transactions makes a lot of sense. Another pattern is to avoid manually updating the cache, and instead use a warmer or a fallback mechanism with a TTL. LMK if you need some examples!