Code concurrency is a default in any Rails code. A threaded web server will simultaneously serve many HTTP requests, and each will hold its controller instance. Threaded active job adapters and action cable channels also handle multiple requests simultaneously. Even when the global process space is shared, work for each instance is managed separately. For scenarios where the shared space is not altered, this process will run smoothly.
Let us see in detail how this is managed in our Rails applications.
- Executor: Executors separate the framework and application codes by wrapping the application code.
The wrapping of code was more complex before Rails 5.0. To protect a code, either separate Rack middleware classes were used or direct wrapping was used. With recent updates in Rails, the executor handles the wrapping in a single step that is easier to understand.
Call the executor to wrap the application code as you invoke it.
One attractive feature of the executor is its reentrancy.
Two callbacks of the executor are 'to run' and 'to complete.' This enables wrapping code in parts when it is not possible to wrap the code as blocks.
The current thread is moved to the 'running' mode, temporarily blocking the thread. So, the thread will not be accessible by any other request that tries to do so.
- Reloader: The Reloader functions similarly to the Executor. The code that is to be protected is wrapped before another request is hit. This is used in scenarios where application code is invoked multiple times by any long-running process. Most often, in Rails, the web requests and Active Jobs are by default wrapped. So the Reloader is rarely used.
A Reloader is employed when there is a need to reload the application. When the requested condition asks for reloading, the Reloader delays the application reload until it is secure. And for scenarios where application reloads are mandated, the Reloader waits until the current block is executed and allows the reload. Thus, the code is protected from errors.
to_run' and '
to_complete' callbacks are used by the Reloader also.
A class unload is involved in the Reloader process. Here, all of the auto-loaded classes are removed and set ready to be further loaded. The application reload is to happen only before or after the class unload, and so, the two additional callbacks of Reloader are '
before_class_unload' and '
Executor and Reloaders are used by the Rails framework components as well.
ActionDispatch::Reloaderare included in the default application stack. Whenever there is a code change, the Reloader serves a fresh copy of the application for an HTTP request. Active Job feature also utilizes Reloaders, whereas Action Cable uses Executor. Action Cable also uses the before_class_unload of Reloader to disconnect all the connections.
As discussed, code concurrency is handled by default with the threaded active jobs and action cables features of Rails codes. With recent Rails updates, Executors and Reloaders are also added to improve the code concurrency handling.
Also, check out 7 Ruby Gems to keep in your toolbox.