Live data from Hacker News

How to Fix Slow Code in Ruby

engineering.shopify.com

81–90 of 213 posts

Re: How to Fix Slow Code in Ruby

#81

Earlier quoted context omitted.

> Also, I'll take "double the hardware specs" if it means I'm actually able to focus on what I'm building and not dicking around with devops or rebuilding all of stuff Rails metaprograms for me by hand. Try 7x to 11x. That's the amount of reduced RAM usage -- and the amount of increased accommodated users -- on identical hosting by the two apps I rewrote from Rails to Phoenix. > If there was a framework for being as…

And yet Phoenix hasn't stolen a significant amount of market share from Rails. > There are a number of web frameworks that perform much better than Rails If you're measuring hardware loads and busting out your stopwatch to measure response times, then sure. Bottom line is, there are plenty of good reasons to choose Rails over Phoenix. If you want to label choosing a well-backed framework with an incredibly mature eco…

> And yet Phoenix hasn't stolen a significant amount of market share from Rails.

I thought we all learned popularity does not correlate with quality. It correlates pretty closely with corporate inertia and perceived lower risk of developer churn though. Businesses love tech stack for which there are bigger pools of programmers. Says nothing of the quality of the stacks.

> If you're measuring hardware loads and busting out your stopwatch to measure response times, then sure.

Needlessly snarky. Response times matter in a lot of businesses. Count yourself lucky that it hasn't been an important metric in your work.

> Have fun writing Ecto queries by hand

I do have fun writing those. Most times the code in my functions ends up more readable than the equivalent Rails code I wrote years ago. There are some exceptions where you have to dig deeper. Haven't seen a framework -- Rails included -- that lets you handle all complex cases with zero deeper digging needed. At one point you do have to understand SQL and query optimizations, no ways around it.

> wiring up document storage on your own

What for?

> trying to find a standout auth library of choice like devise

It's called Pow and works very well. Additionally, Elixir's maintainers themselves are authoring such a library at the moment.

> finding a library that makes managing database views less of a pain

We can argue if this is a good thing until the Sun explodes. It really depends on the business. I have consulted for businesses where it was very important and true enough, using a library that's well-tuned for classic web apps (like Ecto and ActiveRecord) isn't the best idea there.

> wiring up end-to-end system testing

For 3.5 years with Elixir this is the first time I hear that this is a problem. Any data to back this up?

> I'll be over here running rails new, wiring up sidekiq to ActiveJob, and building shit with ease.

More power to you. Rails is excellent for an MVP or a prototype and this is well-known. It's what comes after is what has burned me out of it. Its maintenance burden is much higher than many others, including PHP's Laravel.

Re: How to Fix Slow Code in Ruby

#82

Earlier quoted context omitted.

Interesting but not surprising; I'm being regularly pleasantly surprised by the Erlang/Elixir ecosystem :) . Can you precise what you're talking about when you say "Erlang/Elixir have built-in caches" ? What's the name of the concept and where in the typical stack does it fit? Is this https://blog.appsignal.com/2019/11/12/caching-with-elixir-an... or something else? Care to share a few link to docs/articles? Thanks.

Yes, ETS is the usual go-to but there's also `:persistent_term`[0] for very rare (or never) changing caches. There are libraries that combine Erlang/Elixir's caching mechanisms in an attempt to achieve the best performance for most scenarios[1] as well. Technically, ETS is not perfect because it copies data from its mutable cache to the process that requests it. But it's still orders of magnitude faster than outsourc…

Thanks!

I read in http://erlang.org/doc/man/ets.html that "Each table is created by a process. When the process terminates, the table is automatically destroyed. Every table has access rights set at creation."

-> So, caching is local to each worker node/process? Do nodes/processes communicate between them to synchronize their respective caches, or is it local by design?

If local by design, that wouldn't exactly cover the use case of "Redis in front of an army of $other_language workers", correct? And I guess it's accepted as costing slightly more cache misses, but with the benefit of more decentralization / node independence / resiliency, right?

Re: How to Fix Slow Code in Ruby

#83
post #57

Earlier quoted context omitted.

Stop painting ruby as slow. In benchmarks i've seen it's handily beat out PHP, which still runs much of the web. the ruby 3x3 goal, of getting running super mario at 60FPS, was if i recall correctly, already reached (or close to it) in ruby 2.6. https://developers.redhat.com/blog/2018/03/22/ruby-3x3-perfo... additionally: > Sinatra + Sequel is already very competitive in web performance with Go > Between Ruby 1.8 and…

