Skip to content

Understanding Reports

smattymatty edited this page Aug 25, 2025 · 1 revision

๐Ÿ“Š Understanding Performance Reports

Learn to read, interpret, and act on Mercury's performance reports. Every metric tells a story about your application's health.

Report Philosophy

Mercury reports are designed to be:

  • Progressive: Simple overview โ†’ Detailed analysis
  • Actionable: Every issue includes a solution
  • Educational: Learn while you analyze
  • Adaptive: Different details per profile

Report Anatomy

The Overview Dashboard

Every Mercury report starts with a dashboard:

๐ŸŽจ MERCURY PERFORMANCE DASHBOARD - UserAPITests
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ ๐Ÿš€ Overall Status: NEEDS IMPROVEMENT                          โ”‚
โ”‚ ๐ŸŽ“ Overall Grade: C (65/100)                                 โ”‚
โ”‚ ๐Ÿ“Š Tests Executed: 12                                        โ”‚
โ”‚ โฑ๏ธ  Avg Response Time: 105.6ms                                โ”‚
โ”‚ ๐Ÿง  Avg Memory Usage: 45.7MB                                  โ”‚
โ”‚ ๐Ÿ—ƒ๏ธ  Total Queries: 567 (47.25 avg)                          โ”‚
โ”‚ ๐Ÿšจ N+1 Issues: 3/12 tests affected                           โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Let's break down each element:

๐Ÿš€ Overall Status

Status levels indicate urgency:

  • โœ… EXCELLENT: Everything is optimized
  • โœ… GOOD: Minor improvements possible
  • โš ๏ธ NEEDS IMPROVEMENT: Notable issues to address
  • โŒ CRITICAL: Serious performance problems

๐ŸŽ“ Performance Grades

Mercury uses an academic grading system:

Grade Score Meaning Action Required
S 100 Perfect None - celebrate! ๐ŸŽ‰
A+ 95-99 Excellent Minor tweaks only
A 90-94 Very Good Polish for perfection
B 80-89 Good Some optimization needed
C 70-79 Acceptable Clear room for improvement
D 60-69 Poor Significant issues
F <60 Failing Critical problems

How Grades Are Calculated

# Grade components and weights
Response Time: 40%
Query Count: 30%  
Memory Usage: 20%
Cache Performance: 10%

# Example calculation
Response: 85/100 ร— 0.4 = 34
Queries: 70/100 ร— 0.3 = 21
Memory: 90/100 ร— 0.2 = 18
Cache: 60/100 ร— 0.1 = 6
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Total: 79/100 = Grade C

Core Metrics Explained

โฑ๏ธ Response Time

What it measures: Time from request to response

Response Time: 156ms
โ”œโ”€ Database: 89ms (57%)
โ”œโ”€ Python Logic: 45ms (29%)
โ”œโ”€ Template Rendering: 12ms (8%)
โ””โ”€ Other: 10ms (6%)

Thresholds:

  • Excellent: <50ms
  • Good: 50-100ms
  • Acceptable: 100-200ms
  • Slow: 200-500ms
  • Critical: >500ms

Common Causes of Slow Response:

  1. Unoptimized queries (most common)
  2. Complex computations in views
  3. External API calls without caching
  4. Large data serialization

๐Ÿ—ƒ๏ธ Database Queries

What it measures: Number of database queries per request

Query Analysis:
Total Queries: 89
โ”œโ”€ SELECT: 87
โ”œโ”€ INSERT: 1
โ”œโ”€ UPDATE: 1
โ””โ”€ DELETE: 0

Duplicate Queries: 43 (48%)
N+1 Detected: Yes

Thresholds:

  • Excellent: 1-5 queries
  • Good: 6-10 queries
  • Acceptable: 11-20 queries
  • High: 21-50 queries
  • Critical: >50 queries

Reading Query Patterns:

# Good pattern (2 queries)
SELECT users...
SELECT profiles WHERE user_id IN (1,2,3...)

# Bad pattern (N+1, 101 queries)
SELECT users...
SELECT profile WHERE user_id = 1
SELECT profile WHERE user_id = 2
... (98 more)

๐Ÿง  Memory Usage

What it measures: RAM consumed during request

Memory Breakdown:
Total: 45.7MB
โ”œโ”€ Django ORM: 28.3MB (62%)
โ”œโ”€ Python Objects: 12.1MB (26%)
โ”œโ”€ Templates: 3.2MB (7%)
โ””โ”€ Other: 2.1MB (5%)

Peak Usage: 67.8MB
Baseline: 23.4MB
Overhead: 44.4MB

Thresholds:

  • Excellent: <10MB overhead
  • Good: 10-25MB
  • Acceptable: 25-50MB
  • High: 50-100MB
  • Critical: >100MB

Common Memory Issues:

  1. Loading entire querysets into memory
  2. Large serialized responses
  3. Unbounded caches
  4. Memory leaks in views

