Files
pgsql-jellyfin/ANALYSIS_SUMMARY.md
T
wjones e80dbd757b Implement N+1 query optimization and response caching strategies
- Added QUERY_PATH_MAP.md to document query execution paths and analysis.
- Created QUICK_START.md for a quick guide on N+1 optimization implementation.
- Introduced RESPONSE_CACHING_STRATEGY.md outlining caching strategies for API endpoints.
- Developed TECHNICAL_REFERENCE.md detailing changes made in DtoService.cs for N+1 optimization.
- Optimized item counts retrieval by batching queries, reducing database load significantly.
- Implemented caching for child counts to minimize repeated database queries.
- Enhanced performance metrics showing substantial improvements in query counts and page load times.
2026-07-09 15:58:33 +00:00

9.3 KiB
Raw Blame History

Summary: Query Execution During Web UI Page Load

Overview

I've completed a comprehensive analysis of why queries are executed multiple times during web UI page load. The good news: UserData is properly eagerly loaded. The likely causes are simpler and documented below.


Key Findings

What's Working Correctly

  1. UserData is NOT causing N+1 queries

    • Eagerly loaded via .Include(e => e.UserData) in main query
    • Accessed as in-memory collection, no additional database hits
    • Location: BaseItemRepository.cs
  2. Other navigations are properly included

    • Provider data
    • Images
    • TV/Live TV/Audio extras
    • All loaded in a single query with proper joins

⚠️ What's Causing Multiple Queries

1. Multiple Simultaneous API Calls (MOST LIKELY)

The web UI likely calls 3-5 different GetItems endpoints during page load:

GET /Items?includeItemTypes=Movie&limit=16        (2 queries)
GET /Items?includeItemTypes=Series&limit=16       (2 queries)
GET /Items?filters=IsResumable&limit=16           (2 queries)
GET /Users/{userId}/Items/Resume                  (2 queries)
GET /Playlists                                    (2 queries)
────────────────────────────────────────────────────
TOTAL: 10 queries for basic page load

2. ItemCounts Field N+1 Pattern (IF REQUESTED)

If the UI requests ItemCounts field:

foreach (item in items)  // For each of 20 items
{
    SetItemByNameInfo(item, user);  // +1 query per item
}
// Total: 20 additional queries!

3. ChildCount Field Potential N+1 (IF REQUESTED)

If the UI requests ChildCount field for folders:

foreach (folder in folders)
{
    GetChildCount(folder, user);  // +1 query per folder
}
  • Location: DtoService.cs
  • Impact: Varies by folder count (mitigated for collection folders)

Query Execution Flow

Browser Page Load (Multiple Components)
    ↓
    ├─→ GetItems (Movies)     ─→ 2 queries
    ├─→ GetItems (Series)     ─→ 2 queries
    ├─→ GetItems (Recently)   ─→ 2 queries
    ├─→ GetItems (Resume)     ─→ 2 queries
    └─→ GetPlaylists          ─→ 2 queries
    ═════════════════════════════════════════
    SUBTOTAL: 10 queries

    + [IF ItemCounts requested] ─→ +20 queries
    + [IF ChildCount requested] ─→ +N queries
    ═════════════════════════════════════════
    TOTAL: 10-50+ queries

Evidence & Analysis Files

I've created three detailed analysis documents:

1. QUERY_EXECUTION_ANALYSIS.md (Comprehensive)

  • Complete flow diagram
  • Line-by-line code analysis
  • All 7 key methods examined
  • Why UserData is NOT causing N+1

2. QUERY_ISSUES_REMEDIATION.md (Actionable)

  • Identified actual N+1 patterns
  • Test procedures to confirm issues
  • Code fixes for each pattern
  • Performance impact matrix
  • Priority action items

3. QUERY_PATH_MAP.md (Technical Reference)

  • SQL query patterns
  • Query optimization opportunities
  • Monitoring scripts
  • Batch operation examples

Immediate Investigation Steps

Step 1: Capture Network Traffic

Open browser DevTools → Network tab
Load Jellyfin home page
Count API requests and note parameters

Expected findings:

  • Multiple /Items calls with different parameters
  • Each call results in 2 database queries (normal)
  • Check if fields include ItemCounts or ChildCount

Step 2: Enable SQL Logging

# Edit logging.json
sed -i 's/"Microsoft.EntityFrameworkCore.Database.Command": "Error"/"Microsoft.EntityFrameworkCore.Database.Command": "Debug"/' \
  /home/wjones/projects/pgsql-jellyfin/Jellyfin.Server/Resources/Configuration/logging.json

