Browse files

Deprecate MongoenginePanel and fix MongoDebugPanel to configure jinja…

…_env properly.
  • Loading branch information...
1 parent 13591d3 commit d892cf0a81be5d217469d79c190b174a2d5a8b66 @jorgebastida jorgebastida committed Dec 19, 2011
View
134 flaskext/mongoengine/panels.py
@@ -1,155 +1,32 @@
-import re
+from flask import current_app
-import pymongo
from flaskext.debugtoolbar.panels import DebugPanel
from jinja2 import PackageLoader, ChoiceLoader
-from mongoengine.connection import _get_db
-
import operation_tracker
_ = lambda x: x
class MongoenginePanel(DebugPanel):
- """
- Panel that displays the number of mongodb queries using mongoengine.
-
- * nscanned_vs_nreturned_ratio: Ratio to determine if a query needs index.
- * nscanned_update_limit: Limit to determine if a update query needs index.
- * slow_query_limit: Limit (ms) to determine if a query is slow.
-
- """
+ """ Panel deprecated in favor of MongoDebugPanel """
name = 'Mongoengine'
has_content = True
- nscanned_vs_nreturned_ratio = 1000
- nscanned_update_limit = 1000
- slow_query_limit = 100
-
- def __init__(self, *args, **kwargs):
- """
- We need to patch jinja_env loader to include flaskext.mongoengine
- templates folder.
- """
- super(MongoenginePanel, self).__init__(*args, **kwargs)
- self.jinja_env.loader = ChoiceLoader([self.jinja_env.loader,
- PackageLoader('flaskext.mongoengine', 'templates')])
- self.db = _get_db()
-
- def process_request(self, request):
- """
- This panel use the mongodb Database Profiler [1]
-
- On every request we deactivate, get the biggest ts and reactivate the
- profiler.
-
- [1] http://www.mongodb.org/display/DOCS/Database+Profiler
- """
-
- try:
- self.start_ts = self.db.system.profile.find()\
- .sort("ts", pymongo.DESCENDING)\
- .limit(1)[0].get('ts')
- except IndexError:
- self.start_ts = None
-
- self.db.set_profiling_level(2)
-
- def _get_property(self, key, query):
- try:
- return float(re.search(r'%s\:(\d+)' % key, query).group(1))
- except:
- return None
-
- def _should_optimize(self, query):
- """
- Try to determine if there are any applicable obvious optimizations
- http://www.mongodb.org/display/DOCS/Database+Profiler
-
- index: If nscanned is much higher than nreturned, the database is
- scanning many objects to find the target objects. Consider
- creating an index to improve this.
-
- select members: (reslen) A large number of bytes returned (hundreds
- of kilobytes or more) causes slow performance. Consider passing
- find() a second parameter of the member names you require.
- """
- optimizations = []
-
- if query['operation_type'] == 'query':
- nscanned = self._get_property('nscanned', query['org_info'])
- nreturned = self._get_property('nreturned', query['org_info'])
- if (nreturned and nscanned) and \
- (nscanned > nreturned * self.nscanned_vs_nreturned_ratio):
- optimizations.append("index")
-
- reslen = self._get_property('reslen', query['org_info'])
- if reslen > 100 * 1024:
- optimizations.append("select members")
-
- elif query['operation_type'] == 'update':
- nscanned = self._get_property('nscanned', query['org_info'])
-
- if nscanned and nscanned > self.nscanned_update_limit:
- optimizations.append("index")
-
- if query['millis'] > self.slow_query_limit:
- optimizations.append("slow")
-
- return optimizations
-
- def _process_query(self, query):
- """
- Split the query to recover all interesting information.
- """
- out = {}
- out['millis'] = query.get('millis', 0)
- out['ts'] = query.get('ts')
-
- out['org_info'] = query.get('info')
-
- out['operation_type'] = query.get('op')
- out['collection'] = query.get('ns')
-
- out['query'] = query.get('query')
-
- out['optimizations'] = ', '.join(self._should_optimize(out))
-
- return out
def process_response(self, request, response):
- """
- Get queries from system.profile with ts > self.start_ts
- """
- if self.start_ts:
- query = {'ts': {'$gt': self.start_ts}}
- else:
- query = {}
- self.queries = self.db.system.profile.find(query, timeout=False)
- self.queries = [self._process_query(q) for q in self.queries]
- self.queries_count = len(self.queries)
- self.total_time = sum([q.get('millis') for q in self.queries])
+ pass
def nav_title(self):
return _('Mongoengine')
- def nav_subtitle(self):
- return "%s queries in %sms" % (self.queries_count, self.total_time)
-
def title(self):
return _('Mongoengine Usage')
def url(self):
return ''
def content(self):
-
- context = self.context.copy()
- context.update({
- 'queries': self.queries,
- })
-
- return self.render('panels/mongoengine.html', context)
+ return '<h2>Panel deprecated in favor of MongoDebugPanel'
class MongoDebugPanel(DebugPanel):
@@ -163,6 +40,8 @@ class MongoDebugPanel(DebugPanel):
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
operation_tracker.install_tracker()
+ self.jinja_env.loader = ChoiceLoader([self.jinja_env.loader,
+ PackageLoader('flaskext.mongoengine', 'templates')])
def process_request(self, request):
operation_tracker.reset()
@@ -189,4 +68,5 @@ def content(self):
context['inserts'] = operation_tracker.inserts
context['updates'] = operation_tracker.updates
context['removes'] = operation_tracker.removes
+ context['slow_query_limit'] = current_app.config.get('MONGO_DEBUG_PANEL_SLOW_QUERY_LIMIT', 100)
return self.render('panels/mongo-panel.html', context)
View
6 flaskext/mongoengine/templates/panels/mongo-panel.html
@@ -35,7 +35,7 @@
<tbody>
{% for query in queries %}
<tr class="{{ loop.cycle('djDebugOdd' 'djDebugEven') }}">
- <td>{{ query.time }}</td>
+ <td style="{{ 'color:red;' if query.time > slow_query_limit else '' }}">{{ query.time|round(3) }}</td>
<td>{{ query.operation|title }}</td>
<td>{{ query.collection }}</td>
<td>
@@ -93,7 +93,7 @@
<tbody>
{% for insert in inserts %}
<tr class="{{ loop.cycle('djDebugOdd' 'djDebugEven') }}">
- <td>{{ insert.time }}</td>
+ <td style="{{ 'color:red;' if insert.time > slow_query_limit else '' }}">{{ insert.time|round(3) }}</td>
<td>
<span class="mongo-highlight">{{ insert.document|safe }}</pre>
</td>
@@ -145,7 +145,7 @@
<tbody>
{% for remove in removes %}
<tr class="{{ loop.cycle('djDebugOdd' 'djDebugEven') }}">
- <td>{{ remove.time }}</td>
+ <td style="{{ 'color:red;' if remove.time > slow_query_limit else '' }}">{{ remove.time|round(3) }}</td>
<td>
<span class="mongo-highlight">{{ remove.spec_or_id|safe }}</pre>
</td>
View
98 flaskext/mongoengine/templates/panels/mongoengine.html
@@ -1,98 +0,0 @@
-<table>
- <thead>
- <tr>
- <th>#</th>
- <th style="width:10px;">Millis</th>
- <th>Ts</th>
- <th>Type</th>
- <th>Collection</th>
- <th>Info</th>
- <th>Query</th>
- <th>Optimizations</th>
- </tr>
- </thead>
- <tbody>
- {% for query in queries %}
- <tr class="{{ loop.cycle('djDebugOdd', 'djDebugEven') }}" >
- <td>{{ loop.index }}</td>
- <td style="{% if query.millis>=100 %}color:red;{% endif %}">{{ query.millis }}</td>
- <td>{{ query.ts }}</td>
- <td>{{ query.operation_type }}</td>
- <td>{{ query.collection }}</td>
- <td>{{ query.extra|safe }}</td>
- <td>{{ query.query }}</td>
- <td>{{ query.optimizations }}</td>
- </tr>
- {% endfor %}
- </tbody>
-</table>
-
-<a href="javascript:document.getElementById('ddt-mongodb-info').style.display='block';">More Information</a>
-
-<div id="ddt-mongodb-info" style="display:none;">
- <h4>Query</h4>
- <table>
- <tbody>
- <tr>
- <td><b>ntoreturn</b></td>
- <td>Number of objects the client requested for return from a query.</td>
- </tr>
- <tr>
- <td><b>nscanned</b></td>
- <td>Number of objects scanned in executing the operation.</td>
- </tr>
- <tr>
- <td><b>reslen</b></td>
- <td>Query result length in bytes.</td>
- </tr>
- <tr>
- <td><b>nreturned</b></td>
- <td>Number of objects returned from query.</td>
- </tr>
- </tbody>
- </table>
-
- <br/>
- <h5>Optimizations</h5>
- <p><b>index</b> If nscanned is much higher than nreturned, the database is scanning many objects to find the target objects. Consider creating an index to improve this.</p>
- <p><b>select members</b> reslen A large number of bytes returned (hundreds of kilobytes or more) causes slow performance. Consider passing find() a second parameter of the member names you require.</p>
- <br/>
- <em> Note: There is a cost for each index you create. The index causes disk writes on each insert and some updates to the collection. If a rare query, it may be better to let the query be "slow" and not create an index. When a query is common relative to the number of saves to the collection, you will want to create the index.</em>
-
- <h4>Update</h4>
- <table>
- <tbody>
- <tr>
- <td><b>fastmod</b></td>
- <td>Indicates a fast modify operation. These operations are normally quite fast</td>
- </tr>
- <tr>
- <td><b>fastmodinsert</b></td>
- <td>Indicates a fast modify operation that performed an upsert.</td>
- </tr>
- <tr>
- <td><b>upsert</b></td>
- <td>Indicates on upsert performed.</td>
- </tr>
- <tr>
- <td><b>moved</b></td>
- <td>Indicates the update moved the object on disk (not updated in place). This is slower than an in place update, and normally occurs when an object grows.</td>
- </tr>
- <tr>
- <td><b>key updates</b></td>
- <td>How many index keys changed during the update. Key updates are a little bit expensive since the db needs to remove the old key and insert a new key into the b-tree index.</td>
- </tr>
- </tbody>
- </table>
-
- <br/>
- <h5>Optimizations</h5>
- <p><b>index</b> Examine the nscanned info field. If it is a very large value, the database is scanning a large number of objects to find the object to update. Consider creating an index if updates are a high-frequency operation.</p>
- <br/>
-
- <p>
- <ul>
- <li><a href="http://www.mongodb.org/display/DOCS/Database+Profiler">MongoDB Database Profiler documentation</a>
- </ul>
- </p>
-</div>

0 comments on commit d892cf0

Please sign in to comment.