๐Ÿ’พ Cache Performance

What it measures: Cache hit/miss ratio

Cache Statistics:
Hit Rate: 73%
โ”œโ”€ Database Cache: 45/50 (90%)
โ”œโ”€ Template Cache: 12/20 (60%)
โ”œโ”€ API Cache: 8/15 (53%)
โ””โ”€ Static Files: 15/15 (100%)

Missed Opportunities: 12
Suggested Cache Keys: 
- user_list_page_1
- product_details_5

Target Hit Rates:

  • Excellent: >90%
  • Good: 80-90%
  • Acceptable: 70-80%
  • Poor: 50-70%
  • Critical: <50%

Issue Detection and Explanations

N+1 Query Detection

Mercury identifies N+1 patterns:

๐Ÿšจ N+1 QUERY PATTERN DETECTED
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
Location: UserSerializer.to_representation()
Pattern: Accessing user.profile in loop

Query Pattern Found:
1. SELECT * FROM users (1 query)
2. SELECT * FROM profiles WHERE user_id = ? (N queries)

Impact: 101 queries for 100 users

Solution:
queryset = User.objects.select_related('profile')

Expected Improvement: 
101 queries โ†’ 1 query (99% reduction)

Slow Query Identification

๐ŸŒ SLOW QUERY DETECTED
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
Query: SELECT * FROM orders WHERE status = 'pending' 
       AND created_at > '2024-01-01'
       
Execution Time: 342ms
Rows Examined: 45,678
Rows Returned: 234

Missing Index: orders.status, orders.created_at

Solution:
class Meta:
    indexes = [
        models.Index(fields=['status', 'created_at'])
    ]

Expected Improvement:
342ms โ†’ ~15ms (95% faster)

Memory Spike Detection

๐Ÿ“ˆ MEMORY SPIKE DETECTED
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
Location: ProductListView.get_queryset()
Spike: 23MB โ†’ 89MB (+66MB)

Cause: Loading all products into memory
products = list(Product.objects.all())

Solution:
Use pagination or iterator():
products = Product.objects.iterator(chunk_size=100)

Expected Improvement:
66MB overhead โ†’ ~5MB (92% reduction)

Profile-Specific Reports

๐ŸŽ“ Student Profile Reports

Educational and detailed:

๐Ÿ“š PERFORMANCE REPORT - EDUCATIONAL MODE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Test: test_user_list_api
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Grade: D (65/100)

What happened:
โœ— Response took 234ms (should be under 100ms)
โœ— Made 89 queries (should be under 10)
โœ“ Memory usage OK at 23MB

Why this matters:
โ€ข Slow pages frustrate users
โ€ข Many queries = database overload
โ€ข This will get worse with more data

How to fix (step by step):
1. Open views.py line 45
2. Find: users = User.objects.all()
3. Change to: users = User.objects.select_related('profile')
4. Run test again to verify

Learn more:
mercury-test --learn n1-queries

You're learning! Every fix makes you better! ๐Ÿ’ช

๐Ÿ’ผ Expert Profile Reports

Concise and technical:

PERFORMANCE METRICS
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
test_user_list      234ms  89q  23MB  D  N+1@L45
test_user_detail     45ms   3q  12MB  B  OK
test_user_create     67ms   5q  18MB  B  OK
test_user_search    456ms 234q  45MB  F  N+1@L78,L92

Critical: 2 N+1 patterns
Action: select_related on L45, prefetch_related on L78

๐Ÿค– Agent Profile Reports

Structured JSON:

{
  "summary": {
    "grade": "D",
    "score": 65,
    "status": "needs_improvement"
  },
  "metrics": {
    "response_time_ms": 234,
    "query_count": 89,
    "memory_mb": 23,
    "cache_hit_ratio": 0.73
  },
  "issues": [
    {
      "type": "n_plus_one",
      "severity": "high",
      "location": "views.py:45",
      "impact": {
        "queries_excess": 84,
        "time_excess_ms": 134
      },
      "fix": {
        "code": "User.objects.select_related('profile')",
        "auto_applicable": false,
        "requires_review": true
      }
    }
  ],
  "recommendations": [
    "Add database indexes",
    "Implement query caching",
    "Use pagination"
  ]
}

Understanding Severity Levels

๐Ÿ”ด Critical (Immediate Action)

  • Response time >1 second
  • 100+ queries per request
  • Memory usage >200MB
  • Complete cache misses

๐ŸŸ  High (Fix Soon)

  • Response time 500ms-1s
  • 50-100 queries
  • Memory usage 100-200MB
  • Cache hit rate <50%

๐ŸŸก Medium (Plan to Fix)

  • Response time 200-500ms
  • 20-50 queries
  • Memory usage 50-100MB
  • Cache hit rate 50-70%

๐ŸŸข Low (Nice to Have)

  • Response time 100-200ms
  • 10-20 queries
  • Memory usage 25-50MB
  • Cache hit rate 70-80%