"Sinatra + Sequel is already very competitive in web performance with Go" A lot of the time, that "competitive with X" in web frameworks is because the scripting language has a web server coded in something other than the scripting language. I don't know about that exact stack, but I know that's the case for Node, for instance. The web server is written in C. So when you benchmark a "tight loop" in those languages, y…

Ed: regarding the benchmark, as best I can tell, the top listing for ruby runs under puma:

https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...

PostgreSQL is obviously not written in ruby, but the other components are very much "ruby code". There appears to be c/Java extension for ssl and parsing http, though - but AFAIK the main server is ruby.

https://github.com/puma/puma/tree/master/ext/puma_http11

Tangential, but you might enjoy:

https://deno.land/v1#http-server-performance

> A hello-world Deno HTTP server does about 25k requests per second with a max latency of 1.3 milliseconds. A comparable Node program does 34k requests per second with a rather erratic max latency between 2 and 300 milliseconds.

> Deno's HTTP server is implemented in TypeScript on top of native TCP sockets. Node's HTTP server is written in C and exposed as high-level bindings to JavaScript. We have resisted the urge to add native HTTP server bindings to Deno, because we want to optimize the TCP socket layer, and more generally the op interface.

Re: How to Fix Slow Code in Ruby

#84
post #77

Earlier quoted context omitted.

When my small SaaS customer calls and asks for an extra extension at current hard times, then all I need is to open up Rails production console (pry) and live type in an ad-hoc code with auto-completion aid: $ rails c Loading production environment (Rails 5.2.0) [1] pry(main)> s = Subscriber.find_by_email('xyz@abc.com') => # s.expires_on += 2.months => Wed, 15 Jul 2020 14:10:19 UTC +00:00 [3] pry(main)> s.save! => tr…

I can do the same with Elixir's Phoenix, with exactly 3 lines in a production console like in your example. What you describe is not exclusive to Rails.

Yeah, maybe I should rewrite one day, but customers wouldn't care much and I am content wit Ruby.

Re: How to Fix Slow Code in Ruby

#85

Earlier quoted context omitted.

Yes, ETS is the usual go-to but there's also `:persistent_term`[0] for very rare (or never) changing caches. There are libraries that combine Erlang/Elixir's caching mechanisms in an attempt to achieve the best performance for most scenarios[1] as well. Technically, ETS is not perfect because it copies data from its mutable cache to the process that requests it. But it's still orders of magnitude faster than outsourc…

Thanks! I read in http://erlang.org/doc/man/ets.html that "Each table is created by a process. When the process terminates, the table is automatically destroyed. Every table has access rights set at creation." -> So, caching is local to each worker node/process? Do nodes/processes communicate between them to synchronize their respective caches, or is it local by design? If local by design, that wouldn't exactly cover…

Oops, I forgot to include the most important link[0].

> If local by design, that wouldn't exactly cover the use case of "Redis in front of an army of $other_language workers", correct? And I guess it's accepted as costing slightly more cache misses, but with the benefit of more decentralization / node independence / resiliency, right?

Yes and yes.

Erlang/Elixir don't strive to make distributed caches. I wrote apps that have been scaled to 5 separate servers and each server has their own local cache (running inside the Erlang BEAM VM). Takes a little more time to warm the caches up on restarts but it has never been an issue so far.

The ETS tables ownership is trivial to hand out to another process if the owner dies (look for the "heir" option in ETS docs) but libraries like Cachex (linked at the bottom) and Ane (linked in my previous comment) wrap the ownership worries away from you.

What's left for you is just an uber-performant cache.

Do take a look at Cachex. It's hassle-free and just works.

[0] https://github.com/whitfin/cachex

Re: How to Fix Slow Code in Ruby

#86
post #84

Earlier quoted context omitted.

I can do the same with Elixir's Phoenix, with exactly 3 lines in a production console like in your example. What you describe is not exclusive to Rails.

Yeah, maybe I should rewrite one day, but customers wouldn't care much and I am content wit Ruby.

That's fair. Nobody is making you do it. Use what feels right to you.

I simply felt compelled to point out that Rails is not at all as unique as many of its long-time users believe.

Re: How to Fix Slow Code in Ruby

#87

Earlier quoted context omitted.

Thanks! I read in http://erlang.org/doc/man/ets.html that "Each table is created by a process. When the process terminates, the table is automatically destroyed. Every table has access rights set at creation." -> So, caching is local to each worker node/process? Do nodes/processes communicate between them to synchronize their respective caches, or is it local by design? If local by design, that wouldn't exactly cover…

