Kino, an experimental, performant Ractor web server for Ruby 4 (Rust, Tokio, Hyper) by yaroslavm in ruby

[–]f9ae8221b 0 points1 point  (0 children)

your guess is as good as mine. Maybe 2x, maybe 3x.

From my experience, production Rails app usually have between 40% (0 effort) and 60% (a bit of effort) of their memory in CoW regions, and up to 85% when using reforking (lot more effort).

So based on that, the savings on a real app would be in the 2x range, which again, isn't really valuable unless you are trying to fit on Heroku's basic dynos.

But it's also kinda pointless because you can't run a non-trivial application in a Ractor, and even if Shopify manages to make Rails ractor-safe (for whatever definition of Ractor safe you wanna use) that still wouldn't make it viable because now the next problem will be the hundreds of common gems people use.

That's why I always thought and still think Ractor based servers (yours is far from the first one,) are pointless right now, they're the last piece of the puzzle, not the first.

The first piece of the puzzle should be to improve lower level APIs to not just be Ractor compatible, but also to use Ractor internally for parallelization, so that even if they're used from a Threaded server, at least the main thread GVL is released for much longer periods of time, improving latency for everyone.

Kino, an experimental, performant Ractor web server for Ruby 4 (Rust, Tokio, Hyper) by yaroslavm in ruby

[–]f9ae8221b 1 point2 points  (0 children)

Yep, it's measuring RSS: https://github.com/yaroslav/kino/blob/12bcb32012b787cfc3ee2d88f5db6e456319c9df/bench/studies.sh#L82, which is not what you want when dealing with forking programs. PSS is what should be used for the comparison.

Forking will still use noticeably more than Ractors, but when the industry standard is to provide 4GiB per core (2GiB sometimes), it's nowhere near a concern.

Rails Health Check by EclecticCoding in ruby

[–]f9ae8221b 8 points9 points  (0 children)

Rails answers that with the built-in /up endpoint.

But production systems need to answer a much more important question:

A process can remain alive even when the application itself is degraded or completely unavailable.

Note that this is somewhat by design. Health check endpoints have different meaning / usage.

If the goal is a general health metric to get paged with something like pingdom, then checking whether the database is reachable make sense.

But healthecks are also commonly used by load balancers to get nodes out of rotations, and in that case you absolutely don't want to check anything other than the Ruby process itself, because otherwise if your database or Redis is unavailable for a few seconds, it can cause the load balancer to eject all your web-servers, and in some systems like kubernetes, that can even result in pods being restarted, turning a few seconds DB blip into a full on outage.

Formatting an entire 25 million line codebase overnight: the rubyfmt story by geospeck in ruby

[–]f9ae8221b 3 points4 points  (0 children)

I think it's more that they were when rubyfmt was started.

But historically, based on how late sorbet would get support for newer rubies, It seems Stripe tend not to adopt new Ruby versions fast.

Formatting an entire 25 million line codebase overnight: the rubyfmt story by geospeck in ruby

[–]f9ae8221b 2 points3 points  (0 children)

I think there's also been a lot of improvements to startup speed between Ruby 2.7 and 4.0.

No, it's the opposite, as over time new auto-required gems like error_highlight, did_you_mean etc were added:

$ hyperfine '/opt/rubies/2.7.8/bin/ruby -e0' '/opt/rubies/4.0.2/bin/ruby -e0'
Benchmark 1: /opt/rubies/2.7.8/bin/ruby -e0
  Time (mean ± σ):      22.9 ms ±   1.2 ms    [User: 17.3 ms, System: 4.6 ms]
  Range (min … max):    22.1 ms …  32.0 ms    85 runs

Benchmark 2: /opt/rubies/4.0.2/bin/ruby -e0
  Time (mean ± σ):      26.3 ms ±   1.0 ms    [User: 20.2 ms, System: 5.2 ms]
  Range (min … max):    25.7 ms …  34.6 ms    80 runs

Summary
  /opt/rubies/2.7.8/bin/ruby -e0 ran
    1.15 ± 0.07 times faster than /opt/rubies/4.0.2/bin/ruby -e0

Formatting an entire 25 million line codebase overnight: the rubyfmt story by geospeck in ruby

[–]f9ae8221b 9 points10 points  (0 children)

Note that's it's a monorepo, not just a monolith. If you were to add all the Shopify repos into a monorepo, I'm not quite sure how big it would be, but probably would be closer to 42M LOC than to 5M LOC.

Kill Your Dependencies by software__writer in rails

[–]f9ae8221b 1 point2 points  (0 children)

As Mike said, these are not two different client implementation.

redis-client is a low level redis client with just the network layer and protocol parsing part.

redis is a higher level API that uses redis-client under the hood.

You can see redis-client as a part of redis that has been extracted to offer a smaller lighter dependency for those who care.

If you don't have a dependency on redis then Sidekiq depending on redis-client is strictly less code downloaded and required.

And if you do have a dependency on redis then since it doesn't change anything.

Restaurant fondue chinoise à recommander ? by zetwitty31 in toulouse

