Phoenix application local "cache" solution suggestions?

I often have a need for fast access to application-wide pieces of data. One common example would be application configuration parameters. Yes, they can be stored in the config files, and then “fetched” relatively quickly when needed but this is very inflexible and does not allow any modifications on-the-fly.

In “the other” web application framework I sometimes used a singleton class initialised from persistent storage (database table) upon first instantiation. It was fast enough and flexible in the sense that not only it served the configuration data across the whole application w/o DB round-trips or over-frequent object instantiations/collections but I also could build administrator interface where the parameters could be adjusted at will and changes would be both immediately accessible across the whole system and also persisted to the database so that server/application restart could not affect their values.

Now, what would be a “kosher” Elixir/Phoenix method of achieving similar functionality?

A global mutable state? :slight_smile:

For cache, I would think of cachex.
Or Agent
Or ETS…

And You also want it persisted on disk? Maybe Mnesia?

YEah :smiley: but… no :wink: really. More like a “fast local data server”. Something that wouldn’t have to reach out in order to fetch the data and serve it to consumers. It is vaguely similar to what one might want to use e.g. Redis for but only for tiny datasets, faster on very frequent reads and more confined – no outside trips except for persisting the highly infrequent changes.

Ideally something that would not require additional dependencies, like in the example I described above. From what I read in the docs, something built around GenServer or Agent could fit the bill but maybe what I need is a common use case, for which there is already a design pattern/best practice ?

Again, for persistence I’d rather use what I already have (properly managed) anyway - the DB.

I hope this helps: You may not need Redis with Elixir - Dashbit Blog

2 Likes

FunWithFlags works as you described. It’s backed by a DB and uses Phoenix pub/sub to update the cache (ETS) on every node when a value is modified through the UI.

The ultimate for fast reads + very infrequent writes would be persistent_term.

2 Likes

Mnesia is already included, with an ecto adapter.

You might have a one record table, with a jsonb file to store configuration in your DB.

How will this fit in distributed mode? Probably not well.

You can still do that easily in Elixir. Just make a GenServer that read persisted data on startup and put everything into Application’s configuration, then expose an API that read from Appication.get_env/3 and write to both Application.put_env/4 and the database.

1 Like

I used to have a table with multiple records, especially when not on Postgres :wink: but the general idea was the same

True that. Other approaches are better suitable in such cases.

Thanks - that’s what I derived from reading the docs too. It seems there are also “better” ways in Elixir though.

I would also use a gen_server, coupled with an ets table.

The gen_server load config from somewhere, and manage ets table.

The ets table here is used to avoid any reading bottlenecks.

I would not care at all how to persist changes, this should be done in async mode, with db, or a flat file (with binary_to_term, term_to_bynary).

It’s used only when You need to reboot the system, and BEAM applications are known to run for years.

With Ruby/Rails, I was thinking db first, but not with Elixir.

1 Like

To persist configuration in a flat file, I’d write a text file with :io_lib.format("~tp.~n", [term]), which can be read back with :file.consult(path). The file is in the format of the sys.config format, editable and more expressive than json.

2 Likes