Files
nova/nova/monkey_patch.py
Balazs Gibizer 35207ee8b5 Default native threading for sch, api and metadata
This patch switches the default concurrency mode to native threading
for the services that gained native threading support in Flamingo:
nova-scheduler, nova-api, and nova-metadata.

The OS_NOVA_DISABLE_EVENTLET_PATCHING env variable still can be used to
explicitly switch the concurrency mode to eventlet by

  OS_NOVA_DISABLE_EVENTLET_PATCHING=false

We also ensure that the cover, docs, py3xx and functional tox targets
are still running with eventlet while py312-threading kept running
with native threading.

Change-Id: I86c7f31f19ca3345218171f0abfa8ddd4f8fc7ea
Signed-off-by: Balazs Gibizer <gibi@redhat.com>
2025-11-06 19:42:24 +01:00

151 lines
5.8 KiB
Python

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
# Copyright 2019 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Enable eventlet monkey patching."""
import os
MONKEY_PATCHED = False
def is_patched():
return MONKEY_PATCHED
def _monkey_patch():
if is_patched():
return False
# NOTE(mdbooth): Anything imported here will not be monkey patched. It is
# important to take care not to import anything here which requires monkey
# patching.
# NOTE(artom) eventlet processes environment variables at import-time.
# as such any eventlet configuration should happen here if needed.
import eventlet
import sys
# Note any modules with known monkey-patching issues which have been
# imported before monkey patching.
# urllib3: https://bugs.launchpad.net/nova/+bug/1808951
# oslo_context.context: https://bugs.launchpad.net/nova/+bug/1773102
problems = (set(['urllib3', 'oslo_context.context']) &
set(sys.modules.keys()))
eventlet.monkey_patch()
# NOTE(mdbooth): Log here instead of earlier to avoid loading oslo logging
# before monkey patching.
# NOTE(mdbooth): Ideally we would raise an exception here, as this is
# likely to cause problems when executing nova code. However, some non-nova
# tools load nova only to extract metadata and do not execute it. Two
# examples are oslopolicy-policy-generator and sphinx, both of which can
# fail if we assert here. It is not ideal that these utilities are monkey
# patching at all, but we should not break them.
# TODO(mdbooth): If there is any way to reliably determine if we are being
# loaded in that kind of context without breaking existing callers, we
# should do it and bypass monkey patching here entirely.
if problems:
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
LOG.warning("Modules with known eventlet monkey patching issues were "
"imported prior to eventlet monkey patching: %s. This "
"warning can usually be ignored if the caller is only "
"importing and not executing nova code.",
', '.join(problems))
return True
def patch(backend='eventlet'):
"""Apply eventlet monkey patching according to environment.
:param backend: Defines the default backend if not explicitly set via
the environment. If 'eventlet', then monkey patch if environment
variable is not defined. If 'threading', then do not monkey patch if
environment variable is not defined. Any other value results in a
ValueError. If the environment variable is defined this parameter
is ignored.
"""
if backend not in ('eventlet', 'threading'):
raise ValueError(
"the backend can only be 'eventlet' or 'threading'")
env = os.environ.get('OS_NOVA_DISABLE_EVENTLET_PATCHING', '').lower()
if env == '':
should_patch = (backend == 'eventlet')
elif env in ('1', 'true', 'yes'):
should_patch = False
else:
should_patch = True
if should_patch:
if _monkey_patch():
global MONKEY_PATCHED
MONKEY_PATCHED = True
import oslo_service.backend as service
service.init_backend(service.BackendType.EVENTLET)
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
LOG.info("Service is starting with Eventlet based service backend")
else:
# We asked not to monkey patch so we will run in native threading mode
import oslo_service.backend as service
# NOTE(gibi): This will raise if the backend is already initialized
# with Eventlet
service.init_backend(service.BackendType.THREADING)
# NOTE(gibi): We were asked not to monkey patch. Let's enforce it by
# removing the possibility to monkey_patch accidentally
poison_eventlet()
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
LOG.warning(
"Service is starting with native threading. This is currently "
"experimental. Do not use it in production without first "
"testing it in pre-production.")
def _poison(*args, **kwargs):
raise RuntimeError(
"The service is started with native threading via "
"OS_NOVA_DISABLE_EVENTLET_PATCHING set to '%s', but then the "
"service tried to call eventlet.monkey_patch(). This is a bug."
% os.environ.get('OS_NOVA_DISABLE_EVENTLET_PATCHING', ''))
def poison_eventlet():
import eventlet
eventlet.monkey_patch = _poison
eventlet.patcher.monkey_patch = _poison
# We want to have this but cannot have this yet as we still have common
# code that imports eventlet like nova.utils.tpool
#
# class PoisonEventletImport:
# def find_spec(self, fullname, path, target=None):
# if fullname.startswith('eventlet'):
# raise ImportError(
# "The service started in threading mode so it should "
# "not import eventlet")
# import sys
# sys.meta_path.insert(0, PoisonEventletImport())