aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/threading_and_code_execution.md
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2017-11-17 00:21:13 +1030
committerMatthew Draper <matthew@trebex.net>2017-11-17 00:21:13 +1030
commitb5cd7d5b511361062f67ebad45ab38393970fc47 (patch)
tree6eb51c6321f59ac44f9686a4cdadc42aeceb19fd /guides/source/threading_and_code_execution.md
parent4f7ab453e4afbf2302291d320967f38735de2e31 (diff)
downloadrails-b5cd7d5b511361062f67ebad45ab38393970fc47.tar.gz
rails-b5cd7d5b511361062f67ebad45ab38393970fc47.tar.bz2
rails-b5cd7d5b511361062f67ebad45ab38393970fc47.zip
Add more detail on how the framework is actually configured by default
Diffstat (limited to 'guides/source/threading_and_code_execution.md')
-rw-r--r--guides/source/threading_and_code_execution.md72
1 files changed, 67 insertions, 5 deletions
diff --git a/guides/source/threading_and_code_execution.md b/guides/source/threading_and_code_execution.md
index b35b2fdee4..1c7d61a29c 100644
--- a/guides/source/threading_and_code_execution.md
+++ b/guides/source/threading_and_code_execution.md
@@ -54,6 +54,12 @@ In a default Rails application, the Executor callbacks are used to:
* return acquired Active Record connections to the pool
* constrain internal cache lifetimes
+Prior to Rails 5.0, some of these were handled by separate Rack middleware
+classes (such as `ActiveRecord::ConnectionAdapters::ConnectionManagement`), or
+directly wrapping code with methods like
+`ActiveRecord::Base.connection_pool.with_connection do`. The Executor replaces
+these with a single more abstract interface.
+
### Wrapping application code
If you're writing a library or component that will invoke application code, you
@@ -89,7 +95,16 @@ thread, `wrap` is a no-op.
If it's impractical to physically wrap the application code in a block (for
example, the Rack API makes this problematic), you can also use the `run!` /
-`complete!` pair.
+`complete!` pair:
+
+```ruby
+Thread.new do
+ execution_context = Rails.application.executor.run!
+ # your code here
+ensure
+ execution_context.complete! if execution_context
+end
+```
### Concurrency
@@ -112,6 +127,12 @@ Rails.application.reloader.wrap do
end
```
+The Reloader is only suitable where a long-running framework-level process
+repeatedly calls into application code, such as for a web server or job queue.
+Rails automatically wraps web requests and Active Job workers, so you'll rarely
+need to invoke the Reloader for yourself. Always consider whether the Executor
+is a better fit for your use case.
+
### Callbacks
Before entering the wrapped block, the Reloader will check whether the running
@@ -141,14 +162,55 @@ and `after_class_unload` callbacks.
Only long-running "top level" processes should invoke the Reloader, because if
it determines a reload is needed, it will block until all other threads have
-completed and left any Executor block.
+completed any Executor invocations.
If this were to occur in a "child" thread, with a waiting parent inside the
Executor, it would cause an unavoidable deadlock: the reload must occur before
the child thread is executed, but it cannot be safely performed while the parent
-thread is mid-execution.
-
-Child threads should use the Executor instead.
+thread is mid-execution. Child threads should use the Executor instead.
+
+Framework Behavior
+------------------
+
+The Rails framework components use these tools to manage their own concurrency
+needs too.
+
+`ActionDispatch::Executor` and `ActionDispatch::Reloader` are Rack middlewares
+that wraps the request with a supplied Executor or Reloader, respectively. They
+are automatically included in the default application stack. The Reloader will
+ensure any arriving HTTP request is served with a freshly-loaded copy of the
+application if any code changes have occurred.
+
+Active Job also wraps its job executions with the Reloader, loading the latest
+code to execute each job as it comes off the queue.
+
+Action Cable uses the Executor instead: because a Cable connection is linked to
+a specific instance of a class, it's not possible to reload for every arriving
+websocket message. Only the message handler is wrapped, though; a long-running
+Cable connection does not prevent a reload that's triggered by a new incoming
+request or job. Instead, Action Cable uses the Reloader's `before_class_unload`
+callback to disconnect all its connections. When the client automatically
+reconnects, it will be speaking to the new version of the code.
+
+The above are the entry points to the framework, so they are responsible for
+ensuring their respective threads are protected, and deciding whether a reload
+is necessary. Other components only need to use the Executor when they spawn
+additional threads.
+
+### Configuration
+
+The Reloader only checks for file changes when `cache_classes` is false and
+`reload_classes_only_on_change` is true (which is the default in the
+`development` environment).
+
+When `cache_classes` is true (in `production`, by default), the Reloader is only
+a pass-through to the Executor.
+
+The Executor always has important work to do, like database connection
+management. When `cache_classes` and `eager_load` are both true (`production`),
+no autoloading or class reloading will occur, so it does not need the Load
+Interlock. If either of those are false (`development`), then the Executor will
+use the Load Interlock to ensure constants are only loaded when it is safe.
Load Interlock
--------------