Solid Queue 1.6.0 now supports fiber workers
Posted by earcar 11 hours ago
Comments
Comment by symfoniq 4 hours ago
That said, these days you’ll pry the BEAM from my cold, dead hands. It’s hard to go back to any other concurrency story.
Comment by ajx1001 3 hours ago
Comment by thibaut_barrere 3 hours ago
Comment by ksec 1 hour ago
Compared to Ruby. Elixir land doesn't have something similar to Rails, and phoenix is not it.
Comment by QGQBGdeZREunxLe 1 hour ago
Comment by bingemaker 32 minutes ago
Comment by robertfall 2 hours ago
What could I stop caring about?
Comment by QGQBGdeZREunxLe 39 minutes ago
Comment by mrinterweb 1 hour ago
Comment by ramon156 8 hours ago
Comment by vdombr 6 hours ago
In fact, I got the following results in HTTP benchmark tests:
Go:
* Latency under stable load: p95 0.25–5.32 ms, p99 2.20–9.92 ms * Memory: 23–31 MB RSS across HTTP scenarios
Ruby:
* Latency under stable load: p95 1.03–6.45 ms, p99 2.32–8.30 ms * Memory: 84–295 MB RSS, depending on the scenario
Fibers can also handle WebSockets better because WebSocket workloads involve more I/O waiting.
A typical Falcon setup uses N workers, one thread per worker, and many fibers. Since the fibers are cooperatively scheduled within a single thread, this avoids much of the context-switching overhead associated with OS threads. Multiple workers can still run in parallel across CPU cores.
Comment by cogman10 5 hours ago
The OS has a rather opaque view on what happens in a thread. It doesn't know how the memory is used or what memory is efficient. The OS can block a thread when it runs into IO, but it doesn't know or care about what other threads in an application it can or should activate.
Fibers bring the threading into the application layer. While the application can't choose which and when a thread runs, it can make choices about which fibers to run. Further, the application knows intimate details about things like the stack of a given thread. When it goes to park a fiber, it knows just how much memory should be saved off so the fiber can resume and it doesn't have to save off all the memory allocated to a thread's stack. Further, because so many programs are stack based an application can pretty smartly save and reuse segments of the stack which are common amongst fibers. So, for example, if you spin off 1000 fibers at one location in code, the stack for those 1000 fibers will be identical right up until the fiber starts executing.
The main drawback of fibers is they can't implement things like fair scheduling. Applications have few ways to park a currently running fiber to let another one run if, for example, the app wants to make some progress on all the fibers alive. The app has to wait for the fiber to hit some sort of IO point in the code or insert explicit park checks (The JVM actually does this for GC purposes. It creates "safepoints" which application threads make a quick check to see if the JVM wants to start a GC). The OS has more power here, it can simply interrupt the thread and start running something else for a given quanta.
Comment by jerf 4 hours ago
The other problem with this sort of benchmark, which is a mistake I also commonly see made by Node developers, is that the Ruby HTTP stack has significant native code in it, like: https://github.com/puma/puma/tree/main/ext/puma_http11 This is a good and proper thing that brings benefits to all involved; it's not like it's "cheating" or anything, it's a real performance benefit. But it does mean when you're benchmarking a simple HTTP server, you're benchmarking Ruby qua Ruby a lot less than you think you are, and so the relevance of such benchmarks to codebases that have actual Ruby in them will be less.
[1]: https://programming-language-benchmarks.vercel.app/python-vs...
Comment by vdombr 3 hours ago
Comment by symfoniq 4 hours ago
Comment by vdombr 3 hours ago
Comment by mrinterweb 1 hour ago
Comment by asa400 2 hours ago
Comment by adrian_b 7 hours ago
This feature is obviously intended for them and it might enhance their productivity.
Nonetheless, no program that uses a great number of any variant of the "light-weight threads" can ever be as efficient as a thread pool that is dimensioned to have the same number of threads as the number of hardware threads of a SMT CPU, or a slightly greater number of threads than the number of hardware threads of a non-SMT CPU.
For maximum performance, the use of a correctly-sized thread pool remains the best solution, but writing an efficient program that uses it can be significantly more difficult, because good methods of communication and synchronization must be implemented, while the run-time library of a language with "light-weight threads"/"fibers"/etc. already takes care of such problems so the programmer does not need to think about them.
Comment by dosshell 4 hours ago
Fiber is a datastructure where the execution context is saved. Eg. registers (including instruction pointer) and stack etc. That is a fiber: data.
You normally use a threadpool, core pinned, to execute these fibers.
Since you jump in userspace, you more or less only have to pay for cache misses.
There are many upsides of designing a program using fibers. The major downside i see is that you can not blindly trust mutex and semaphores any longer - since the fiber can change execution thread while yielding/waiting for condition.
Comment by jherdman 2 hours ago
Comment by the_sleaze_ 2 hours ago
Comment by Lio 9 hours ago
Is it possible to either have multiple ractors dispatching jobs with fibres or to set up multiple queues with different strategies?
E.g. one for IO bound and one for CPU bound?
With Sidekiq I’ve had luck having workers running on Truffleruby but generally don’t use it for my main rails apps.
Comment by pqdbr 4 hours ago
From his article:
One backend, two modes
Fiber mode isn’t universally better. CPU-bound jobs get nothing from it, and blocking libraries or C extensions that do not cooperate with Ruby’s fiber scheduler stall the reactor. And that’s fine – you don’t have to pick one.
As Trevor Turk pointed out in the PR discussion, that’s the whole point: separately configured worker pools. Here’s what Chat with Work actually runs in production:
workers: - queues: [ chat ] fibers: 10 processes: 2 polling_interval: 0.1 - queues: [ turbo ] fibers: 10 processes: 1 polling_interval: 0.05 - queues: [ notifications, default, maintenance ] fibers: 5 processes: 1 polling_interval: 0.2 - queues: [ cpu ] threads: 1 processes: 1
Comment by swe_dima 7 hours ago
Comment by pqdbr 4 hours ago
The difference is staggering when you compare to threaded mode: it requires 1,320 database connections to run the same benchmark that the fiber mode runs with 60.
https://paolino.me/solid-queue-doesnt-need-a-thread-per-job/
Comment by alex_smart 1 hour ago
There is absolutely no logical reason why database pool sizing could be a reason for preferring fibers over threads.
Comment by resonious 6 hours ago
Comment by looperhacks 7 hours ago
Comment by swe_dima 5 hours ago
Comment by achernik 2 hours ago
Comment by nicechianti 1 hour ago