-
Jean Boussier authored
Sometimes a controller or a job has to perform multiple independent queries, e.g.: ``` def index @posts = Post.published @categories = Category.active end ``` Since these two queries are totally independent, ideally you could execute them in parallel, so that assuming that each take 50ms, the total query time would be 50ms rather than 100ms. A very naive way to do this is to simply call `Relation#to_a` in a background thread, the problem is that most Rails applications, and even Rails itself rely on thread local state (`PerThreadRegistry`, `CurrentAttributes`, etc). So executing such a high level interface from another thread is likely to lead to many context loss problems or even thread safety issues. What we can do instead, is to schedule a much lower level operation (`Adapter#select_all`) in a thread pool, and return a future/promise. This way we kepp most of the risky code on the main thread, but perform the slow IO in background, with very little chance of executing some code that rely on state stored in thread local storage. Also since most users are on MRI, only the IO can really be parallelized, so scheduling more code to be executed in background wouldn't lead to better performance.
Jean Boussier authoredSometimes a controller or a job has to perform multiple independent queries, e.g.: ``` def index @posts = Post.published @categories = Category.active end ``` Since these two queries are totally independent, ideally you could execute them in parallel, so that assuming that each take 50ms, the total query time would be 50ms rather than 100ms. A very naive way to do this is to simply call `Relation#to_a` in a background thread, the problem is that most Rails applications, and even Rails itself rely on thread local state (`PerThreadRegistry`, `CurrentAttributes`, etc). So executing such a high level interface from another thread is likely to lead to many context loss problems or even thread safety issues. What we can do instead, is to schedule a much lower level operation (`Adapter#select_all`) in a thread pool, and return a future/promise. This way we kepp most of the risky code on the main thread, but perform the slow IO in background, with very little chance of executing some code that rely on state stored in thread local storage. Also since most users are on MRI, only the IO can really be parallelized, so scheduling more code to be executed in background wouldn't lead to better performance.
Loading