Optionally run a wsgi profiler when asked

This is the code used to run placement with the wsgi
profiling described in a blog post [1]. It has proven
useful enough that we may wish to include it in the
released code. It is added in a way that is off by
default and makes no changes to requirements.

Brief docs are included in testing.rst. They are
brief because it's assumed that someone who wants to
do this already mostly knows what they want to do and
merely needs the specifics on how to do it in this
environment.

[1] https://anticdent.org/profiling-wsgi-apps.html

Change-Id: I342512732b94bc19bd711684ba3ec9480cc51f81
This commit is contained in:
Chris Dent 2019-03-14 10:40:16 +00:00 committed by Eric Fried
parent 7db53444fb
commit 88c6ad9cb4
2 changed files with 42 additions and 0 deletions

View File

@ -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/

View File

@ -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,