HNHacker News
TopNewBestAskShowJobs

brunosutic

1,230 karma · joined December 9, 2011

I work on: https://railsbilling.com Personal website: https://brunosutic.com

Get in touch: hacker.news AT brunosutic.com

submissionscomments
brunosutic··on Async Ruby
Red-black tree, a well-known algorithm also uses color references

https://en.wikipedia.org/wiki/Red–black_tree

brunosutic··on Async Ruby
Thank you for sharing your perspective in a non-conflicting way Yoric.

It seems you have more experience than me with async-related work. I'll keep an eye on this kind of scenario you brought up.

brunosutic··on Async Ruby
Wrap the example in a `Sync` block to get deterministic results. Now you don't have to worry if the code gets nested in an existing event loop.

    Sync do
      results = []
      Async do
        sleep 10
        results << "Hello"
      end
      puts results
    end
brunosutic··on Async Ruby
Thank you for the clarification. You are right, these types of race conditions are possible with Async Ruby.

I find these races relatively easy to spot: yes, global state CAN change when you yield back to the reactor.

IMO thread race conditions are much, much worse. Global state can change AT ANY POINT, because thread scheduler preemptively switches threads. Here's an example:

    global.a = 0

    thread {
      global.a = 1
      print(global.a) // a is 1 or 2
    }

    thread {
      global.a = 2
      print(global.a) // a is 1 or 2
    }

    print(global.a) // a is 0, 1 or 2
brunosutic··on Async Ruby
> Does it come down to fundamental language differences?

No, I don't think there's anything fundamentally different.

Ruby 3.0 implements a "fiber scheduler" feature that enables "colorless Async". Fiber scheduler is an obscure Ruby feature, but the end result is brilliant. It was also a huge amount of work.

Side note: Fiber scheduler was implemented by the same guy who created Async Ruby - Samuel Williams. This guy is the mastermind (and master-coder) behind this project.

brunosutic··on Async Ruby
That's right - Async Ruby uses fibers (stackful coroutines) as a concurrency primitive. There's a lot to say about this, but the end result is that we get to write simple synchronous code and it can run asynchronously.
brunosutic··on Async Ruby
I just ran your example and I'm getting `puts results` line to output "Hello", just as the program intends. I'm not sure why you're getting a different result.

In any case, I'm assuring you: getting the results "out of tasks" is trivially easy.

> Surely any real program will have all the work done inside a single top-level event loop.

I don't get this. Can you please explain more what you have in mind and I'll try to help clarify things.

brunosutic··on Async Ruby
Async has async-websocket gem. I don't have experience with it so can't tell how it compares to ActionCable.

https://github.com/socketry/async-websocket

brunosutic··on Async Ruby
> Which means that you can just write a whole normal Ruby program, that just uses Task::Async.current.async(...) > All without having to pass the current task down the call stack everywhere.

Yes, you can also use `Async { work }` instead of `Async::Task.current.async { work }`.

brunosutic··on Async Ruby
Polyphony monkey-patches Ruby core methods... this has always prevented me from experimenting more with it.

Source link: https://digital-fabric.github.io/polyphony/faq/#why-does-pol...

> Polyphony “patches” some Ruby core and stdlib APIs, providing behavioraly compatible fiber-aware implementations

brunosutic··on Async Ruby
> I regret that this is generally false. Once you have async, you have race conditions and reentrancy issues

Can you get more specific please?

My experience is 100% from Ruby where I've worked heavily with threads in the past, and with Async Ruby for the past 18 months.

From what I can tell, threads require a great deal of locks (mutexes) and are frustratingly hard to get right - because of the language-level race conditions.

Async Ruby has been refreshingly easy to write. I have yet to encounter an example of a race condition.

If it helps to bridge the language gap here: from what I know Async Ruby is similar to Go's goroutine model.

brunosutic··on Async Ruby
Yes, this is possible!

In fact this is a perfect use case for Falcon + Async.

brunosutic··on Async Ruby
> Task counts themselves are also a pretty useless metric. Sure, you can have may fibers doing nothing.

Sorry if I wasn't explicit. I was talking about tasks/fibers performing actual work, like handling HTTP connections.

It's practically possible for Async Ruby programs to do work with hundreds of thousands Async Tasks (concurrent fibers).

Some users have worked with millions of Async tasks, but I'm not sure if it was practical work, or proof of concept.

> You can easily have tens of thousands of threads on Linux.

Thank you for the correction about threads on Linux. I'm not sure if you're talking about threads in general or threads in Ruby?

I've only lightly tested increasing the number of threads in Ruby to about a thousand, and my test code was very slow.

I think that has to do with thread switching overhead. Threads have a relatively high switching overhead, so I don't think it's advisable to run more than 100 threads - in Ruby at least.

brunosutic··on Async Ruby
> is it hard to get the results back out

It's trivially easy to get the results, here's a quick example:

    require "async"
    require "open-uri"

    results = []

    Async do |task|
      task.async do
        results << URI.open("https://httpbin.org/delay/1.6")
      end

      task.async do
        results << URI.open("https://httpbin.org/delay/1.6")
      end
    end
brunosutic··on Async Ruby
> What does Async (Fibers underneath) do differently than normal Threads?

There's a lot to say on this topic.

- Threads implement "preemptive scheduling". A scheduler switches control from one thread to another every 10ms or so. A thread running the code may be ready for the switch or not. The ensuing race conditions are nasty.

- Async + Fibers implement "cooperative scheduling". The currently running fiber (voluntarily) yields control to another fiber when it's ready. The result is there are no race conditions.

There's so much to say about this, I'll blog about this in the future.

brunosutic··on Async Ruby
I think you're generally on the right track.

Small correction: fibers (blocking or non-blocking) never used threads in any way. Fibers are "stackful coroutines", another concurrency primitive.

They're pretty hard to understand, and unfortunately I can't find a good explanation of "Ruby Fibers" that I can link to.

brunosutic··on Async Ruby
> the gem changed ownership

I think the gem did change ownership at some point. The first couple gem releases from 2013 were yanked.

brunosutic··on Async Ruby
Ruby's Global Interpreter Lock (GIL) is applicable, and not "sidestepped" with Async.
brunosutic··on Async Ruby
Yes, async is using fibers as a concurrency primitive.

Async gem is starting fibers automatically. Fiber pausing and resuming is handled by event reactor (also called "event loop") from 'nio4r' gem.

io_uring support with asynchronous File IO is also on the way.

brunosutic··on Async Ruby
> This gem is almost a decade old

Hm, the first commit to the gem was in 2017. The gem was not "advertised" so that the interfaces can be polished and done right.

Async gem is hugely improved with Ruby 3.0 release (from December 2020) when Ruby language added "fiber scheduler" feature just to integrate better with Async gem.

brunosutic··on Async Ruby
- Async Ruby is much more performant than threads. There are less context switches, enabled by the event reactor. The performance benefits are visible in simple scenarios like making a thousand HTTP requests.

- Async is more scalable, can handle millions concurrent tasks (like HTTP connections). There can only be a couple thousand threads at the same time.

- Threads are really hard to work with - race conditions everywhere. Async doesn't have this.

brunosutic··on Async Ruby
Async Ruby is colorless!

It's obvious from the examples provided in the post. For example, you can use the method `URI.open` both synchronously and asynchronously.

It's something I didn't want to mention in the article because it's a relatively advanced async concept, but yea - Async Ruby is already colorless and it's great.

brunosutic··on Async Ruby
Yea, it's a big new Ruby feature.

From what I was able to tell (I followed the discussion around this) enabling Rails to work with Async is not huge amount of effort. One guy was working on this, but he got distracted with other stuff.

It's just a matter of time someone steps up and gets this to work.

brunosutic··on Ask HN: What is the best-designed software you've used?
- Unix command line.

- Vim's commands and keybingings interface (not the UI).

brunosutic··on Show HN: Didyougogo – An Altavista slayer
Is there a way to be notified of product updates?
brunosutic··on Ask HN: What are your favorite terminal programs?
Here are some of my favorites that I use daily:

- tmux

- mutt - email client

- wyrd + remind - calendar

- ledger - double-entry accounting tool

- weechat + weeslack plugin - fully featured, super fast slack from the CLI

brunosutic··on Ask HN: What is your “mission” in work and life?
I read "So Good They Can't Ignore You" last year and was having the same question: "what is my mission in life and how to get/realize/understand that?"

My conclusion was: I don't know and don't have a specific mission. The closest I have to a mission is a set of positive (may probably not conscious) principles in life that I'm sticking to. Examples:

- always be improving - learn, learn, learn - work hard - presented with negative or ambiguous choices, choose the best available one (don't screw up everything because of frustration)

I'd also be curious to hear opinions on these:

- Is it "necessary" to have a specific higher mission? Can't I just stick to the set of positive principles? - Do I have to actively be looking for a mission? Or I wait and let it happen?

brunosutic··on N26 raises $160M from Tencent and Allianz
I'm Croatia based I've been using revolut for a year now. My only complaint with all these "new banks" is: none of them supports Apple Pay.

Maybe now that N26 is going to become a real bank supporting Apple Pay will become a possibility?

brunosutic··on Announcing gRPC Support in Nginx
This is great! TL;DR: instead of building JSON or GraphQL API now you can easily expose your gRPC service to the outside world!

We use gRPC in my company. We're happy but some things were not easy or straightforward to implement. With this update nginx makes load balancing and authentication easier to implement.

brunosutic··on Cora – Kitty Hawk's prototype air taxi [video]
A lot of negative comments here. I must say I'm impressed and I applaud these people for building an innovative working thing and taking it to production. No pilot - mind blown.

And sure, this might not be the solution to humanity's overall mobility, but heck I can see it being useful in many ways.

← PreviousPage 2 of 3Next →