Performance Profiling Stack
Date: November 26, 2025 (Updated: November 30, 2025)
Category: Development Tools
Status: Active
Overview
A modern, open-source performance debugging stack for Rails development and local analysis. These tools complement AppSignal which provides production APM.
Development vs Production
| Environment | Tools | Purpose |
|---|---|---|
| Development | rack-mini-profiler, Prosopite, PgHero | Interactive debugging, N+1 detection |
| Production | AppSignal APM | Request tracing, error tracking, host metrics |
Components
1. Rack Mini Profiler
Purpose: Visual performance dashboard showing request timing breakdown
# Gemfile (development group)
gem 'rack-mini-profiler'
gem 'stackprof' # Enables flamegraphs
gem 'memory_profiler' # Memory leak detection
gem 'flamegraph' # Flamegraph visualization
Usage:
- Enable with
DEV_TRACE=trueenvironment variable - Speed badge appears in top-left corner of pages
- Click badge for detailed breakdown (SQL, Ruby, Views)
- Click "flamegraph" for CPU profiling visualization
Configuration: config/initializers/rack_mini_profiler.rb
Rack::MiniProfiler.config.enabled = ENV.fetch('DEV_TRACE', false).to_b
Rack::MiniProfiler.config.position = 'top-left'
Rack::MiniProfiler.config.enable_advanced_debugging_tools = true if defined?(StackProf)
Excluded Paths:
/assets,/packs,/javascripts/webpack/sidekiq,/pghero,/letter_opener/v1,/api/cable,/media
2. Prosopite (N+1 Query Detection)
Purpose: Detect N+1 queries with zero false positives
Configuration: config/initializers/prosopite.rb
Prosopite.prosopite_logger = Rails.logger
Prosopite.ignore_queries = [
/sidekiq/i, /pghero/i, /devise/i, /paper_trail/i,
%r{app/workers/}, %r{app/mailers/}, %r{lib/tasks/}
# ... more patterns
]
How it works:
- Monitors transaction call stacks (more accurate than Bullet)
- Logs warnings to Rails logger in development
- Can raise exceptions in test environment (disabled by default)
3. PgHero (PostgreSQL Dashboard)
Purpose: PostgreSQL performance analysis and index recommendations
Access: https://crm.warmlyyours.me:3000/pghero (admin login required)
Features:
- Slow query detection
- Index suggestions (with hypopg for what-if analysis)
- Connection monitoring
- Vacuum health
- Space usage analysis
- EXPLAIN ANALYZE for queries
Configuration: config/pghero.yml
Required PostgreSQL Extensions:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS hypopg;
4. AppSignal APM (Production)
Purpose: Production performance monitoring, error tracking, and host metrics
Access: https://appsignal.com/warmlyyours
Features:
- Request performance tracking with timing breakdown
- Slow endpoint detection
- Error tracking with stack traces
- Host metrics (CPU, memory, disk)
- Deploy markers for correlation
- Anomaly detection and alerting
Configuration: config/appsignal.rb
Documentation: Infrastructure/MONITORING.md
Quick Reference
| Tool | Environment | Enable | Access |
|---|---|---|---|
| rack-mini-profiler | Development | DEV_TRACE=true rails s |
Speed badge (top-left) |
| Flamegraphs | Development | Click badge → "flamegraph" | In mini-profiler |
| Prosopite | Development | Automatic | Check Rails logs for N+1 warnings |
| PgHero | Dev/Staging | Automatic | /pghero (admin only) |
| memory_profiler | Development | bin/rails memory:expr EXPR='code' or manual |
stdout report (or pipe to file) |
| AppSignal | Production/Staging | Automatic | https://appsignal.com/warmlyyours |
When to Use Each Tool
| Scenario | Tool |
|---|---|
| Debug slow page in development | rack-mini-profiler |
| Find N+1 queries while coding | Prosopite (check Rails logs) |
| Analyze database performance | PgHero |
| CPU profiling / hotspots | Flamegraphs (via mini-profiler) |
| Memory leak investigation | memory_profiler (bin/rails memory:expr or ?_profile_memory=1 on staging) |
| Per-gem startup cost | bundle exec derailed bundle:mem |
| Production slow endpoints | AppSignal APM |
| Production errors | AppSignal Error Tracking |
| Post-deploy performance check | AppSignal Deploy Markers |
Memory Profiling Recipes
The memory_profiler gem ships in dev/test bundles. Three entry points:
1. From a controller/action on staging
Append ?_profile_memory=1 to any URL. The RequestMemoryTracer
middleware (config/initializers/request_memory_tracer.rb) wraps the
request in MemoryProfiler.report and writes the report inside the
running Kamal app container to
/var/www/heatwave/shared/log/memory_profiler/<timestamp>-<path>.txt
(CRASH_LOG_DIR is still hardcoded to that legacy Capistrano-era path in
the initializer; under Kamal/Docker it's a plain container-internal
directory, not a host bind-mount). Pull a report off the staging host with
kamal app exec --reuse "cat /var/www/heatwave/shared/log/memory_profiler/<file>"
or docker cp <container>:/var/www/heatwave/shared/log/memory_profiler/. ..
Rate-limited to one capture per 60s per worker so you can't accidentally
DoS the staging servers.
2. From the command line — arbitrary expression
bin/rails memory:expr EXPR='Order.pending.includes(:customer).to_a'
bin/rails memory:expr EXPR='Search.find(123).search_results.to_a' MAX=25
3. From the command line — wrap another rake task
bin/rails memory:rake TASK=ahrefs:stats
4. Boot-time gem analysis
bundle exec derailed bundle:mem # how much each gem costs to require
bundle exec derailed bundle:objects # objects allocated per gem
Concrete patterns to grep for memory wins
When auditing a hot path that allocates too much:
Model.where(...).map(&:id)→Model.where(...).pluck(:id)
(≈90% allocation reduction per the pawelurbanek.com benchmark).Model.where(...).map(&:single_column)→pluck(:single_column).Model.where(...).map { |r| [r.id, r.name] }→pluck(:id, :name).- Need a few model methods but not full records?
.select(:id, :col_a, :col_b)
returns lightweight model instances (~65% reduction); accessing
unfetched columns raisesActiveModel::MissingAttributeErrorso this
catches over-fetching at runtime. Model.all.each→find_each(orin_batches) for any iteration over
more than a few hundred records.- Loading full records just to call
.count/.exists?→ use those
query methods directly.
To find candidates in this repo:
grep -rn "\.map(&:id)\|\.map(&:name)\|\.map(&:email)\|\.map(&:sku)" app/ lib/
Files
| File | Purpose |
|---|---|
config/initializers/rack_mini_profiler.rb |
Mini profiler config |
config/initializers/prosopite.rb |
N+1 detection config |
config/initializers/pghero.rb |
PgHero controller setup |
config/pghero.yml |
PgHero database config |
config/appsignal.rb |
AppSignal APM config |
Removed
scout_apmgem (replaced by this stack + AppSignal)config/scout_apm.yml.obsolete
See Also
- AppSignal Monitoring — Production APM & error tracking
- AppSignal MCP Prompts — AI-assisted error investigation
- PgHero GitHub
- rack-mini-profiler
- Prosopite