Regarding hosting, I think this is actually a great time to host Rails apps either via Heroku, via the new Digital Ocean k8s app cluster or – and this is my new favourite tool – using Hatchbox.io to deploy apps to Digital Ocean or AWS. It’s a dream!
Regarding hosting, I think this is actually a great time to host Rails apps either via Heroku, via the new Digital Ocean k8s app cluster or – and this is my new favourite tool – using Hatchbox.io to deploy apps to Digital Ocean or AWS. It’s a dream!
I wanna say something nice about rails too so I’ll say I have never seen a team so quickly deliver high quality web app features than a well oiled rails team. It’s something to behold.
But that being said, the primary driver for tools should be what the developers know and ease of access finding developers who know this technology. If the city you work in mostly has PHP developers, PHP is a great choice. Similar for Java, Haskell, Lisp, etc. My point is that the tool ”Rails” definitely is adequate for this problem (minus streaming video...). Look at Shopify, Github or any other massive Rails app
Yeah, if you are serving video at that rate, there are plenty of CDNs to work with, why would you submit your app engine to that.
https://stackoverflow.com/a/373188
> Wikipedia seems to be 30000 to 70000 [requests] per second spread over 300 servers
So 2k is not unusual for bigger news sites, that may or may not employ a (few) rails team(s).
Financial exchanges are doing 100,000rps of transactions per sever. That’s hard.
If we were using Java it would have taken us three times the people and four times as long to build the site, and we all would have been laid off.
That says nothing about the added cost of running inefficient services, which require additional nodes to serve the same requests and thus increase operational cost and also risk to perform the same service.
In a CRUD app the Rails layer should be extremely thin and the storage layer(s) should be doing nearly all of the heavy lifting.
There is a level of traffic at which even a "properly" thin Rails layer becomes the bottleneck, relative to many other frameworks.
TechEmpower benchmarks suggest it is around 2,500 requests per second in their "multiple queries" benchmark. In a more real-world scenario that might be 1,000 req/sec or less.
https://www.techempower.com/benchmarks/#section=data-r19&hw=...
If one is attempting to serve more requests than this per minute then yes, perhaps Rails is the bottleneck. Admittedly, Rails' large memory consumption relative to other frameworks means it can be tough (well, technically easy, but expensive) to scale horizontally at this point.
That said, there are definitely some self-inflicted wounds people run into here. Of course there are the oft-cited expert beginner problems like n+1 queries and missing indices, but there's a more subtle issue as well: ActiveRecord itself is so convenient that it discourages writing baseline efficient code even when you need it. Basically any time you need to process a medium to large amount of data, ActiveRecord acts like a sort of super boxed type (to use Java terminology) adding a huge amount of overhead. Yes it's true that ruby doesn't even have true primitive types that can achieve the performance of Go or Java, but often times ruby primitives are plenty fast, you just have to be willing to write a bit of extra code, and ActiveRecord as an ORM has plenty of escape hatches at various levels of granularity to facilitate this (eg. pluck, find_by_sql, update_all, execute, etc).
At the places who did handle this well, the "high-performance code" that needs to be hand-tuned SQL is usually much smaller than you think (a few queries here and there), and ActiveRecord is still great for your simple queries or for smallish tables.
My understanding is that MRI Ruby provides non-block IO operations if you wrap them in a thread and that it is only CPU bound tasks that are blocked by the GVL.
Is there some other issue related to that?
(JRuby provides fully multi-threading for cpu bound tasks without GVL).
All IO operations in ruby are subject to the GIL (global interpreter lock).
JRuby, for example, has no GVL including for CPU based code; everything there can run in parallel.
Even in MRI Ruby though, wrapping IO operations in a thread allows you to release the GVL when the IO operation blocks.
e.g.
5.times.map do
Thread.new do
Net::HTTP.get('example.com', '/index.html')
end
end.each(&:join)
Will perform those network requests in parallel rather than sequencially. This is how ActiveRecord can perform asynchronous database calls in parallel on MRI Ruby.I got that HTTP example from[1], which has a good write up but it's also covered in Working with Ruby Threads by Jesse Storimer[2].
I asked the original question because in the Concurrent-Ruby Readme they discuss Ruby's mutable references and the possibility of thread safety issues because of that.[3]
1. https://pawelurbanek.com/ruby-concurrent-requests
2. https://www.goodreads.com/book/show/17826435-working-with-ru...
I currently work on a rather large Rails app for a site that most of us here use. A common pattern for our performance pitfalls are things like this:
Tag.all.map(&:name)
versus Tag.all.pluck(:name)
Using `#map` will instantiate a `Tag` object and do all the slow(er) metaprogramming to get you the syntactic sugar that makes interacting with an ActiveRecord object a treat. It does this, and then you only hit `#name`, completely wasting all that effort.`#pluck` will change the SQL from `select * from tags` to `select tags.name from tags` and never instantiate a `Tag` object, instead short-circuiting and directly fetching the resulting SQL rows — which comes back as an array. It's something along the lines of:
ActiveRecord::Base.connection.exec_query(Tag.select(:id).to_sql)
Another one I see: ProgrammingLanguage.where(tag_id: @user.tags.select(&:language_tag?).map(&:id))
versus ProgrammingLanguage.where(tag_id: @user.tags.select(:id).where(type: 'LanguageTag'))
The first example loops over the loaded `@user.tags`, loads them if they're not already `#loaded?`, selects ones that are `type == 'LanguageTag'`, only to grab the `#id`.The second example joins the two resulting SQL statements and calls `#to_sql` on the second statement, building one query from two queries.
Are these times when the first example would be preferred? Yeah, plenty! If your result is already `#loaded?`, then you probably don't need to hit the database again. But for this example and the ones I'm stumbling across while getting our company up-to-speed on "good ways", these are the the commonalities.
Save for only very recently, the company I work for hasn't put emphasis on real-world Ruby/Rails skills, instead "if you can code at a high level for any language, we think you can make reasonable contributions to our codebase." This has lead to hiring skilled developers that just don't know that there's a more preferred way of doing things in Rails for different contexts.
Double-edged sword, really.
It's a good example of choosing magic/brevity over expressiveness. You don't know that Tag.all.map calls SQL because it's not something you explicitly tell it to do. That's the real issue with Ruby & Rails. The magic lets you do some powerful stuff but sometimes it's hard to tell what exactly is happening.
Confusion here simply shouldn't be a problem for even a moderately seasoned developer, and if they do make such a mistake (because hey, we all make mistakes...) in performance-sensitive code they could quickly recognize it for what it is - a bug - and fix it.
If you're hiring junior developers, on the other hand? Sure! But you should know what you're getting, and your code review / mentoring process should get them straight.
I'm not sure I really understand how this is Ruby's or Rails' fault, unless your premise is "ORMs considered harmful" - in which case, ActiveRecord is far from alone here, and that's a different sort of conversation.
I also deal with a lot of scale, the issues people have here doesn’t match my reality. I think people have issues and rather than looking at what is fundamentally happening with their call patterns, they jump to calling out rails itself.
Rails does have some specific issues, but you’d have to go pretty deep to see them and boot times are terrible.
https://medium.com/coryodaniel/from-erverless-to-elixir-4875...
Elixir/Phoenix solves a lot of these pain points and is a joy to work with. If you like Ruby/Rails then you already know quite a bit of Phoenix. Schedule yourself 4 hours to follow a tutorial or book.
Also Pragmatic Programmers has very good books on both, written by the creators.
I love Phoenix but I will disagree with the OP in 2 points.
Yes probably Elixir/Phoenix will manage better thousands of concurrent connections, but if you are doing personal projects or are a start-up that condition is irrelevant in the meantime. So that only lefts us with the big established applications. That does not mean Phoenix is not good, it only means you will not notice a difference with Rails for most of your projects.
The second thing, that OP failed to mention is that the Rails ecosystem is at least an order of magnitude larger. From the gems available, job opportunities,developers available, instructional material, SOP for many tasks and so on.That is boring, but valuable.
The official online documentation is pretty good too.
Lack of library support was a bit annoying at first. But then I realized that, back when I used to take advantage of existing libraries in Ruby/Rails, in the vast majority of situations I was just utilizing a small portion of those libraries anyway. It ended up easy enough to write my own code for those features.
It has the upsides of Rails without the connected antipatterns, native technical debt.
I can also confirm that there isn't a need to learn BEAM gotchas (which i did learn). One of my ex colleagues is a junior dev who has been using elixir for 3 years now and didn't need to use those special aspects of BEAM.
As a Ruby expert, Elixir is substantially easier, learning BEAM is a joke (and very instructive) compared to learning the entirety of the Ruby object model.
I can't think of reasons to use Rails nowdays. If you are in the CRUD apps market, use Postgrest or similar products. As for the rest sure, choose any language you want, the framework is way less relevant (arguably: detrimental) at that point.
The industry has proven extensively that for non-trivial projects, Rails is damaging due to ActiveRecord.
It's comforting to see I'm not alone. I just wanna ship clean code and contribute value to the business, while all the young bucks in my team are more interested in rewriting our REST apis in Graphql (with all kinds of rationalizations). Looks like the younger you are the more eager you are to explore new tech and the older you are the more keen you are in just delivering value in a boring old stack.
Same story here. Our company is the only API user, why go through all that trouble? Donno.
Whenever I take the plunge I am always reassured; Ruby and Rails does make me happy.
However, I got tired of the lack in conventions in Express, and Firebase has quite a few gotchas and limitations, so I'm moving to good old Django. I'm delighted thus far, I love not having to reinvent the wheel every time I start a project or add a feature.
I'm probably keeping React/Next.js as my primary working tools for the front-end nonetheless, but that's just me because I feel highly productive with them and I've become accustomed to the decoupled client/API architecture.
I can see the merits of SSR with templating as seen in RoR or Python, and I'm happy to have that tool in my belt. But conversely I also think it's worth for pretty much everyone to learn React (or Vue, Svelte, or what have you) because they do open different possibilities in UX engineering and app architecture, and the market is quite hot for them too.
So that weekend could rabbit hole into complexity figuring out which framework to use.
You don't have to make the whole app a SPA to use these tools. You can just use it in those few pages that really need it.
Alternative like Preact is very small and can be easily (pre)cached on the client side.