fireproofsocks

fireproofsocks

Best practices: gen_stage to prevent overloading database

I’m resurrecting some old code that relied on gen_stage to prevent the databases (postgres and mongo) from being overloaded. However, now that I’m looking at it, I’m not sure if it is following best practices. From what I remember, we had problems when processing large amounts of data that required database lookups, conceptually, this boiled down to something like

big_list_of_records
|> Enum.reject(fn x -> already_exists_in_db?(x) end)
|> cont_to_next_step()
# ... etc...

From what I remember, when we had a bunch of processes running with code like this, the database could get slammed (and I don’t remember if this was both mongo and postgres or if this was specific to only one, or if it had to do with a driver-issue).

Our solution at the time was to break up our pipelines so the database lookups happened in their own process (controlled by GenStage, but without any control over the demand). I.e. the “database producer” did this:

def handle_demand(_demand, state), do: {:noreply, [], state}

The thought was that it couldn’t magically summon demand, it just chewed on the stuff that the other processes sent to it. The effect was that this provided a way to have a mailbox for those items needing database cross-checks and it provided some control over concurrency with the various GenStage options.

However, now that I’m looking at this, I’m wondering if organizing our stages this way is the anti-pattern the gen_stage docs warned about? It feels like we are re-inventing a wheel. Our processes get bottlenecked (by design) by these database lookups – it’s almost like having a rate-limiter in front of it. Yes, this does prevent us from over-taxing the database, but shouldn’t the database drivers have their own queuing/pooling built-in so we don’t have to think about them? Not to mention the fact that by sending all this info into another process mailbox, we end up having to copy data across process boundaries at the cost of some efficiency.

My gut is telling me I should just implement each use case as a “siloed” GenStage producer + consumer and only put a layer of protection around the database if it really is needed.

I’m working solo on this, so I appreciate a sanity check from the forum comrades. Thanks!

First Post!

LostKobrakai

LostKobrakai

Yes there are queues everywhere, but queues don’t fix overload. You’ll need to be able to detect if you’re trying to do too much (e.g. the backpressure of GenStage vs how much events queue up in the producer) and then decide what you want to do if there’s more work than you can handle.

Last Post!

fireproofsocks

fireproofsocks

That was a great article, thank you!

I will keep this in mind as we redesign our input strategy.

Where Next?

Popular in Discussions Top

CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). Elixir Enum.at … coo...
New
MarioFlach
Hello, I want to share a project I’ve been working on for a while: https://github.com/almightycouch/gitgud Background Some time ago I ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
pillaiindu
I want to convert a Phoenix LiveView CRUD website to a CRUD mobile app. What do you think is the easiest way to do so?
New
Rustixir
Hi everyone, im working on find best language/framework/system for high concurrency, high performance and stable performance after wor...
New
fireproofsocks
I’ve been working on an Elixir project that has required a lot of scripting. I usually reach for Elixir because I like it more (and in th...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

We're in Beta

About us Mission Statement