Bug #21173
closedclass loading leads to deadlocks on rails 5
Description
See https://github.com/ruby-concurrency/concurrent-ruby/issues/585 and https://github.com/rails/rails/issues/26847 for more details.
On rails 5, code executing in a run or finalize phase can deadlock the server due to autoloading classes. If you run in production (or really with config.eager_load config.cache_classes true), the problem does not occur. I have only seen it as part of sync() tasks within a controller action, but it likely happens with async() tasks too.
From my tests, wrapping individual steps (i.e. the run phase of an action) in the suggested methods does NOT resolve the issue, such as:
def run Rails.application.executor.wrap do ActiveSupport::Dependencies.interlock.permit_concurrent_loads do #auto load some constants end end end
Wrapping the full call to sync_task() within the controller does fix it, such as within katello's candlepin_proxies_controller:
#api :POST, "/environments/:environment_id/consumers", N_("Register a consumer in environment") def consumer_create Rails.application.executor.wrap do ActiveSupport::Dependencies.interlock.permit_concurrent_loads do content_view_environment = find_content_view_environment host = Katello::Host::SubscriptionFacet.find_or_create_host(content_view_environment.environment.organization, rhsm_params) sync_task(::Actions::Katello::Host::Register, host, rhsm_params, content_view_environment) host.reload update_host_registered_through(host, request.headers) render :json => Resources::Candlepin::Consumer.get(host.subscription_facet.uuid) end end end
to reproduce currently with katello:
1. Install a dev environment for katello and enable rails 5
2. Checkout this commit onto katello: https://github.com/Katello/katello/pull/6967
3. Enable rails 5 support in foreman and bundle install
4. start the rails server and try to register a system with subscription-manager
The server seems to hang during the run phase of the Actions::Katello::Host::Register action, during fact importing. It hangs when loading the FactValue class here: https://github.com/theforeman/foreman/blob/develop/app/services/fact_importer.rb#L66
It is 100% reproducible on a freshly started server.
Updated by Ivan Necas about 7 years ago
From the https://github.com/ruby-concurrency/concurrent-ruby/issues/585#issuecomment-256131537, it looks like the wrap
should wrap the run
or finalize
method, while the permit_concurrent_loads
should be in the request processing thread (perhaps we could wrap https://github.com/theforeman/foreman-tasks/blob/ecb8c5b39f6281c9c29ff3ea677b3d43002bbea2/lib/foreman_tasks.rb#L22-L33 with it)
From what I've read, it should affect only the sync
variant: could you verify that?
Updated by Justin Sherrill about 7 years ago
Ivan Necas wrote:
From the https://github.com/ruby-concurrency/concurrent-ruby/issues/585#issuecomment-256131537, it looks like the
wrap
should wrap therun
orfinalize
method, while thepermit_concurrent_loads
should be in the request processing thread (perhaps we could wrap https://github.com/theforeman/foreman-tasks/blob/ecb8c5b39f6281c9c29ff3ea677b3d43002bbea2/lib/foreman_tasks.rb#L22-L33 with it)
Ah! Yes i wasn't sure where to put that permit_concurrent_loads. I've tested that suggestion and it appears to work fine!
From what I've read, it should affect only the
sync
variant: could you verify that?
Indeed, the async() variant does not fail
Updated by Justin Sherrill about 7 years ago
- Status changed from New to Assigned
- Assignee set to Justin Sherrill
Updated by The Foreman Bot about 7 years ago
- Status changed from Assigned to Ready For Testing
- Pull request https://github.com/theforeman/foreman-tasks/pull/289 added
Updated by Justin Sherrill about 7 years ago
- Status changed from Ready For Testing to Closed
- % Done changed from 0 to 100
Applied in changeset 25f4b482bfc9ac35d26eb33be47ad7ec2e9a8541.
Updated by Ivan Necas about 7 years ago
- Translation missing: en.field_release set to 316