Back to Source Monitor

Phase 2: Test Performance - Research

.vbw-planning/milestones/aia-ssl-fix/02-test-performance/02-RESEARCH.md

0.13.02.4 KB
Original Source

Phase 2: Test Performance - Research

Investigation Method

3-agent competing hypothesis team (H1: DB/parallelism, H2: HTTP/VCR, H3: test helpers)

Findings

Time Budget (133s total, 1031 tests)

ComponentTime%Root Cause
FeedFetcherTest (71 tests)84.8s64%Monolithic class, CPU-bound (Feedjira parse, SHA256, content extraction)
Integration tests (9 tests)30.8s23%Subprocess spawning (bundle exec rails g)
System tests (22 tests)~5s4%Selenium/Chrome
Debug log IO5-15s8%95MB :debug output to disk
Everything else (~930 tests)~15s11%Fast

Why Parallelism Doesn't Help Today

  • Minitest distributes by CLASS, not by test
  • FeedFetcherTest = 1 class with 71 tests = 1 worker gets all 84.8s
  • With 8 workers: max(84.8, 25.6, ~3) ≈ 85s + overhead

DB Is NOT the Bottleneck

  • SQL: 3.639s of 69.4s (5.25%) via EventProf
  • schema.rb used (fast load)
  • Transactional tests enabled (proper rollback)

HTTP Mocking Is NOT the Bottleneck

  • Only 4 VCR cassettes (456KB total), 83 WebMock stubs across 9 files
  • WebMock configured once globally, auto-resets
  • Only finding: default_cert_store recreated per connection (~0.5s total)

Test Helpers Are Cheap

  • reset_configuration!: microseconds per call (pure Ruby)
  • create_source!: 1 INSERT per call, 158 calls total
  • with_inline_jobs: 4 calls total, negligible

Profiling Data (tmp/test_prof/)

  • TagProf: lib tests 38.0s (55%), integration 28.5s (41%), controllers 0.97s, jobs 0.53s, models 0.47s
  • EventProf: FeedFetcherTest 0.888s SQL in 34.3s total (2.6% SQL)

Relevant Patterns

  • TestProf before_all available but used in only 1 of 54 eligible files
  • Thread-based parallelism already proven in coverage mode
  • PG fork segfault only on single-file runs, NOT full suite

Risks

  • Thread safety: reset_configuration! may need thread-local config or mutex
  • Test isolation: splitting FeedFetcherTest must preserve test independence
  • before_all: only safe for tests that don't mutate shared data

Recommendations (by impact)

  1. Split FeedFetcherTest into 5-6 classes → enables parallelism, -50s
  2. Set config.log_level = :warn → -5-15s
  3. Tag/skip integration tests in dev → -30s dev experience
  4. Switch to thread-based parallelism → enables splitting benefit
  5. Adopt before_all in DB-heavy test files → -3-5s