diff --git a/doc/source/contributor/testing.rst b/doc/source/contributor/testing.rst index 25f0d8211..b8d5badb8 100644 --- a/doc/source/contributor/testing.rst +++ b/doc/source/contributor/testing.rst @@ -115,11 +115,36 @@ example in devstack. See `gabbi-run`_ to get started. If you don't want to go to the trouble of using devstack, but do want a live server see :doc:`quick-dev`. +Profiling +--------- + +If you wish to profile requests to the placement service, to get an idea of +which methods are consuming the most CPU or are being used repeatedly, it is +possible to enable a ProfilerMiddleware_ to output per-request python profiling +dumps. The environment (:doc:`quick-dev` is a good place to start) in which +the service is running will need to have Werkzeug_ added. + +* If the service is already running, stop it. +* Install Werkzeug. +* Set an environment variable, ``OS_WSGI_PROFILER``, to a directory where + profile results will be written. +* Make sure the directory exists. +* Start the service, ensuring the environment variable is passed to it. +* Make an HTTP request that exercises the code you wish to profile. + +The profiling results will be in the directory named by ``OS_WSGI_PROFILER``. +There are many ways to analyze the files. See `Profiling WSGI Apps`_ for an +example. + + .. _bug: https://github.com/cdent/gabbi/issues .. _fixtures: http://gabbi.readthedocs.io/en/latest/fixtures.html .. _gabbi: https://gabbi.readthedocs.io/ .. _gabbi-run: http://gabbi.readthedocs.io/en/latest/runner.html .. _JSONPath: http://goessner.net/articles/JsonPath/ +.. _ProfilerMiddleware: https://werkzeug.palletsprojects.com/en/master/middleware/profiler/ +.. _Profiling WSGI Apps: https://anticdent.org/profiling-wsgi-apps.html .. _syntax: https://gabbi.readthedocs.io/en/latest/format.html .. _telemetry: http://specs.openstack.org/openstack/telemetry-specs/specs/kilo/declarative-http-tests.html +.. _Werkzeug: https://palletsprojects.com/p/werkzeug/ .. _wsgi-intercept: http://wsgi-intercept.readthedocs.io/ diff --git a/placement/deploy.py b/placement/deploy.py index a9f3f012b..7706f6c5e 100644 --- a/placement/deploy.py +++ b/placement/deploy.py @@ -11,6 +11,8 @@ # under the License. """Deployment handling for Placmenent API.""" +import os + from microversion_parse import middleware as mp_middleware import oslo_middleware from oslo_middleware import cors @@ -29,6 +31,14 @@ from placement import resource_class_cache as rc_cache from placement import util +PROFILER_OUTPUT = os.environ.get('OS_WSGI_PROFILER') +if PROFILER_OUTPUT: + # If werkzeug is not available this raises ImportError and the + # process will not continue. This is intentional: we do not want + # to make a permanent dependency on werkzeug. + from werkzeug.contrib import profiler + + def deploy(conf): """Assemble the middleware pipeline leading to the placement app.""" if conf.api.auth_strategy == 'noauth2': @@ -57,6 +67,13 @@ def deploy(conf): request_log = requestlog.RequestLog application = handler.PlacementHandler(config=conf) + + # If PROFILER_OUTPUT is set, generate per request profile reports + # to the directory named therein. + if PROFILER_OUTPUT: + application = profiler.ProfilerMiddleware( + application, profile_dir=PROFILER_OUTPUT) + # configure microversion middleware in the old school way application = microversion_middleware( application, microversion.SERVICE_TYPE, microversion.VERSIONS,