This is an idea we've been entertaining with Rafael for a while.
A pattern that often emerge in large app is that the controller need to perform several DB queries, and then use that to render the view. Sometimes the queries are interdependent, so they have to happen one after the other, but sometimes they are totally independent and could be parallelized.
e.g.
class BlogController
def index
@categories = Category.all # to render the sidebar or something
@posts = Post.order(published_at: :desc)
end
end
In this example both Post
and Category
could be queried in parallel. Of course on paper you could just do query from threads:
class BlogController
def index
categories_future = Thread.new { Category.all.to_s }
@posts = Post.order(published_at: :desc).to_a
@categories = categories_future.value
end
end
But then you are executing a lot of user code in a background thread which breaks lots of expectations. You what's stored in Thread.current
, so things like Marginalia breaks, CurrentAttributes
are lost, pretty sure ActiveSupport::Instrumentation
also breaks.
So long story short, this won't work on most realistic scenarios, and for it to work users would need to be extremely careful to be really thread safe. Not just thread safe across request like today, but thread safe inside each request.
So instead I implemented a quick and dirty proof of concept that only perform the query in a thread pool, the rest of the work (instatiating models, etc) is still done on the main thread. That takes care of the vast majority of the per thread/fiber context problems. Some things AS::Instrumentaion
would still need to be ironed out.
The script above showcase how it works, you have to explicitly call .defer
on a Relation
to schedule the query in the background. Then wehn you do use the relation, either the query is completed and it just uses the results, or it is being executed and it waits for it to complete, or the thread pool was busy and it simply execute it in the foreground.
It's far from perfect, making it production quality would require a bunch more work, but at this stage it seems totally doable.