venomnert

venomnert

Context:

I was reading through Dockyard’s article on ETS. The blog post build a ratelimiter using a GenServer first and then replace it with ETS.

I was curious to see if I can replicate the same ratelimiter using DynamicSupervisor + GenServer. I have went ahead an implemented both versions (DynamicSupervisor and ETS).

Question:

  1. I curious to know how I can benchmark both the implementation?
  2. What’s the general idea regarding GenServer vs ETS?

Showing Posts 1 to 10

wolfiton

wolfiton

Hi,

I made this search on google Google Search and got this:

GenMetricsBench – gen_metrics_bench v0.1.0 - library to test genservers

https://thoughtbot.com/blog/make-phoenix-even-faster-with-a-genserver-backed-key-value-store -article how to use cache and test

keathley

keathley

The ets docs and lyse are good resources. The main benefit of ets is that it allows shared, concurrent access to data. If a process owns the data then other processes have to go through the owner to access that data. The data-holding process becomes a bottleneck on the system.

Standard benchmarks aren’t going to show you much here. What you’ll want to do is generate contention on the data by creating a lot of callers. Under enough load the process solution will begin to back up while the ets solution should remain relatively constant.

venomnert

venomnert OP

“If a process owns the data then other processes have to go through the owner to access that data.” Instead of having a single process be responsible for dealing with the data, what I decided to was to create multiple process which will be responsible for multiple data entry. And this is what I want to test against ETS.

I hope that makes sense :confused:

keathley

keathley

Yeah that makes sense. But lets assume that your data access follows a power law. One piece of data will need to be accessed much more than everything else. You’ll still end up with contention on a single piece of data. In either scenario the benchmark will be the same. Have multiple concurrent readers try to access data in a process and in ETS and compare their tail latencies (the worst 5% is typical).

If you assume that data access is uniform then using multiple processes will help to spread out the load. But most data access doesn’t follow a uniform distribution. Even if it did, accessing an ETS table is going to be faster. I threw together this gist to demonstrate. You can call the time/0 function on both of those modules and compare results.

bottlenecked

bottlenecked

Hi there! Be aware that reading data from ets has copy-on-read semantics. When combined with large binaries it can make for some ‘interesting’ behavior

keathley

keathley

This isn’t any different than reading from a process though. The data still gets copied. Persistent term avoids that but isn’t appropriate in all use cases.

bottlenecked

bottlenecked

Right, reading state from another process will copy that data in the message sent. For situations however where there is no need for another process to get hold of all the data (or none perhaps) it might make a difference, as data that stays local in a process is copy-on-write only

shahryarjb

shahryarjb

I am very confused, whole the bottleneck the dockyard talked about it and using ETS just for writing? Or it should be used for calling, even user can’t change the state of Genserver and the system only able to edit it?

Or for concurrent calling state by many users in a same time, you suggest to use ETS or top level of it like cachex? For example website setting, each user when put the URL in their browser should load it or user token for login in different platforms?!!

Thank you

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

GenServers are a bottleneck for both reads and writes. A genserver can only process a single message at a time, no matter if that message is used to change data or fetch data.

sbuttgereit

sbuttgereit

Let me given you an example case which uses both GenServer and ETS from the application I’m working on. My only caution is that I’m not very experienced with Elixir or other BEAM based applications so I may well, “be doing it wrong”.

In my application, there are a fair number of user maintainable settings which influence runtime behavior of the application for that tenant. These settings are persisted in the backend database, but the usage pattern is that any given setting will have its value read pretty frequently, but changed relatively rarely.

While I could retrieve setting values from the database on-demand, the fact that these settings are relatively static over time makes me think that’s a lot of avoidable noise to/from the database and that I am probably better off caching those settings in some way. So what I do is I have a GenServer for each tenant that loads all the settings from the database for that tenant on start-up. The GenServer itself creates an ETS table to load the settings into, rather than keep the settings as the GenServer state. The GenServer is the parent process of the ETS table and the ETS table is setup as a protected ETS table.

When the application needs to retrieve a setting value, the application directly reads the value from the ETS table. ETS reads are concurrent and shouldn’t block others reading or writing settings; as @benwilson512 says, this avoids the bottleneck that GenServers process a single message at a time. When a user decides to update a setting, this gets sent to the GenServer via a call; while ETS writes are concurrent, I want something that looks atomic between the ETS table and updating the setting in the long term persistence of the database (admittedly, looks atomic at a distance). So, because, the GenServer only processes one message at a time, it effectively serializes any setting update which is just a touch easier to reason about in my use case. Again, I expect writes to be rare, so concurrent writes aren’t essential in this case. Also, for my use case, while the settings are relatively static, there will be many of them. Accessing the settings via the ETS API feels more natural than having to parse through a larger state object that a GenServer will keep, even if those APIs aren’t in any sense “bad” or “cumbersome”.

To my mind, there is certainly a a bit of overlap of cases where either a GenServer or ETS tables would make roughly the same sense for sure which is why it can be a bit confusing. For me, it boils down to the relative weights of need for concurrency, controls, and perhaps the size of the state to be managed, though I’m much less firm commitment on that last point.

— All posts loaded —

Where Next? Top

Trending in Discussions Top

AstonJ
The obligatory hello world thread! Who are you and where are you from? :stuck_out_tongue:
4663 61218 595
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
515 12931 101
New
wmnnd
Hey folks, I wanted to share with you a project I’ve been working on for a while: It’s an Open Source (AGPLv3) newsletter tool. Think Ma...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
sntran
In my third year of college, around 2010, I found Erlang. A few years later, I found Elixir. I liked the ideas at once: many small proces...
New
Ganna
Hi everyone, I have 8 years of production experience with Erlang/OTP and I’m just starting to get to know Elixir. What would you learn f...
New
gtcode
I’ve been tinkering with Lynx (by José) via hold_ledger. I used AI to write the code, a separate specification, the tests, and the Lean p...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews