5 ms·
I’ve found it interesting to see several different concurrency frameworks for Ruby over the years with mixed success (e.g. EventMachine), along with the multipl
by jbotdev 4y ago
I’ve found it interesting to see several different concurrency frameworks for Ruby over the years with mixed success (e.g. EventMachine), along with the multiple built-in primitive (fibers, threads, forks). The patterns I’ve seen most used in the real world essentially involve distributing the work elsewhere, rather than getting concurrency right locally, for example:
* Run a bunch of background workers (Sidekiq/Resque), and queue up a job for each item you want processed in parallel.
* Provide relatively granular HTTP APIs, and have your JS frontend call them in parallel with AJAX, instead of having the server handle concurrency.
I think this is just the nature of Ruby being widely used for web apps where performance isn’t a big concern. That said, I’d love to see Ractor catch on, since it’s a pattern built into the language everyone could standardize on.
- pfarrell 4y agoI thought the Global Interpreter Lock limited how effective concurrency could ever get in a single Ruby runtime (excepting maybe JRuby). My understanding was that you could ever only get green threads. My info may be dated, I haven’t kept up with developments in Ruby 3. Is the GIL not going to be a thing any more?
- byroot 4y ago> My understanding was that you could ever only get green threads. Ruby 1.8 had green threads, 1.9 onwards has native threads, but with a GVL. And 3.2 may have N:M threads. > Is the GIL not going to be a thing any more? On paper it's already done in 3.0. What used to be the GVL is now one lock per Ractor. Every object belongs to a Ractor, and the Ractor lock need to be acquired to access the objects, except for mutable objects that can be shared across ractors. So in practice if you are not spawning any more ractor than the main one the VM execute your program in, then it's technically still a global lock, but you now have (limited) ways to go around it.
- jbotdev 4y agoYou can still use native threads, as long as you’re not expecting them to improve performance with multi-core CPU usage. The main purpose is typically to perform several I/O operations in parallel (e.g. multiple external API calls). That’s why Ruby applications often rely on “worker pools”, which are essentially forks, to scale performance across cores. Edit: as the sibling comment mentions, that’s part of what Ractor is trying to solve.
- byroot 4y ago> distributing the work elsewhere, rather than getting concurrency right locally It's entirely orthogonal concerns. Job queues are for "fire and forget" tasks. Using Sidekiq and co offer tons of advantage over queuing this in a local thread pool / coroutine or similar. It provides efficient backoff retries, work distribution, durability, backpressure, and a tons of other goodies. If you were to just queue these things in-process, you may queue more work than your process can handle which may hurt latency. It also make deploy of web application awkward. If you know jobs are externally queue, you can stop sending traffic to that process and once all in-flight requests are completed you know it's safe to stop the process. If that process may contains queued work, well who knows when it's safe to restart it. In process concurrency (or parallelism) is useful for other things (e.g. parallel queries to a service), and that's were you use threads / fibers / ractors / async.
- rubyfan 4y agoI don’t think Sidekiq/Resque are trying to get concurrency right. I’ve run a number of async processing workloads using a variety of Ruby out of the box and framework features - as well as Java, Scala concurrency features/frameworks. Any of the low level stuff really isn’t designed for the kind of higher functionality you get from something like a Sidekiq or Resque. Honestly for implementing any workload that needs consistency, durability or execution guarantees you need something more complicated than what any language’s out of the box concurrency model gives you. Even the frameworks like EventMachine or Akka help with programming abstraction and the concurrent execution but they don’t really solve high level job/queue features. They are just different tools that solve different problems.