Comparative Reports

Baseline Comparison

๐Ÿ“Š PERFORMANCE COMPARISON
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
              Baseline โ†’ Current    Change   Status
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Response:     145ms โ†’ 89ms         -38.6%   โœ… Improved
Queries:      67 โ†’ 45              -32.8%   โœ… Improved
Memory:       34MB โ†’ 38MB          +11.7%   โš ๏ธ Increased
Cache:        65% โ†’ 82%            +26.1%   โœ… Improved

Overall: Performance IMPROVED by 27%

Biggest wins:
โ€ข UserListView: 78% faster
โ€ข ProductSearch: 65% fewer queries

New issues:
โ€ข OrderHistoryView: Memory increased 45%

Trend Analysis

๐Ÿ“ˆ PERFORMANCE TRENDS (Last 7 Days)
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Response Time:
Mon โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 156ms
Tue โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  145ms  
Wed โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    134ms
Thu โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ     128ms
Fri โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ       112ms
Sat โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ        98ms  โ† Best
Sun โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ       105ms

Pattern: Steady improvement
Recommendation: Current optimizations working

Taking Action on Reports

Priority Matrix

Use this matrix to decide what to fix first:

         High Impact
              โ†‘
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚    1    โ”‚    2    โ”‚
    โ”‚ Criticalโ”‚Importantโ”‚
    โ”‚  & Easy โ”‚ & Easy  โ”‚
    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚    3    โ”‚    4    โ”‚
    โ”‚Critical โ”‚Importantโ”‚
    โ”‚  & Hard โ”‚ & Hard  โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    Easy โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’ Hard

Fix Order:

  1. High impact, easy fixes (N+1 queries)
  2. High impact, moderate difficulty (indexes)
  3. Lower impact, easy fixes (caching)
  4. Complex architectural changes (last)

Creating Action Items

Transform report issues into tasks:

# From report
"N+1 in UserSerializer, 89 queries"

# To action item
"""
Task: Fix N+1 in UserSerializer
Priority: High
Estimate: 15 minutes
Steps:
1. Open serializers.py
2. Update queryset in ViewSet
3. Add select_related('profile')
4. Run tests to verify
Expected: 89 queries โ†’ 2 queries
"""

Tracking Improvements

Document your fixes:

## Performance Improvements Log

### 2024-01-15
- **Issue**: N+1 in UserListView
- **Fix**: Added select_related
- **Result**: 89 queries โ†’ 2 queries
- **Impact**: 234ms โ†’ 45ms (80% faster)

### 2024-01-16  
- **Issue**: Missing index on orders.status
- **Fix**: Added database index
- **Result**: 342ms โ†’ 15ms query time
- **Impact**: Overall 50% speed improvement

Common Report Patterns

The "Death by Thousand Cuts"

No single critical issue, but many small ones:
- 15 queries (not terrible)
- 150ms response (not terrible)
- 35MB memory (not terrible)
- 60% cache hits (not terrible)

Combined: Grade D performance

Fix: Address all small issues systematically

The "One Bad Apple"

Everything excellent except one critical issue:
- โœ… 3 queries
- โŒ 2.5 second response time
- โœ… 15MB memory
- โœ… 95% cache hits

Cause: External API call without timeout

Fix: Add timeout and async processing

The "Hidden N+1"

Seems OK at first:
- 12 queries (acceptable)
- 89ms response (good)

But with more data:
- 120 queries (10x users)
- 890ms response (10x slower)

Classic N+1 scaling problem

Report Customization

Configure Report Detail

# mercury_config.toml
[reporting]
detail_level = "high"  # low, medium, high
show_sql_queries = true
show_stack_traces = false
show_suggestions = true
group_by = "test_class"  # test_class, endpoint, severity

Custom Report Formats

# HTML report for sharing
mercury-test --format=html > report.html

# CSV for spreadsheet analysis
mercury-test --format=csv > metrics.csv

# Markdown for documentation
mercury-test --format=markdown > PERFORMANCE.md

Focus Reports

# Just show failures
mercury-test --report-failures-only

# Show worst performers
mercury-test --show-worst 5

# Filter by metric
mercury-test --report-filter="queries>50"

Learning from Reports

Pattern Recognition

Look for patterns across tests:

  • Same issue in multiple places?
  • Issues clustered in one app?
  • Certain operations always slow?

Building Intuition

Over time, you'll recognize issues instantly:

  • "45 queries for 10 items = N+1"
  • "500ms for simple list = missing index"
  • "Memory spike = loading full queryset"

Sharing Knowledge

Share interesting reports with your team:

  • "Look at this N+1 I found!"
  • "Check out this optimization"
  • "Here's a pattern to avoid"

Next Steps


Remember: Reports are not judgments, they're opportunities to improve.

Django Mercury Wiki

๐Ÿ  Overview

Clone this wiki locally