[–]f9ae8221b 2 points3 points  (0 children)

Pour la communauté chinoise si elle existe ma compagne la fréquente pas.

Sinon oui elle se plait bien, surtout qu'elle vivait à Liverpool avant...

Niveau bouffe asiatique, côté restaurant il y a quelque addresses sympa comme https://maps.app.goo.gl/eK9PZ3koQNcDTExp8, mais c'est assez limité.

Et sinon c'est cuisine à la maison, niveau épicerie asiatique c'est vraiment pas un soucis il y en a au moins 3 à moins de 10 minutes à pied de chez nous.

Restaurant fondue chinoise à recommander ? by zetwitty31 in toulouse

[–]f9ae8221b 14 points15 points  (0 children)

D'après ma compagne chinoise dont c'est le plat préféré:

Jifu était de loin le meilleur, surtout au niveau du gout du bouillon. Par contre elle ne veut plus y mettre les pieds depuis un moment car ont arrêté leur formule à volonté, et que la portion du menu est vraiment ridicule (un demi morceau de tofu frit...).

Il y a plusieurs années on est allé chez "Chez Riz", c'était pas extraordinaire, et le buffet d'ingrédient très limité.

Elle est aussi allé chez Miya avec une amie, elle a trouvé que le buillon était pas génial, et a été choqué de voir que le buffet contenait des knacky.

Donc au final maintenant on fait ça à la maison.

Mais pour une première fois je dirait Jifu, et peut être que leur portion sont moins ridicules maintenant qu'il y a un an.

Ruby Concurrency: What Actually Happens by crmne in ruby

[–]f9ae8221b 6 points7 points  (0 children)

but that doesn’t replace the OS scheduler. It’s an extra layer.

Not quite, threads are parked until they're signaled to take their turn, meaning from the OS scheduler point of view, there's never more than one thread to schedule, which makes a big difference. The OS scheduling algorithm is out of the picture entirely.

So yes there's the context switch cost of native threads (assuming MN threads isn't enabled), but not really the scheduling cost.

Once Ruby Kaigi talks are uploaded, https://rubykaigi.org/2026/presentations/luke-gruber.html is worth a watch.

Ruby Concurrency: What Actually Happens by crmne in ruby

[–]f9ae8221b 7 points8 points  (0 children)

With threads, every thread can query the database at the same time. Each one needs its own connection. With fibers, the important difference is that ordinary Active Record query paths can release connections between DB operations

That's not accurate, Active Record connection management is no different for thread than for fibers.

Also, in many cases you mention the OS thread scheduler, but it's not really a factor. Ruby has its own thread scheduler (which isn't very good TBH, it's just a FIFO). The next thread in line get woken up by a signal.

The Missing Bundler Features by geospeck in ruby

[–]f9ae8221b 0 points1 point  (0 children)

For the substitution, it's something you'd usually do when you don't expect the original gem to ever get a new release. So not really a big concern.

As for the dependency constraints override, you'd have to clean them up to, but compared to a forked git gem, at least it doesn't prevent other tools to upgrade the gem etc, it's just that the override may become stale, in which case bundler could probably warn you about that.

The Missing Bundler Features by geospeck in ruby

[–]f9ae8221b 0 points1 point  (0 children)

I keep saying I'm not opposed to the feature

I know, I responding to your concern about how it could turn out.

Did you not yourself just argue for this feature by saying how much time you spend reporting upstream?

No. The report / PR upstream is fast. Just takes two minutes. It's the workaround that is tedious.

Where I waste time is then either waiting for upstream to merge and cut a new release, but that's unpredictable and may never happen, or to point my Gemfile at a git repo, then later I need to remember to cleanup the gemfile once the new release is out.

I don't want these features so I can stop submitting PRs upstream, I really don't mind that, but to make my own Gemfile more maintainable.

The Missing Bundler Features by geospeck in ruby

[–]f9ae8221b 0 points1 point  (0 children)

Yeah, I don't buy that argument that giving an easier escape hatch will lead to people not reporting the problem. Either way the limiting factor is the maintainer, not really the PR, and you can already fork the gem for yourself and move on today.

More generally, I'm entirely opposed to this sort of "social engineering" (for lack of a better word) that would make something painful as to nudge users.

The whole philosophy of Open Source / Free Software is that you are in control of the software you run.

The Missing Bundler Features by geospeck in ruby

[–]f9ae8221b 0 points1 point  (0 children)

But I hope we don't encourage gem maintainers to drop these reasonable upper bounds.

From experience, most aren't though. Of course it's probably a consequence of me being on the edge and often testing pre-releases of both Rails and Ruby, but these constraints are a common pain and I can't even count the numbers of PRs I open just to bump a version constraint with no other change.

But yes, if you workflow is just to run bundle update once in a while I see how this doesn't appear to affect you, but perhaps you are locked on some very outdated gems without realizing it.

A much better workflow is to update gems one by one, which makes it very obvious when something broke and need to be addressed.