# Restart Jellyfin
sudo systemctl restart jellyfin

# Load page and check logs
tail -100 /var/log/jellyfin/log_*.log | grep "SELECT"

Step 3: Count Queries by Type

# Count total queries during page load
grep "Database.Command" /var/log/jellyfin/log_*.log | wc -l

# Count UserData queries
grep "FROM.*UserData" /var/log/jellyfin/log_*.log | wc -l

# Count ItemCounts queries (if any)
grep -c "SetItemByNameInfo\|ItemCounts" /var/log/jellyfin/log_*.log

Code Locations Summary

Component File Line Purpose
API Entry Point ItemsController.cs 166 HTTP endpoint, builds DtoOptions
Query Building LibraryManager.cs 1647 Builds InternalItemsQuery
Main Query Execution BaseItemRepository.cs 421 GetItemsAsync - executes queries
Eager Loading BaseItemRepository.cs 797-799 Include(e => e.UserData)
DTO Conversion Loop DtoService.cs 160 Per-item DTO building
Per-Item Processing DtoService.cs 241-268 AttachPeople, MediaSources, etc.
ItemCounts N+1 DtoService.cs 182-183 SetItemByNameInfo per item
ChildCount N+1 DtoService.cs 497-506 GetChildCount per folder
UserData Access UserDataManager.cs 247-253 In-memory collection access (NO QUERY)

Performance Impact Matrix

Scenario Queries Cause Fix Priority
Basic page load (5 endpoints, 20 items each) 10 Multiple API calls Architecture - normal behavior
+ ItemCounts field 100+ N+1 per item HIGH - batch SetItemByNameInfo
+ ChildCount field 10-50 N+1 per folder MEDIUM - add caching
+ Large library (1000 items) 1000+ Pagination multiplied LOW - normal for large datasets
All fields enabled 1000+ Combination of above HIGH - optimize field selection

Root Cause Assessment

Most Likely Cause: Multiple API Calls

The web UI loads 3-5 different components, each making separate API calls. This is normal and expected for single-page applications.

Secondary Cause: ItemCounts Field

If the UI frequently requests the ItemCounts field, this creates an actual N+1 problem that should be fixed via batching.

Tertiary Cause: ChildCount Field

Less likely to be problematic due to existing optimizations for collection folders.


Next Steps (Priority Order)

🔴 HIGH

  1. Capture network traffic during page load (DevTools)

    • Confirms multiple API calls
    • Shows which fields are requested
  2. Enable SQL logging and analyze

    • Count total queries
    • Identify N+1 patterns
    • Check for ItemCounts queries
  3. If ItemCounts is the issue: Implement batching in DtoService

    • Currently: N queries per page
    • After fix: 1 query per page
    • Performance improvement: 10-100x

🟡 MEDIUM

  1. If ChildCount is problematic: Add caching
  2. Profile actual page load time
  3. Measure query improvement after fixes

🟢 LOW

  1. Consider API consolidation (batch requests from UI)
  2. Add query caching for frequently accessed data
  3. Monitor performance over time

Documentation Provided

File Purpose Read Time
QUERY_EXECUTION_ANALYSIS.md Complete flow analysis 15 min
QUERY_ISSUES_REMEDIATION.md Specific issues + fixes 10 min
QUERY_PATH_MAP.md SQL patterns + monitoring 10 min
This file Summary + next steps 5 min

Quick Reference

To verify UserData is working correctly:

# UserData should NOT appear in query logs N times
grep "FROM.*UserData" /var/log/jellyfin/log_*.log | wc -l
# Should be ≤ 2 (once per API call), NOT 20-100

# Should see FROM BaseItems with LEFT JOIN UserData
grep "LEFT JOIN.*UserData" /var/log/jellyfin/log_*.log | head -1
# Should exist (eager loading in action)

To identify which field causes the issue:

# Look for ItemCounts in API calls
grep "fields=" /browser-network-log.txt | grep "ItemCounts"

# Or in DevTools console
console.log(document.querySelector('[data-api-call]')?.innerText)

Questions This Analysis Answers

Are UserData queries N+1? No - they're eagerly loaded

What causes multiple queries? Multiple API endpoints (normal) + optional N+1 fields (ItemCounts)

How do I fix it? See QUERY_ISSUES_REMEDIATION.md for specific code fixes

What's the performance impact? 10-50x slower if ItemCounts is always requested

How many queries should there be?

  • Basic page: ~10 (5 API calls × 2 queries each)
  • With ItemCounts: ~100+ (very slow)
  • Optimized: Still ~10 (batch operations)

Additional Resources