tap349

tap349

How to store lots of data in memory?

Good day!

I searched the forum for similar questions but couldn’t find exactly what I need.

I use Agent process to store shared read-only data in memory. The size of data is ~10 MB.
Every user has its own handler process (implemented as GenServer process) that uses this data to calculate some result. So every time request comes, 10 MB are copied from Agent process (since everything must be immutable I guess).

I’ve noticed that the 3rd or the 4th time data is read from Agent process in each user process, it’s not fully garbage collected so memory consumed by each user process grows in size.

And if I have 1000 users, total memory consumption might grow up to 10 MB x 1000 = 10 GB which seems unacceptable.

My question is how to store lots of read-only data in memory so that it’s efficiently shared among many long-running process? Using ETS doesn’t seem to solve the problem.

P.S. data stored in memory is a parsed YML file. It occupies 10 KB in filesystem but 10 MB when read and parsed - maybe there’s a way to optimize storing it in memory as well?

Thanks.

First 10 of 20 Posts Switch mode

yurko

yurko

P.S. data stored in memory is a parsed YML file. It occupies 10 KB in filesystem but 10 MB when read and parsed - maybe there’s a way to optimize storing it in memory as well?

There should be, it’s 1000 times more in memory then on disk, doesn’t sound right :slight_smile: How did you measure this difference? If you dump it into file, is it 10kb again?

Using ETS doesn’t seem to solve the problem.

If you store it there “as is” as one entry it will not bring much, but if you put the parts of that (structured) data under different keys it will help with memory consumption.

See related discussion:

LostKobrakai

LostKobrakai

Depending on how often your shared data does change maybe look at this one: GitHub - discord/fastglobal: Fast no copy globals for Elixir & Erlang. · GitHub

cdegroot

cdegroot

I don’t understand that, because storing lots of in-memory state in ETS usually works fine. Unless of course, you still copy the data out of ETS for every request…

(also, the size does not make sense; what library are you using to parse the yaml and how does the structure look like internally e.g. when you inspect it?)

Ordermind

Ordermind

Depending on your needs it might work to have a genserver that holds the data and which you can query through an api to get the parts you need. If that’s possible for you it should be able to cut down memory usage.

tap349

tap349 OP

There should be, it’s 1000 times more in memory then on disk, doesn’t sound right :slight_smile: How did you measure this difference? If you dump it into file, is it 10kb again?

Sorry for misleading all of you about data size - after reading file from disk I calculate and store some additional metadata. That’s why memory grows that large )

If you store it there “as is” as one entry it will not bring much, but if you put the parts of that (structured) data under different keys it will help with memory consumption.

It would be an option but unfortunately I need all this data to process each request :frowning:
This data (read from YML file) contains sets of rules all of which need to be evaluated against each request.

tap349

tap349 OP

Depending on how often your shared data does change maybe look at this one: https://github.com/discordapp/fastglobal17

That looks like I what need exactly - but I’m still a little bit surprised that Elixir/BEAM copies all the data on each read from Agent process, that there are no optimizations in this regard since it must be a common case. And copying all the time for each user doesn’t sound scalable.

I’ll give this library a try )

tap349

tap349 OP

(also, the size does not make sense; what library are you using to parse the yaml and how does the structure look like internally e.g. when you inspect it?)

This part of my question is no longer relevant as I wrote above - YML parser must be okay (I use a fork of yamler).

I don’t understand that, because storing lots of in-memory state in ETS usually works fine. Unless of course, you still copy the data out of ETS for every request…

Frankly speaking, I didn’t try ETS solution by myself - I judge by what I’ve read somewhere on this forum: storing in-memory state in ETS is okay but when this state is read by thousands of processes, it’s copied from ETS each time leading to the problem I stated in the first post.

tap349

tap349 OP

Depending on your needs it might work to have a genserver that holds the data and which you can query through an api to get the parts you need. If that’s possible for you it should be able to cut down memory usage.

As far as I understand, Agent is GenServer as well. So I must be already using it )
The point is that I need not the parts but all this data at once and on each request.
So using any kind of API wouldn’t help me.

Thanks for all answers - it looks like it’s a normal behaviour in OTP: when dealing with both Agent process and ETS, data is copied into client process each time.

So possible solutions I see now:

  • use fastglobal package mentioned by @LostKobrakai
  • tune Erlang VM to use more agressive garbage collection strategy
  • kill long-running user handler processes when there are no messages in their mailboxes (maybe this is why not all data read from Agent process is garbage collected after processing request)
  • reduce the size of data (I store lots of IDs - probably using something like NatSet instead of MapSet would help)
cdegroot

cdegroot

Compile the set of rules. Clearly it’s code, not data. Problem solved.

Another way - depending on the problem - would be to have a pool of workers that each have a copy of the ruleset as state and something like poolboy to route requests; that would also get rid of the copying.

cdegroot

cdegroot

It becomes less of a surprise if you think through the implications of doing that. Erlang does garbage collection on the process level, which is very simple in multiple regards: process memory is small, and processes are interruptable so a small pause to do a quick GC is acceptable. This keeps GC simple. Now, think of the case when you would get data by reference out of a process (an Agent is just a process) - suddenly the GC has to keep track of pointers globally in the VM and simplicity gets tossed out of the window.

(the actual details are, of course, a bit more complicated than I just said. This seems to be a decent quick overview with pointers to further reading. Erlang will ask you to open the hood and look at how the engine works a bit sooner than other systems, but the pay-off is good performing stable code and the investment isn’t that high (compared to, say, learning the Java Memory Model). Well worth it.)

Where Next?

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
silverdr
Using Phoenix.LiveView.TagEngine as an EEx.Engine is deprecated! To compile HEEx, use Phoenix.LiveView.TagEngine.compile/2 instead. Sta...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
michallepicki
I am using Oban and occasionally, shortly after a deployment, a handful of jobs can fail because of dependency on other parts of the syst...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement