- Problem: CPU limits causing instance crashes (50% failure rate)
- Root Cause: CPU-intensive JavaScript generation for form widgets on every request
- Current Status: 18 instances, WEB_CONCURRENCY=2, 1600M memory - still unstable
cf set-env touchpoints WEB_CONCURRENCY 1
cf restart touchpointsWhy: Single worker per instance reduces CPU contention Risk: Lower throughput, but higher stability
- Check
cf app touchpointsevery 2 hours - Look for crashed instances
- Monitor CPU usage patterns
File: app/models/form.rb
Method: touchpoints_js_string
def touchpoints_js_string
Rails.cache.fetch("form-js-#{short_uuid}-#{updated_at.to_i}", expires_in: 1.day) do
ApplicationController.new.render_to_string(partial: 'components/widget/fba', formats: :js, locals: { form: self })
end
endFile: app/models/form.rb
Add to: after_commit callback
after_commit do |form|
FormCache.invalidate(form.short_uuid)
Rails.cache.delete("form-js-#{form.short_uuid}-#{form.updated_at.to_i}")
endGoal: Generate JavaScript files when forms are published, not on request
Implementation: Background job to create static .js files in public/widgets/
- Serve cached widget files from CDN
- Reduce server load for static content
- Improve global performance
File: app/models/form_cache.rb
Optimize: Complex includes query
Form.includes([:questions, { form_sections: [questions: [:question_options]] }], :organization)- Contact cloud.gov support
- Request higher CPU allocation for production space
- Justify with performance metrics
- Extract form widget delivery to dedicated microservice
- Lightweight service optimized for static content delivery
- Independent scaling from main application
- Move widget generation to build-time process
- Serve widgets as static files with proper caching headers
- Eliminate runtime JavaScript generation
- 0% instance crash rate
- CPU usage < 20% per instance
- All 18 instances stable
- JavaScript caching implemented
- 90%+ cache hit rate for widget requests
- CPU usage < 15% per instance
- Ability to reduce instance count
- Widget delivery separated from main app
- Sub-100ms response times for widget requests
- Horizontal scaling capability
- Cost reduction from fewer required instances
- P0: Reduce WEB_CONCURRENCY to 1 (immediate stability)
- P1: Implement JavaScript caching (eliminate CPU bottleneck)
- P2: Pre-generate static assets (architectural improvement)
- P3: CDN integration (performance optimization)
- P4: Separate widget service (long-term scalability)