Skip to content

Commit 844353e

Browse files
PiDelportcamilonova
authored andcommitted
Consider query params when detecting duplicate queries (#995)
* (Factor out key function for duplicate queries) * Consider the query params when detecting duplicate queries * SQL query recording: Track the raw_params too The duplicate query detection needs this, because `params` is not always JSON-serialisable. * (Rename variable to avoid shadowing) * SQLPanel: Track duplicated queries both with and without params * SQL panel template: Include counts of duplicates with params too * Convert raw_params lists to hashable tuples * Wording change: duplicate -> similar, for queries with differing params * Wording: "duplicate with params" -> duplicate, for identical queries * SQL panel: Add explanatory tooltip text to similar / duplicate query counts
1 parent 72d2d43 commit 844353e

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

debug_toolbar/panels/sql/panel.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,16 @@ def disable_instrumentation(self):
141141
def generate_stats(self, request, response):
142142
colors = contrasting_color_generator()
143143
trace_colors = defaultdict(lambda: next(colors))
144+
query_similar = defaultdict(lambda: defaultdict(int))
144145
query_duplicates = defaultdict(lambda: defaultdict(int))
146+
147+
# The keys used to determine similar and duplicate queries.
148+
def similar_key(query):
149+
return query['raw_sql']
150+
151+
def duplicate_key(query):
152+
return (query['raw_sql'], tuple(query['raw_params']))
153+
145154
if self._queries:
146155
width_ratio_tally = 0
147156
factor = int(256.0 / (len(self._databases) * 2.5))
@@ -164,7 +173,8 @@ def generate_stats(self, request, response):
164173
trans_id = None
165174
i = 0
166175
for alias, query in self._queries:
167-
query_duplicates[alias][query["raw_sql"]] += 1
176+
query_similar[alias][similar_key(query)] += 1
177+
query_duplicates[alias][duplicate_key(query)] += 1
168178

169179
trans_id = query.get('trans_id')
170180
last_trans_id = trans_ids.get(alias)
@@ -209,10 +219,18 @@ def generate_stats(self, request, response):
209219
if trans_id:
210220
self._queries[(i - 1)][1]['ends_trans'] = True
211221

212-
# Queries are duplicates only if there's as least 2 of them.
222+
# Queries are similar / duplicates only if there's as least 2 of them.
213223
# Also, to hide queries, we need to give all the duplicate groups an id
214224
query_colors = contrasting_color_generator()
215-
query_duplicates = {
225+
query_similar_colors = {
226+
alias: {
227+
query: (similar_count, next(query_colors))
228+
for query, similar_count in queries.items()
229+
if similar_count >= 2
230+
}
231+
for alias, queries in query_similar.items()
232+
}
233+
query_duplicates_colors = {
216234
alias: {
217235
query: (duplicate_count, next(query_colors))
218236
for query, duplicate_count in queries.items()
@@ -223,15 +241,23 @@ def generate_stats(self, request, response):
223241

224242
for alias, query in self._queries:
225243
try:
226-
duplicates_count, color = query_duplicates[alias][query["raw_sql"]]
227-
query["duplicate_count"] = duplicates_count
228-
query["duplicate_color"] = color
244+
(query["similar_count"], query["similar_color"]) = (
245+
query_similar_colors[alias][similar_key(query)]
246+
)
247+
(query["duplicate_count"], query["duplicate_color"]) = (
248+
query_duplicates_colors[alias][duplicate_key(query)]
249+
)
229250
except KeyError:
230251
pass
231252

232253
for alias, alias_info in self._databases.items():
233254
try:
234-
alias_info["duplicate_count"] = sum(e[0] for e in query_duplicates[alias].values())
255+
alias_info["similar_count"] = sum(
256+
e[0] for e in query_similar_colors[alias].values()
257+
)
258+
alias_info["duplicate_count"] = sum(
259+
e[0] for e in query_duplicates_colors[alias].values()
260+
)
235261
except KeyError:
236262
pass
237263

debug_toolbar/panels/sql/tracking.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ def _record(self, method, sql, params):
134134
'duration': duration,
135135
'raw_sql': sql,
136136
'params': _params,
137+
'raw_params': params,
137138
'stacktrace': stacktrace,
138139
'start_time': start_time,
139140
'stop_time': stop_time,

debug_toolbar/templates/debug_toolbar/panels/sql.html

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@
55
<li>
66
<strong class="djdt-label"><span data-background-color="rgb({{ info.rgb_color|join:", " }})" class="djdt-color">&#160;</span> {{ alias }}</strong>
77
<span class="djdt-info">{{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %}
8-
{% if info.duplicate_count %}
9-
{% blocktrans with dupes=info.duplicate_count %}including {{ dupes }} duplicates{% endblocktrans %}
8+
{% if info.similar_count %}
9+
{% blocktrans with count=info.similar_count trimmed %}
10+
including <abbr title="Similar queries are queries with the same SQL, but potentially different parameters.">{{ count }} similar</abbr>
11+
{% endblocktrans %}
12+
{% if info.duplicate_count %}
13+
{% blocktrans with dupes=info.duplicate_count trimmed %}
14+
and <abbr title="Duplicate queries are identical to each other: they execute exactly the same SQL and parameters.">{{ dupes }} duplicates</abbr>
15+
{% endblocktrans %}
16+
{% endif %}
1017
{% endif %})</span>
1118
</li>
1219
{% endfor %}
@@ -35,6 +42,12 @@
3542
<div class="djDebugSqlWrap">
3643
<div class="djDebugSql">{{ query.sql|safe }}</div>
3744
</div>
45+
{% if query.similar_count %}
46+
<strong>
47+
<span data-background-color="{{ query.similar_color }}">&#160;</span>
48+
{% blocktrans with count=query.similar_count %}{{ count }} similar queries.{% endblocktrans %}
49+
</strong>
50+
{% endif %}
3851
{% if query.duplicate_count %}
3952
<strong>
4053
<span data-background-color="{{ query.duplicate_color }}">&#160;</span>

0 commit comments

Comments
 (0)