Hacker News new | ask | show | jobs
by jupp0r 776 days ago
"I can make a JS or Go program perform the same way. In fact the exact same thing happened in my shop with Go/Gorm. The key question is: how do you compute the 5mb of JSON? The devil is in those details. We changed the way we computed ours, and the issue was gone."

The problem is ActiveRecord in my case. The data layout is not great (lots of joins through relationships, I think 12 tables or so). ActiveRecord objects are HUGE compared to the few bytes of actual data they hold.

What do you use (except raw sql) in Ruby if you cannot use ActiveRecord? There is no other ORM that's optimized for fast reads and I don't feel like writing one.

I actually reimplemented the same API endpoint in Go using https://github.com/go-jet/jet and measured 10MB of allocations and essentially zero overhead over queries itself, a 50x speedup.

Don't get me wrong, this is not what the typical Rails shop will deal with, but it definitely shows where limitations of Rails lie and I'm dealing with stuff like that on a weekly basis in my job, and it's not even a large Rails app (3M LOC).

1 comments

I agree that if you make a very complex query in ActiveRecord, it could eat a lot of resources.

That said, I would highly recommend going with raw queries for this sort of complexity, no matter the language. There are usually 2 kinds of queries: normal ORM-powered CRUD operations (which can get moderately complex), and hairy specialized report-style calculations. The latter I always recommend to keep in raw, well-written, well-commented SQL form. You can still wrap it into some nice object.

You could write them in something efficient like Jet or Elixir's Ecto, but for such a complex case I'd argue that you shouldn't obfuscate SQL at all. For all other cases ActiveRecord works well.

If you are serving these results in real time, something like materialized views (in postgres) would move the burden of calculation to when data changes, rather than when data is viewed.

And to tie it back to the original convo: a very efficient concurrent language doesn't solve these root causes, rather gives you more time not to address them, and allows you to get away with more neglect. There's some value in that, but you have to weigh it against the downsides mentioned in previous comments. If the language+framework is super efficient and has no downsides to its ecosystem and ergonomics, then there's no debate, I'd just use that.