4 ms·
Forking is going out of fashion in Ruby land really fast due to the problems you mentioned. Now multi-threading is the norm it's easier to just run one process
by jashmatthews 8y ago
Forking is going out of fashion in Ruby land really fast due to the problems you mentioned. Now multi-threading is the norm it's easier to just run one process per core and live with a little extra RAM usage.
I was working on some more improvements to forked memory usage in CRuby but I don't think it's worth pursuing.
- joelbluminator 8y agoWhat are you basing that it's going out of fashion? Shopify and Github both run unicorn which is pre forking as far as i know. I think some companies prefer to prevent thread safety issues and pay the extra performance cost.
- jashmatthews 8y agoYeah, and Shopify are still using Resqueue too but almost all newer large apps are going multi-threaded from the start and using Puma + Sidekiq. Puma has exceeded Unicorn in total downloads from RubyGems now.
- coder543 8y agoI meant all kinds of internal forking. Running a process per core is just pre-forking.
- jashmatthews 8y agoSidekiq just runs N independent processes in the default setup. No forking at all. Pre-forking is an optional thing in Sidekiq Enterprise.
- coder543 8y agook, so that is a fair distinction. The point I'm trying to make is that you can't share resources easily between all of those processes, even though they're on a single machine, so you usually open a lot more database connections that you would need with a single shared connection pool. So, people often end up dealing with PgBouncer and other inconveniences much earlier than they would otherwise need. But you're right, it's not necessarily forking.
- jashmatthews 8y agoTrying to share a much smaller number of connections between a larger number of threads with fine-grained checkin/checkout is a nightmare, in my experience. You end up with all sorts of difficult resource and lock contention issues. As soon as you need a simple transaction you're stuck holding the connection for the duration anyway.
- coder543 8y agoIn my experience, it's all handled transparently behind the scenes... there is no headache. In Rust, checking a connection out is a single function call on the pool, which is easily shared among all threads, and it will automatically get checked back into the pool when the connection goes out of scope... you don't have to do a single thing to check it back in. In Go, the connection pooling is all handled transparently behind the scenes, such that you don't even need to know it's happening. I actually had to do some googling when I started using Go, as I was concerned that no one was recommending the use of a connection pool... creating and tearing down a connection per request is just wasteful when connection pools are so nice to use. It just turns out that Go embraces connection pools so deeply that I don't know of an easy way to avoid pooling your database connections. If your application gets bottlenecked by the number of connections in your pool, it's easy enough to increase the number, but the more independent pools you have, the more overprovisioned connections (connected but not being used) you will have scattered throughout those pools. It's also usually possible to run a connection pool without an upper limit, if you trust your database to handle large number of connections gracefully. Rust and Go's connection poolers will also automatically scale down the connection pool when connections are idle for a given period of time, which is nice. I can't think of any nightmares or headaches that I've encountered with those connection poolers. It all "Just Works"... except for PgBouncer, the ultimate connection pooler. PgBouncer doesn't work with prepared statements or transactions unless you run it in transaction mode, and then you have to run every query in a transaction to use prepared statements. I'm definitely not suggesting that you try to serve 1000 concurrent requests with 10 connections or something silly like that, but that is what often happens when you get large Ruby deployments which would attempt to establish more connections than Postgres can handle, so you route them through PgBouncer where a small fraction of the number of connections exists. But, this is pretty off-topic at this point. I didn't mean to point the conversation in this direction.