performance-optimization
Performance Optimization for Rails 8
Overview
Performance optimization focuses on:
- N+1 query detection and prevention
- Query optimization
- Memory management
- Response time improvements
- Database indexing
Quick Start
# Gemfile
group :development, :test do
gem 'bullet' # N+1 detection
gem 'rack-mini-profiler' # Request profiling
gem 'memory_profiler' # Memory analysis
end
N+1 Query Detection and Prevention
N+1 queries occur when code loads a collection then makes a separate query for each associated record. The Bullet gem detects these automatically. Fix them with eager loading via includes, preload, or eager_load.
Eager Loading Decision Table
| Method | Use When |
|---|---|
includes |
Most cases (Rails chooses best strategy) |
preload |
Forcing separate queries, large datasets |
eager_load |
Filtering on association, need single query |
joins |
Only need to filter, don't need association data |
Key patterns: Bullet configuration, eager loading methods, scoped eager loading, counter caches, N+1 specs with query count assertions.
See references/n-plus-one.md for all code examples and patterns.
Query Optimization
Optimize queries by selecting only needed columns, using batch processing for large datasets, and choosing efficient existence checks.
Key Patterns
| Pattern | Bad | Good |
|---|---|---|
| Column selection | User.all.map(&:name) |
User.pluck(:name) |
| Large iterations | Event.all.each { ... } |
Event.find_each { ... } |
| Existence checks | .any? / .present? |
.exists? |
| Collection size | .length (loads all) |
.size (smart) |
Database Indexing
Add indexes for: foreign keys, columns in WHERE/ORDER BY/JOIN clauses, and unique constraints. Use composite indexes for multi-column queries. Use partial indexes for filtered subsets.
Query Analysis
Use Event.where(...).explain(:analyze) to inspect query plans. Set up slow query logging via ActiveSupport::Notifications to catch queries over a threshold.
See references/query-optimization.md for all code examples and patterns.
Memory Management and Profiling
Use memory_profiler to detect memory issues. Prefer pluck over loading full AR objects, use find_each for streaming, and use update_all / in_batches for bulk operations.
Rack Mini Profiler
Provides per-request profiling in development. Shows query count, timing, and flamegraphs (with stackprof gem). Access via the profiler badge or ?pp=flamegraph.
See references/memory-and-profiling.md for all code examples and patterns.
Quick Fixes Reference
| Problem | Solution |
|---|---|
| N+1 on belongs_to | includes(:association) |
| N+1 on has_many | includes(:association) |
| Slow COUNT | Add counter_cache |
| Loading all columns | Use select or pluck |
| Large dataset iteration | Use find_each |
| Missing index on FK | Add index on *_id columns |
| Slow WHERE clause | Add index on filtered column |
| Loading unused associations | Remove from includes |
Performance Checklist
- Bullet enabled in development/test
- No N+1 queries in critical paths
- Foreign keys have indexes
- Counter caches for frequent counts
- Eager loading in controllers
- Batch processing for large datasets
- Query analysis for slow endpoints
Workflow
- Detect -- Enable Bullet, run specs, check Rack Mini Profiler
- Analyze -- Use
explain(:analyze), check slow query logs, profile memory - Fix -- Apply the appropriate pattern from the reference files
- Verify -- Re-run specs, confirm query counts, check profiler
Reference Files
- references/n-plus-one.md -- N+1 detection, eager loading methods, Bullet config, counter caches, testing patterns
- references/query-optimization.md -- Column selection, batch processing, indexing strategies, EXPLAIN analysis, slow query logging
- references/memory-and-profiling.md -- Memory profiler usage, memory-efficient patterns, Rack Mini Profiler setup, deployment checklist