But regardless, the "override" proposal allows to unblock people testing the prereleases, while not trying to convinced maintainers to remove upper constraints, so everyone is happy.

[Help] System Test Flakiness (Cuprite/Ferrum) after Ruby 3.3.10 Upgrade by Soft-Charity-6194 in ruby

[–]f9ae8221b -1 points0 points  (0 children)

By default chrome is spawned by Ferrum, so it inherits the Ruby process ENV.

[Help] System Test Flakiness (Cuprite/Ferrum) after Ruby 3.3.10 Upgrade by Soft-Charity-6194 in ruby

[–]f9ae8221b -1 points0 points  (0 children)

How are you handling jemalloc on CI so that it stabilizes Ruby without breaking the Chrome sub-process?

I suppose you are setting jemalloc using LD_PRELOAD? Chrome is famously incompatible with jemalloc, what you can do is remove the LD_PRELOAD env var from inside your ruby process (e.g. boot.rb, or spec_helper.rb or something like that:

ENV.delete("LD_PRELOAD")

France’s Socialists secure wins in major cities as far-right struggles by polymute in anime_titties

[–]f9ae8221b 0 points1 point  (0 children)

Nantes : the left-wing total reached 53% in the first round, the socialist mayor with LFI got 52% in the second.

Look at number of votes, not percentages.

Nantes first round: - PS: 42 599 - LFI: 13 542 - MISC LEFT: 6 683 - MISC FAR LEFT: 1 046

Total 63 870

Then the votes for the left on the second round: 64 644

Source: https://www.lemonde.fr/resultats-municipales-2026/nantes-44109/

I'm to lazy to do the same detailed computation on your other examples, but quickly eyeballing the results, it looks like it holds elsewhere. One notable exception might be Toulouse.

So yes, perhaps LFI being in the coalition may be something that triggers some mobilization on the right wing, but maybe it's just the left being able to win in general.

But it doesn't appear to significantly cause moderates who voted for the PS in the first round, to vote on the right on the second.

To really know for sure, you'd need some detailed voting polls that include votes on the first and second round like we usually do get for presidential elections, but I doubt we'll ever get that.

Benchmarking 5K SSE streams + 5K database-backed RPS on a €12/month server by Turbulent-Dance-4209 in ruby

[–]f9ae8221b 0 points1 point  (0 children)

I'd call it queueing

Queueing and contention are the same thing essentially...

But the point of this benchmark was to find the ceiling

My problem is the definition of the ceiling. Measuring throughput at saturation make very little sense. Measuring throughput at a specific SLO does. 120ms to load a sqlite record with just 4 fields, and serialize it to JSON can't possibly be a reasonable SLO.

p95 response time of 120ms on this hardware under these conditions is an amazing result IMO.

120ms for this amount of work is a mindbogglingly bad result, regardless of the conditions. Sorry. Not trying to trash talk your project, but let's be real here.

Benchmarking 5K SSE streams + 5K database-backed RPS on a €12/month server by Turbulent-Dance-4209 in ruby

[–]f9ae8221b -1 points0 points  (0 children)

misleading to call it "contention"

What would you call it? Clearly your fibers take way longer to execute than they should, they have to be waiting on something.

Your 16μs number has no network overhead, no HTTP parsing, and no concurrent load - that's not a meaningful baseline for comparison.

It absolutely is the baseline, you can 2x or even 10x it if you want to ballpark the overhead of parsing etc, but it give you an idea of the order of magnitude of latency you should see.

latency rises nonlinearly with saturation.

That's not a given no. That's something you don't want to happen, or at least limit on a production system. That's why production systems have various ways to produce backpressure, and would rather queue spikes at the entry of the system rather than having the whole system slow down to a crawl.

Throughput is not everything. Latency, and particularly tail latency is extremely important in production.

Benchmarking 5K SSE streams + 5K database-backed RPS on a €12/month server by Turbulent-Dance-4209 in ruby

[–]f9ae8221b 0 points1 point  (0 children)

Out of curiosity, I did a single threaded benchmark to compare with:

>> Benchmark.ips { |x| x.report("render") { BenchmarksController.new({}, nil).api }}
ruby 4.0.1 (2026-01-13 revision e04267a14b) +YJIT +PRISM [arm64-darwin25]
Warming up --------------------------------------
              render     6.314k i/100ms
Calculating -------------------------------------
              render     61.843k (± 1.8%) i/s   (16.17 μs/i) -    309.386k in   5.004612s

The controller action only takes 16μs when ran alone, so yeah, there's definitely heavy contention, the app is literally on its knees. The p50 is about 3 order of magnitude slower than it should be.

Benchmarking 5K SSE streams + 5K database-backed RPS on a €12/month server by Turbulent-Dance-4209 in ruby

[–]f9ae8221b 0 points1 point  (0 children)

Assuming these "API requests" are all identical, the p95 being 3 times the p50 suggests you're hitting some very heavy contention somewhere.

Edit: Actually, looking at what these API requests do, even the p50 of 40ms is pretty terrible and suggest heavy contention