4.4 KiB
4.4 KiB
name, description, auto_invoke, trigger_on, file_patterns, tags, priority, version
| name | description | auto_invoke | trigger_on | file_patterns | tags | priority | version | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| rails-performance-patterns | Detects N+1 queries, suggests eager loading, and recommends database indexes | true |
|
|
|
2 | 2.0 |
Rails Performance Patterns Skill
Automatically detects performance issues and suggests optimizations.
What This Skill Does
Automatic Detection:
- N+1 query problems (missing eager loading)
- Missing database indexes on foreign keys
- Inefficient query patterns
- Large result sets without pagination
When It Activates:
- Model files with associations modified
- Controller actions that query models
- Iteration over associations detected
Key Checks
1. N+1 Query Detection
Problem Pattern:
# app/controllers/posts_controller.rb
def index
@posts = Post.all # 1 query
@posts.each do |post|
puts post.author.name # N queries (one per post)
end
end
Skill Output:
⚠️ Performance: Potential N+1 query
Location: app/controllers/posts_controller.rb:15
Issue: Accessing 'author' association in loop without eager loading
Fix: Add includes to eager load:
@posts = Post.includes(:author).all
Solution:
def index
@posts = Post.includes(:author).all # 2 queries total
end
2. Missing Indexes
Checks:
- Foreign keys have indexes
- Commonly queried columns indexed
- Unique constraints have indexes
Example:
# db/migrate/xxx_create_posts.rb
create_table :posts do |t|
t.references :user # ✅ Auto-creates index
t.string :slug # ❌ Missing index if queried often
end
Skill Output:
⚠️ Performance: Missing index recommendation
Location: app/models/post.rb:5
Issue: slug column used in where clauses without index
Add migration:
add_index :posts, :slug, unique: true
3. Pagination Missing
Problem:
def index
@products = Product.all # ❌ Loads all 100k+ products
end
Skill Output:
⚠️ Performance: Large result set without pagination
Location: app/controllers/products_controller.rb:10
Issue: Loading all Product records (estimated 100k+ rows)
Recommendation: Add pagination
# Use kaminari or pagy
@products = Product.page(params[:page]).per(20)
4. Counter Cache Opportunities
Pattern:
# Without counter cache
@user.posts.count # Runs COUNT(*) query every time
# With counter cache
@user.posts_count # Reads from cached column
Skill Output:
ℹ️ Performance: Counter cache opportunity
Location: app/views/users/show.html.erb:12
Pattern: Frequently accessing post count
Add to migration:
add_column :users, :posts_count, :integer, default: 0
Update association:
belongs_to :user, counter_cache: true
Configuration
# .rails-performance.yml
n1_detection:
enabled: true
severity: warning
indexes:
check_foreign_keys: true
check_query_columns: true
pagination:
warn_threshold: 1000
require_for_large_tables: true
counter_cache:
suggest_threshold: 3 # Suggest if accessed 3+ times
Monitoring Integration
Add instrumentation:
# config/initializers/query_monitoring.rb
ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
if event.duration > 100 # Log slow queries
Rails.logger.warn("Slow query (#{event.duration}ms): #{event.payload[:sql]}")
end
end
Common Optimizations
Eager Loading:
# N+1
Post.all.each { |p| p.author.name }
# Fixed: includes (left join)
Post.includes(:author).each { |p| p.author.name }
# Fixed: preload (separate queries)
Post.preload(:author).each { |p| p.author.name }
# Fixed: eager_load (always joins)
Post.eager_load(:author).each { |p| p.author.name }
Select Specific Columns:
# Loads all columns
Post.all
# Loads only needed columns
Post.select(:id, :title, :created_at)
Batch Processing:
# Loads all at once
Post.all.each { |p| process(p) }
# Loads in batches of 1000
Post.find_each(batch_size: 1000) { |p| process(p) }
References
- Rails Performance Guide: https://guides.rubyonrails.org/performance_testing.html
- Bullet Gem: https://github.com/flyerhzm/bullet
- Pattern Library: /patterns/caching-patterns.md
This skill helps you build fast Rails applications from the start.