Itsi Scheduler
Itsi Scheduler is an implementation of a Ruby Fiber Scheduler.
When combined with Itsi Server, you can write endpoints that look just like regular synchronous Ruby code. Behind the scenes, the scheduler will transparently pause and resume fibers to prevent threads from blocking, greatly increasing throughput for I/O-heavy workloads
If you’re purely after a lightweight, yet efficient Ruby scheduler, you can use Itsi Scheduler as a standalone scheduler for any Ruby application.
Just use Fiber.set_scheduler to set an instance Itsi::Scheduler as a scheduler to opt in to this IO weaving behavior
automatically for all blocking IO.
Primer on Fiber Schedulers
Fiber schedulers are a way to automatically manage the execution of non-blocking fibers in Ruby. A scheduler is responsible for the automatic pausing and resumption of Fibers based on whether or not they are awaiting IO operations. Ruby’s Fiber scheduler implementation automatically invokes the current Fiber scheduler (if it exists) for each blocking operation, allowing it to seamlessly drive the execution of huge numbers of simultaneous non-blocking fibers while ensuring the main thread is never blocked on IO.
This behind the scenes magic allows Ruby to provide async IO (just like we find in languages with async/await like Rust, C#, JavaScript) but with the added beauty
that synchronous and asynchronous code is identical! (i.e. Ruby’s functions are colorless)
Getting Started
To install and use Itsi Scheduler follow the below instructions:
1 - Install Itsi Scheduler
Prerequisites
You’ll need at least build-essential and libclang-dev installed to build Itsi on Linux.
E.g.
apt-get install build-essential libclang-devThen use gem to install the Itsi package. This will in turn install both the
itsi-server gem, and the itsi-scheduler gem.
gem install itsi-schedulergem install itsi (to get both Itsi scheduler and server)
Or gem install itsi-server to install just Itsi Server.Mac:
gem install itsi-schedulergem install itsi (to get both Itsi scheduler and server)
Or gem install itsi-server to install just Itsi Server.Windows: Itsi currently doesn’t support native Windows builds, but it runs well on https://learn.microsoft.com/en-us/windows/wsl/install.
Follow the linked instructions to Install a Linux distribution like Ubuntu or Debian and then follow the instructions in the Linux tab.
2 - Use Itsi Scheduler
Great! You now have Itsi Scheduler installed. Now you can run code like this:
require 'itsi/scheduler'
require 'socket'
results = Thread.new do
Fiber.set_scheduler Itsi::Scheduler.new
results = []
Fiber.schedule do
results << Addrinfo.getaddrinfo("www.ruby-lang.org", 80, nil, :STREAM)
end
Fiber.schedule do
results << Addrinfo.getaddrinfo("www.google.com", 80, nil, :STREAM)
end
results
end.value
puts results.map(&:inspect)to run many blocking operations simultaneously all while occupying only a single Ruby thread!
3 (Optional) - Enable Scheduler Refinements
You can opt-in to a tiny set of Ruby refinements provided by the Itsi::Scheduler to make usage even more ergonomic.
By opting in to this refinement (using using Itsi::ScheduleRefinement) you gain access to the top-level #schedule(&block) method, as well
as enumerable methods #schedule_each, and #schedule_map.
require 'net/http'
require "json"
using Itsi::ScheduleRefinement
addr = Resolv.getaddress("httpbin.org")
# Fire-and-forget: 20 HTTP calls in parallel
result = []
20.times.schedule_each do |i|
result << JSON.parse(Net::HTTP.get(addr, "/anything/#{i}"))["url"].split("/").last
end
puts "schedule_each: #{result}"
# => ["6","1","8","3",...]
# Concurrent transform that keeps the original order
squares = (1..20).schedule_map { |n| sleep Random.rand(0.0..0.1); n * n }
puts "schedule_map: #{squares}"
# => [1, 4, 9, 16, … 400]
# Manual orchestration — still one thread
schedule do
a, b = Queue.new, Queue.new
schedule { a << Net::HTTP.get(addr, "/get") }
schedule { b << Net::HTTP.get(addr, "/uuid") }
puts a.pop
puts b.pop
end