Search code examples
erlangelixirets

ETS entry limit, to use as a cache server


My idea is to use ETS as a temporary cache for my GenServer state.

For example when I restart my application, the GenServer state should be transported to ETS and when the application starts again, the GenServer should be able to get the state from there.

I would like to keep it simple, so all the state (a Map) from the GenServer should take a single entry. Is there a limit for entry-sizes?

Another approach would be, to simply create a file, and load it again, when needed. Maybe this is even better/simpler, but I am not sure :)

In case of an ETS table, the App could start on a completely other host, and connect to the Cache Node (ETS).


Solution

  • This can most certainly be done in a wide variety of ways. You could have a separate store, like Mnesia(which is ETS under the hood), Redis or a plain database. In the latter two cases, you would need to cast your Genserver state to a string and back doing: :erlang.term_to_binary and :erlang.binary_to_term respectively.

    In the case that you are dealing with multiple GenServer processes that need to be cached in this way, e.g. every GenServer represents a unique customer cart for instance, then that unique identifier can be utilized as the key under which to store the state which can then later on be retrieved. This is particularly useful when you are running your shopping application on multiple nodes behind a load balancer, and every new request on part of a customer can get 'round robin'-ned around to any random node.

    When the request comes in:

    • fetch the unique identifier belonging to that customer in one way or the other,
    • fetch the stored contents from wherever that may be(Mnesia/Redis/...),
    • spawn up a new GenServer process initialized with that stored contents,
    • do the various operations required for that request,
    • store the latest modified GenServer shopping cart into Redis/Mnesia/wherever,
    • tear down the GenServer and
    • respond to the request with whatever data is required.

    Based on the Benchmarks I have done of ETS vs Redis on my local, it is no surprise that ETS is the more performant way to go, but ElastiCache is an awesome alternative if you are not in the mood to bother spinning up a dedicated Mnesia store.

    In the case it pertains to a specific GenServer that needs to run, then you are most likely looking at failover as opposed to managing individual user requests.

    In such a case, you could consider using something like: https://hexdocs.pm/elixir/GenServer.html#c:terminate/2 to have the state first persisted to some store and in your init make the GenServer first look in that store and reuse the cache accordingly.

    The complicated matter here is in the scenario where you have multiple applications running, which key will you utilize in order to have the crashed application reinitialize the GenServer with the correct state?

    There are several open ended questions over here that revolve around your exact use case, but what has been presented so far should give you a fair idea as to when it makes sense to utilize this caching solution and how to start implementing it.