Use Rails executor instead of reloader to fix Nats::Timeout errors #149
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
When using Rails, this changes the NATS reloader to use the Rails
executor
instead of thereloader
.This continues to meet the objectives originally outlined in #120.
Rails' reloader is basically a combination of the executor plus hot reloading. Since hot reloading is typically only enabled in development, this should have no impact on production.
In development, when the code has changed, the block wrapped by the Rails reloader is blocked from execution until all other reloader blocks complete. Nested reloaders no-op when inside the same thread. However, NATS connections run in a separate thread.
Most of Rails already runs inside a reloader block, including ActiveJob and ActionController. When a NATS subscription block (in my case, a
nats_client.request
) is initiated from inside ActiveJob, etc, and the code changes, the subscription's processing block is blocked from execution until the reloader block from ActiveJob, etc finishes. Unfortunately, this means the original NATS request (again, inside ActiveJob, etc) will raise aNATS::IO::Timeout
every time, since the replies are indeed blocked from execution by the reloader.With this PR, only the Rails executor is used to wrap the subscription processing blocks. This continues to isolate database connections (the original concern in #120). And, since the subscription request itself is usually defined in hot-reloadable code, it will be reloaded once that outer block finishes.
If NATS subscriptions are going to be processed in isolated code (outside of Rails' own use of its reloader), and that code wants to enable hot reloading of the subscription block, then the app/daemon running those subscriptions can wrap the subscription in its own Rails reloader block. This is the reason Sidekiq (which was the inspiration for the original use of the reloader) uses reloader where it does.
For example, in #125, the sample
bin/nats-listener
could enable hot-reloading by wrappingsubscribe{ ... }
in a reloader. It might also need to register an on-reload handler and, as part of the finalloop
, know how to unregister/reregister the original subscription. (Whether to wrapsubscribe
itself, as described here, or just the interior block will depend on the nature of the app/code at hand.)This change is consistent with the Rails Executor guide which says that libraries calling application code should generally use an executor (section 2.2) and usually only long running process should use the reloader (section 3).