Skip to content

Commit 1e11572

Browse files
committed
implement db cursor wrapper so don't need DEBUG=True
1 parent 2632ad9 commit 1e11572

File tree

3 files changed

+31
-54
lines changed

3 files changed

+31
-54
lines changed

ansible_base/lib/middleware/profiling/README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,13 @@ MIDDLEWARE = [
5050

5151
### Enabling SQL Profiling
5252

53-
**Important:** This middleware relies on Django's `connection.queries` list, which is only populated when `settings.DEBUG` is set to `True`. Therefore, you must have `DEBUG = True` in your Django settings for this middleware to have any effect.
54-
55-
The middleware is controlled by the `ANSIBLE_BASE_SQL_PROFILING` setting. For backwards compatibility, it will also be enabled if the standard Django `SQL_DEBUG` setting is `True`.
53+
The middleware is controlled by the `ANSIBLE_BASE_SQL_PROFILING` setting.
5654

5755
To enable SQL profiling, set the following in your Django settings:
5856

5957
```python
6058
# settings.py
6159
ANSIBLE_BASE_SQL_PROFILING = True
62-
DEBUG = True
6360
```
6461

6562
## `DABProfiler`

ansible_base/lib/middleware/profiling/profile_request.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
import uuid
88
from typing import Optional, Union
99

10-
from django.db import connection
1110
from django.conf import settings
11+
from django.db import connection
1212
from django.utils.translation import gettext_lazy as _
1313

1414
from ansible_base.lib.utils.settings import get_function_from_setting, get_setting
1515

16-
1716
logger = logging.getLogger(__name__)
1817

1918

@@ -85,23 +84,32 @@ def __call__(self, request):
8584
return response
8685

8786

87+
class SQLQueryMetrics:
88+
def __init__(self):
89+
self.query_count = 0
90+
self.query_time = 0.0
91+
92+
def __call__(self, execute, sql, params, many, context):
93+
start_time = time.time()
94+
try:
95+
return execute(sql, params, many, context)
96+
finally:
97+
self.query_count += 1
98+
self.query_time += time.time() - start_time
99+
100+
88101
class SQLProfilingMiddleware:
89102
def __init__(self, get_response):
90103
self.get_response = get_response
91104

92105
def __call__(self, request):
93-
sql_profiling_enabled = get_setting('ANSIBLE_BASE_SQL_PROFILING', get_setting('SQL_DEBUG', False))
94-
if sql_profiling_enabled:
95-
if not settings.DEBUG:
96-
logger.warning("ANSIBLE_BASE_SQL_PROFILING is enabled, but DEBUG is False. No SQL queries will be logged or counted.")
97-
return self.get_response(request)
106+
if not get_setting('ANSIBLE_BASE_SQL_PROFILING', False):
107+
return self.get_response(request)
98108

99-
queries_before = len(connection.queries)
100-
response = self.get_response(request)
101-
q_times = [float(q['time']) for q in connection.queries[queries_before:]]
102-
response['X-API-Query-Count'] = len(q_times)
103-
response['X-API-Query-Time'] = '%0.3fs' % sum(q_times)
104-
else:
109+
metrics = SQLQueryMetrics()
110+
with connection.execute_wrapper(metrics):
105111
response = self.get_response(request)
106112

113+
response['X-API-Query-Count'] = metrics.query_count
114+
response['X-API-Query-Time'] = f'{metrics.query_time:.3f}s'
107115
return response

test_app/tests/lib/middleware/test_profiling_middleware.py

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import uuid
21
import os
32
import tempfile
3+
import uuid
44
from unittest.mock import patch
55

66
from django.http import HttpResponse
@@ -11,22 +11,26 @@
1111
from ansible_base.lib.utils.settings import get_setting
1212
from test_app.models import User
1313

14+
1415
# A simple view for testing middleware
1516
def simple_view(request):
1617
return HttpResponse("OK")
1718

19+
1820
# A view that performs a database query
1921
def db_view(request):
2022
# Create a user with a unique username to guarantee a query.
2123
User.objects.create(username=f"test-{uuid.uuid4()}")
2224
return HttpResponse("OK")
2325

26+
2427
# Define URL patterns for the test
2528
urlpatterns = [
2629
path('test/', simple_view),
2730
path('test-db/', db_view),
2831
]
2932

33+
3034
@override_settings(ROOT_URLCONF=__name__)
3135
class ProfileRequestMiddlewareTest(TestCase):
3236
@override_settings(CLUSTER_HOST_ID='test-node')
@@ -36,7 +40,7 @@ def test_profile_request_middleware_headers(self):
3640
"""
3741
middleware = ProfileRequestMiddleware(simple_view)
3842
response = middleware(self.client.get('/test/').wsgi_request)
39-
43+
4044
# Test X-API-Time
4145
self.assertIn('X-API-Time', response)
4246
self.assertTrue(response['X-API-Time'].endswith('s'))
@@ -74,10 +78,7 @@ def test_profile_request_middleware_cprofile_disabled(self):
7478
self.assertNotIn('X-API-CProfile-File', response)
7579

7680

77-
@override_settings(
78-
ROOT_URLCONF=__name__,
79-
MIDDLEWARE=['ansible_base.lib.middleware.profiling.profile_request.SQLProfilingMiddleware']
80-
)
81+
@override_settings(ROOT_URLCONF=__name__, MIDDLEWARE=['ansible_base.lib.middleware.profiling.profile_request.SQLProfilingMiddleware'])
8182
class SQLProfilingMiddlewareTest(TestCase):
8283
def test_sql_profiling_disabled_by_default(self):
8384
"""
@@ -87,25 +88,10 @@ def test_sql_profiling_disabled_by_default(self):
8788
self.assertNotIn('X-API-Query-Count', response)
8889
self.assertNotIn('X-API-Query-Time', response)
8990

90-
@override_settings(ANSIBLE_BASE_SQL_PROFILING=True, DEBUG=True)
91+
@override_settings(ANSIBLE_BASE_SQL_PROFILING=True)
9192
def test_sql_profiling_enabled_with_new_setting(self):
9293
"""
93-
Test that the SQLProfilingMiddleware adds headers when ANSIBLE_BASE_SQL_PROFILING is True.
94-
"""
95-
response = self.client.get('/test-db/')
96-
self.assertIn('X-API-Query-Count', response)
97-
self.assertGreaterEqual(int(response['X-API-Query-Count']), 1)
98-
self.assertIn('X-API-Query-Time', response)
99-
self.assertTrue(response['X-API-Query-Time'].endswith('s'))
100-
try:
101-
float(response['X-API-Query-Time'][:-1])
102-
except ValueError:
103-
self.fail("X-API-Query-Time value is not a valid float")
104-
105-
@override_settings(SQL_DEBUG=True, DEBUG=True)
106-
def test_sql_profiling_enabled_with_fallback_setting(self):
107-
"""
108-
Test that the SQLProfilingMiddleware adds headers when SQL_DEBUG is True as a fallback.
94+
Test that the SQLProfilingMiddleware adds headers when ANSIBLE_BASE_SQL_PROFILING is True
10995
"""
11096
response = self.client.get('/test-db/')
11197
self.assertIn('X-API-Query-Count', response)
@@ -116,17 +102,3 @@ def test_sql_profiling_enabled_with_fallback_setting(self):
116102
float(response['X-API-Query-Time'][:-1])
117103
except ValueError:
118104
self.fail("X-API-Query-Time value is not a valid float")
119-
120-
@override_settings(ANSIBLE_BASE_SQL_PROFILING=True, DEBUG=False)
121-
def test_sql_profiling_logs_warning_if_debug_is_false(self):
122-
"""
123-
Test that the SQLProfilingMiddleware logs a warning and does not add headers
124-
if profiling is enabled but DEBUG is False.
125-
"""
126-
with self.assertLogs('ansible_base.lib.middleware.profiling.profile_request', level='WARNING') as cm:
127-
response = self.client.get('/test-db/')
128-
self.assertIn("ANSIBLE_BASE_SQL_PROFILING is enabled, but DEBUG is False", cm.output[0])
129-
self.assertNotIn('X-API-Query-Count', response)
130-
self.assertNotIn('X-API-Query-Time', response)
131-
132-

0 commit comments

Comments
 (0)