Browse files

Make gae_mini_profiler thread safe

  • Loading branch information...
1 parent d65adbf commit 3be8882b5c3b2c193c376fe30794a6dba011d855 @cdman cdman committed Jun 20, 2012
Showing with 26 additions and 11 deletions.
  1. +25 −10 profiler.py
  2. +1 −1 templatetags.py
View
35 profiler.py
@@ -4,6 +4,8 @@
import os
import cPickle as pickle
import re
+import threading
+import base64
# use json in Python 2.7, fallback to simplejson for Python 2.5
try:
@@ -29,8 +31,24 @@
else:
config = gae_mini_profiler.config.ProfilerConfigProduction
-# request_id is a per-request identifier accessed by a couple other pieces of gae_mini_profiler
-request_id = None
+class RequestStore:
+ def __init__(self):
+ self._threadlocal = threading.local()
+
+ def clear_id(self):
+ if not hasattr(self._threadlocal, 'request_id'): return
+ self._threadlocal.request_id = None
+
+ def generate_id(self):
+ self._threadlocal.request_id = base64.urlsafe_b64encode(os.urandom(5))
+ return self._threadlocal.request_id
+
+ def get_id(self):
+ if not hasattr(self._threadlocal, 'request_id'): return None
+ return self._threadlocal.request_id
+
+requeststore = RequestStore()
+
class SharedStatsHandler(RequestHandler):
@@ -320,22 +338,19 @@ def __init__(self, app):
def __call__(self, environ, start_response):
- global request_id
- request_id = None
-
# Start w/ a non-profiled app at the beginning of each request
self.app = self.app_clean
self.prof = None
self.recorder = None
self.temporary_redirect = False
self.simple_timing = cookies.get_cookie_value("g-m-p-disabled") == "1"
+ requeststore.clear_id()
# Never profile calls to the profiler itself to avoid endless recursion.
if config.should_profile(environ) and not environ.get("PATH_INFO", "").startswith("/gae_mini_profiler/"):
# Set a random ID for this request so we can look up stats later
- import base64
- request_id = base64.urlsafe_b64encode(os.urandom(5))
+ request_id = requeststore.generate_id()
# Send request id in headers so jQuery ajax calls can pick
# up profiles.
@@ -422,7 +437,7 @@ def wrapped_appstats_app(environ, start_response):
# Just in case we're using up memory in the recorder and profiler
self.recorder = None
self.prof = None
- request_id = None
+ requeststore.clear_id()
else:
result = self.app(environ, start_response)
@@ -476,10 +491,10 @@ def headers_with_modified_redirect(environ, headers):
reg = re.compile("mp-r-id=([^&]+)")
# Keep any chain of redirects around
- request_id_chain = request_id
+ request_id_chain = requeststore.get_id()
match = reg.search(environ.get("QUERY_STRING"))
if match:
- request_id_chain = ",".join([match.groups()[0], request_id])
+ request_id_chain = ",".join([match.groups()[0], requeststore.get_id()])
# Remove any pre-existing miniprofiler redirect id
location = header[1]
View
2 templatetags.py
@@ -29,6 +29,6 @@ def profiler_includes_request_id(request_id, show_immediately = False):
@register.simple_tag
def profiler_includes():
- return profiler_includes_request_id(profiler.request_id)
+ return profiler_includes_request_id(profiler.requeststore.get_id())

0 comments on commit 3be8882

Please sign in to comment.