Oops, I forgot to include the most important link[0]. > If local by design, that wouldn't exactly cover the use case of "Redis in front of an army of $other_language workers", correct? And I guess it's accepted as costing slightly more cache misses, but with the benefit of more decentralization / node independence / resiliency, right? Yes and yes. Erlang/Elixir don't strive to make distributed caches. I wrote apps th…

Thanks again for the extra info and links :)

> "Erlang/Elixir don't strive to make distributed caches. I've wrote apps that have been scaled to 5 separate servers and each server has their own cache. Takes a little more to warm the caches up on restarts but it has never been an issue so far."

Yeah that's what I figured, makes total sense. Got into exactly the same thinking designing a circuit breaker at work. Better to let individual nodes do their own circuit breaking rather than introduce state, the complexity savings largely exceed the small cost of a little extra time needed to circuit break.

Re: How to Fix Slow Code in Ruby

#88
post #15

What's current state on GraalVM as it relates to running production Ruby code? From what I understand, it's the fastest VM out there at the moment for Ruby. And yes, it's from Oracle but they have GPL'd the code [2] [1] https://www.graalvm.org [2] https://www.graalvm.org/docs/faq/ Edit: looks like TruffleRuby is built onto of Graal. https://github.com/oracle/truffleruby

It's mirroring small amounts of production traffic at Shopify.

Re: How to Fix Slow Code in Ruby

#89

Earlier quoted context omitted.

And yet Phoenix hasn't stolen a significant amount of market share from Rails. > There are a number of web frameworks that perform much better than Rails If you're measuring hardware loads and busting out your stopwatch to measure response times, then sure. Bottom line is, there are plenty of good reasons to choose Rails over Phoenix. If you want to label choosing a well-backed framework with an incredibly mature eco…

> And yet Phoenix hasn't stolen a significant amount of market share from Rails. I thought we all learned popularity does not correlate with quality. It correlates pretty closely with corporate inertia and perceived lower risk of developer churn though. Businesses love tech stack for which there are bigger pools of programmers. Says nothing of the quality of the stacks. > If you're measuring hardware loads and bustin…

> For 3.5 years with Elixir this is the first time I hear that this is a problem. Any data to back this up?

My point is that it doesn't come with Phoenix out of the box (unless something has changed?), and that difference in philosophy is the core of what I'm getting at. With rails new I'm getting an end-to-end test suite and chrome driver installation for free.

Phoenix also has no plans of implementing something similar to ActiveStorage do they? How about ActiveJob now that elixir developers have rediscovered how great a queueing system is with the adoption of Oban?. Will Phoenix ever make a move to include something like ActionText?

They won't even consider adding basic things for developer productivity like undoing a generator.

https://github.com/phoenixframework/phoenix/issues/2607 https://github.com/phoenixframework/phoenix/issues/1597

The Rails team is much quicker and happier to extract something out of the companies supporting it (Basecamp, GitHub, Shopify, etc) and include it directly in the framework, whereas the Phoenix team seems much less willing to take a "batteries" included approach.

There's a balance of making something easy to use and making something "technically superior". The Rails team seems to care much more deeply about what the framework feels like to use, and the Phoenix team cares more about building something that is "dogmatically perfect" (which you're correlating with quality).

What's easier for someone to learn?

rails g model article title body user:references

mix phx.gen.html Content Article articles title body

I understand what they're going for here, but I'm not sure the tradeoff is worth it. If Phoenix wants to increase their adoption, then I think they need to accept that things like this matter.

Re: How to Fix Slow Code in Ruby

#90
post #27

Earlier quoted context omitted.

Last time I tried to run Truffle on the company test suite it spent an hour processing 1/8th of the suite. Then it was killed by the OOM killer. On a 32 GB machine. To be fair there was only one or two errors during that, so compatibility is at least getting there. Meanwhile Ruby 2.6.5 chugs along and finishes the whole suite in 8 minutes. Never hitting any unreasonable amounts of memory.

Yes TruffleRuby struggles to run test code because it works against the optimisations we add to make production code fast. For example when you add more profiling to better optimise the hot code it makes the cold code slower, and tests are almost all cold code. Also our C extension emulation layer used to be extraordinarily slow while we made it work correctly, and it's still rather slow. It's a challenge but we're w…

What speed gains do you get? Does it ever seen like a lot more trouble to try to optimize ruby instead of just writing slow parts in a different language?
Post reply on HN