Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into permissions-system-update
Browse files Browse the repository at this point in the history
  • Loading branch information
dancoates committed Jul 8, 2024
2 parents fbc2897 + fd2c933 commit 56054fa
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ web/src/__generated__

# pulumi config files
Pulumi*.yaml

# performance profile files
profiles
45 changes: 41 additions & 4 deletions api/server.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import os
import time
import traceback
Expand All @@ -12,7 +13,12 @@

from api import routes
from api.graphql.schema import MetamistGraphQLRouter # type: ignore
from api.settings import PROFILE_REQUESTS, SKIP_DATABASE_CONNECTION, SM_ENVIRONMENT
from api.settings import (
PROFILE_REQUESTS,
PROFILE_REQUESTS_OUTPUT,
SKIP_DATABASE_CONNECTION,
SM_ENVIRONMENT,
)
from api.utils.exceptions import determine_code_from_error
from api.utils.openapi import get_openapi_schema_func
from db.python.connect import SMConnections
Expand Down Expand Up @@ -49,10 +55,42 @@ async def app_lifespan(_: FastAPI):


if PROFILE_REQUESTS:
from pyinstrument import Profiler
from pyinstrument.renderers.speedscope import SpeedscopeRenderer

from fastapi_profiler.profiler import PyInstrumentProfilerMiddleware
@app.middleware('http')
async def profile_request(request: Request, call_next):
"""optional profiling for http requests"""
profiler = Profiler(async_mode='enabled')
profiler.start()
resp = await call_next(request)
profiler.stop()

if 'text' in PROFILE_REQUESTS_OUTPUT:
text_output = profiler.output_text()
print(text_output)

timestamp = (
datetime.datetime.now().replace(microsecond=0).isoformat().replace(':', '-')
)

if 'json' in PROFILE_REQUESTS_OUTPUT:
os.makedirs('profiles', exist_ok=True)
json = profiler.output(renderer=SpeedscopeRenderer())

with open(f'profiles/{timestamp}.json', 'w') as file:
file.write(json)
file.close()

if 'html' in PROFILE_REQUESTS_OUTPUT:
os.makedirs('profiles', exist_ok=True)
html = profiler.output_html()
with open(f'profiles/{timestamp}.html', 'w') as file:
file.write(html)
file.close()

return resp

app.add_middleware(PyInstrumentProfilerMiddleware) # type: ignore

if SM_ENVIRONMENT == 'local':
app.add_middleware(
Expand Down Expand Up @@ -185,6 +223,5 @@ async def exception_handler(request: Request, e: Exception):
'api.server:app',
host='0.0.0.0',
port=int(os.getenv('PORT', '8000')),
# debug=True,
reload=True,
)
1 change: 1 addition & 0 deletions api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
SM_ENVIRONMENT = os.getenv('SM_ENVIRONMENT', 'production').lower()
SKIP_DATABASE_CONNECTION = bool(os.getenv('SM_SKIP_DATABASE_CONNECTION'))
PROFILE_REQUESTS = os.getenv('SM_PROFILE_REQUESTS', 'false').lower() in TRUTH_SET
PROFILE_REQUESTS_OUTPUT = os.getenv('SM_PROFILE_REQUESTS_OUTPUT', 'text').lower()
IGNORE_GCP_CREDENTIALS_ERROR = os.getenv('SM_IGNORE_GCP_CREDENTIALS_ERROR') in TRUTH_SET
MEMBERS_CACHE_LOCATION = os.getenv('SM_MEMBERS_CACHE_LOCATION')

Expand Down
9 changes: 9 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,12 @@ The deploy github action builds the container, and is deployed.
Additionally you can access metamist through the identity-aware proxy (IAP),
which handles the authentication through OAuth, allowing you to access the
front-end.


## Performance Profiling

If you are working on performance issues it can be handy to generate a report that shows which bits of code are taking most of the time. The api server has pyinstrument profiling support that can be turned on by setting the environment variable `SM_PROFILE_REQUESTS` to `true`.

There are a few different options for outputting profiles which can be specified in the `SM_PROFILE_REQUESTS_OUTPUT` environment variable. The possible values are `text` which will print the profiling results to stdout, `html` which will generate an interactive pyinstrument report, or `json` which will generate a json profiling report which can be dropped into [speedscope](https://www.speedscope.app/) to explore the profile.

You can output multiple report types by specifying the types in a list like: `export SM_PROFILE_REQUESTS_OUTPUT=json,text,html`

0 comments on commit 56054fa

Please sign in to comment.