Puma vs Phusion Passenger
github.com
github.com
A couple of notes: Puma has a dynamic pool but it's common to see people configure the pool as 16:16 in production, meaning it's staticly configured to 16 threads.
Some people have asked about using Puma to manage multiple apps recently. I'm currently consider what that would look like. If it's done, it would likely be a separate gem rather than being wired directly into Puma as it is now.
On the topic of time limiting requests, this is conscience choice. Aborting a thread or process externally is really problematic and I don't personally think it's something that should be done casually. If a user wants to do time limiting, they can easily use a Rack middleware that uses timeout.rb.
The same goes for OobGC. Performing it in a multithreaded env makes little sense because the only out-of-band is when all threads are idle. I have thought of some ways to implement it in puma, but I see OobGC is a kludge. Better to tune your interpreter to handle your garbage load better (something 1.9/2.0/rubinius/jruby can all do).
Thanks for the great comparison! I look forward to future back and forth and we all work to raise the level of technology used in ruby webservers.
I keep reading how Puma is meant for be run multi-threaded on JRuby or Rubinius.
Would it be non-sense to run it in "cluster" mode (multi-process) under MRI 2.0? Similar to how we'd run Unicorn, for example. Any benefits doing that versus just running Unicorn?
EDIT: we have plenty of ram so that's not really a concern
Unicorn currently does have better fault-tolerance tools than Puma though, e.g. the 1-minute timeout on that Unicorn has by default. Unicorn also currently has more documentation than Puma. So there is a tradeoff in the choice, and there's still plenty that all the app servers can learn from each other.
https://github.com/jrochkind/fake_work_app/blob/master/READM...
For some reason it didn't get much HN traction. But either it inspired these recent HN posts involving puma, or it's just a coincidence -- either way I'm glad to see puma getting more attention.
I think multi-threaded request dispatch (from either puma or passenger enterprise) are often the best way to maximize throughput in a web server with I/O-bound work, as the Java community has been doing for a while. Any implementation details are just minor tweaks compared to just doing multi-threaded concurrent request dispatch in the first place -- so it's no surprise to see Passenger Enterprise and puma performing similarly.
i agree with the Phusion essay, that Passenger currently has more robust admin/management/supervision features than puma. I hope puma continues to evolve, inspired by passenger
Although these features are not trivial to implement well -- I am very impresed by passenger's feature set, and very robust and reliable performance.
But if you want a free/open-source solution, or a solution that it makes sense to run on heroku -- puma is a (and the only) solid concurrent-request-dispatch solution already.
There are two reasons I did not include Passenger Enterprise in my benchmarks:
1. As far as I know there is no way to run it on heroku. And I was intentionally benchmarking on heroku.
2. I wasn't going to pay for it just for the purpose of running benchmarks on it.
http://jasdeep.ca/2013/07/deploying-rails-apps-with-puma-and...
I appreciate the compare and contrast.
Perhaps he meant putting Puma directly on port 80. Although I usually wouldn't do that, I am not entirely sure whether one shouldn't do that. For example Unicorn is multi-proces single-threaded so it should never be used without a buffering web server, but Puma is different. We'll need the Puma author to make a statement about this.
I'm glad the comparison was useful to you. :)
For more scalable architectures I prefer putting multiple of these app instances behind a Load Balancer such as Nginx reverse proxy. That is part 2 of my post :)
I'm running Puma in production for a simple project which we have behind a Nginx load balancer.
I needed about 5-6 passenger processes in my app to service requests in a timely fashion moving to puma essentially cut down the processes I needed to run to 1 ... saving me about 300MB of RAM, but not only did it save me memory, performance was better than with Passenger.
Granted, I miss the ease of just typing
touch tmp/restart
to reboot my app, and I had to set up Apache (then later Nginx) proxying to get it going but it was well worth it.
PS: here is my writeup on how you can get puma up and running with your Apache/Passenger setup in less than an hour to try it out http://www.concept47.com/austin_web_developer_blog/rails/how...
http://stackoverflow.com/questions/15184338/how-to-know-what...
Yeah, Ruby core data structures are not thread-safe. But that doesn't matter. You're not supposed to a lot of any data structures between requests anyway. Of the few that are shared (e.g. the Rails cache object), they are explicitly engineered to be thread-safe.
There's also the story that Ruby cannot use multi-core. That's partially correct: on the MRI implementation, it doesn't matter how many threads you have, only one can be active at a time. However what you can have is multiple processes, each with multiple threads. Each process can use a different core. Furthermore, the JRuby and Rubinius implementations can handle multi-core with multithreading just fine. Both Phusion Passenger and Puma support JRuby and Rubinius.
Maybe I'm just too familiar with Ruby and multithreading, but I don't understand why people have trouble with multithreaded Ruby. In my eyes it's pretty easy. If you have a library that's not thread-safe, then just don't share that library's objects between threads, or ensure that you grab a lock before using that object, and you'll be fine. In my opinion, the situation is not much different in Python, Java or C++.
For applications that utilize more blocking I/O though, e.g. applications that perform a lot of HTTP calls, you need a multithreaded or an evented app server, as well as an app written to support that concurrency style. Otherwise you will run out of concurrency very soon, and your app will spend a lot of time waiting on I/O instead of doing useful work.
If the request-response loop spends a non-trivial amount of time waiting on I/O -- and most do -- then concurrent request dispatch has huge advantages. I tried to investigate/show that in these benchmarks: https://github.com/jrochkind/fake_work_app/blob/master/READM...
(And writing a ruby app to actually work right with evented/reactor-style request dispatch turns out to be way worse than multi-threading, in actual practice. http://www.slideshare.net/KyleDrake/hybrid-concurrency-patte... )
(Your reference to 'blocking I/O' is confusing, I think -- that phrase means different and sometimes opposite things to different people/contexts. But it's not even neccesary to get into it for this discussion, you just mean 'I/O' at all, I think.)
With blocking I/O I mean I/O calls which put the process to sleep for a while. This is in contrast to I/O calls which succeed immediately.
In other contexts, people talk about "non-blocking IO" and "blocking IO" to mean, well, 'blocking IO' is where the process or thread is _not_ put to sleep, it's not being switched out _even though_ it's waiting on IO. For instance, most ruby database drivers used to be broken in this way with respect to multi-threading, but were fixed to be "non-blocking IO", so threads would actually be unscheduled (even in MRI) when waiting on IO.
For examples of this use of "blocking IO" vs "non-blocking IO" see http://en.wikipedia.org/wiki/Asynchronous_I/O (Ha, look what wikipedia URL did to "I/O").
That usage is pretty common, so I think your different use of 'blocking IO' is confusing.
I think the problem is a mix of language features and culture. Ruby does not have a history good thread-safe practices, and the language has some extremely convenient features which are death for thread-safety (eg. class instance variables). Combine that with prolific meta-programming and a lack of static analysis tools, and it can become very very difficult to be certain that a given application is thread safe. As long as all developers are well-versed and keep thread-safety front and center from the beginning of application developer then I agree it's not hard per se, but in practice that is so rarely the case that if I knew I needed heavy multi-threading for memory efficiency and CPU utilization I might disqualify Ruby on cultural reasons alone (and I say this as a full-time rubyist who knows no language better).
A web application doesn't usually share much in-memory state _between requests_ -- especially if we're talking about app-specific code and not framework code. If it does (modify class variables or other global state, etc) -- you've got to eliminate that or make it threadsafe, but that's not _that_ hard to identify. A web application is very rarely going to be doing class-modifying meta-programming _as part of request handling_ (as opposed to on boot, where it won't be a problem).
Your actual app-specific code in a web application is highly likely to be MT-request-dispatch safe already, and if not is not too hard to get it there -- and worth the effort because of the extreme throughput increases you can get with an MT-request-dispatch app server.
Now, what can definitely be trickier is framework and gem code. ActiveRecord, historically, has had quite a few problems with thread-safety under MT-request-dispatch, off and on (the database connections themselves are the shared state it's tricky to deal with, among other things). But it's gotten a LOT better and should be fairly robust now.
I know what you mean about 'cultural reasons', but I think the ruby culture has been gradually changing for a while (jruby has a lot to do with it), and some of us hope is becoming more MT-friendly.
But I'm not saying it's trivial or guaranteed problem free, but the potential gains are worth it.
(I do run a rails app that uses multiple threads and ActiveRecord.)
Weeell, but this (class-level state) is true of virtually all non-functional environments though, right? -- including the otherwise excellent concurrency support within the JVM.
If someone's using class/global state in a contentious, multi-threaded environment, they...probably should not be developing on that particular problem.
As you pointed out, the fact of class-level state isn't so much an argument against Ruby per se. To me it's more of an argument in favor of stateless/functional programming where data contention is high -- which is somewhat besides the point in this conversation because we're talking about (mostly) "embarrassingly parallel" concerns where data tends to be siloed and contention is low.
>> if I knew I needed heavy multi-threading for memory efficiency and CPU utilization I might disqualify Ruby on cultural reasons alone
But...aren't these exactly the kind of problems that REE/Passenger, Unicorn and Puma (not to mention EventMachine and Rack) have been digging into for years now?
As much of a bad rap as Ruby tends to get when it comes to threading, the GIL and so forth, I think what gets lost in that conversation is the fact that many flavors of Ruby application server configurations are serving thousands and thousands of concurrent requests in production every second and doing a pretty good job of it. In certain contexts, Ruby handles concurrency and parallelism admirably well.
"Culture" is a relative question -- are we talking about the guys at Phusion, or Engine Yard, or Heroku, or someone like Evan Phoenix, or Yehuda? Is that Ruby culture? There's some heavy threading brain-power in that group, and they've left a pretty big footprint on the Ruby/Rails community.
Or are we talking about (no offense) journeyman web coders who generally don't have to solve threading/high-contention, CPU-intensive problems all the time? I'm honestly not sure that at this point the Ruby community is any worse off than other communities in that regard.
In 2013, the Ruby/Threading issue is starting to feel (to me) a bit like the "Java is slow" notion did in the early 2000s. Might be time to look at what Ruby is actually doing in production and rethink that idea.
Unless you're running jRuby, you're limited by Ruby's green threads. If you want to saturate all of the cores on your server, you need 1-2 processes per core and then a thread pool within each to keep it well utilized.
I've been running a 4x10 rainbows process/thread setup in production with good results on AWS dual core systems.
Ruby (MRI) started using native threads in 1.9, but you are still bound by the GIL.
Not that they're not to be trusted and can't be unbiased in their judgment, but I'd put more trust in a more independent study,
But we are claiming that the Enterprise version is better than the both of them, with that one exception as documented on the page.
However this should not be interpreted as us abandoning the open source version in favor of the Enterprise version. We are continuously developing the open source version as well. Since the launch of Phusion Passenger Enterprise, the open source version has gained quite some improvements as well.
EDIT: after re-reading the conclusion, I see that it is kind of useless. Other than promoting Enterprise, it didn't say anything substantial. I've reworded the conclusion so that it's hopefully more useful for those who are not interested in Enterprise.
The "License and price" section at the beginning is also worded in such a way that it should be clear that the text came from the Phusion Passenger authors.
Any reason why you do not keep it with the company repo https://github.com/phusion ?
Thanks for the detailed comparison. Good to know how it stacks up against the other options out there.