--- skill: jobs category: reference description: Mission Control Jobs setup and authentication patterns --- # Mission Control Jobs - Complete Setup Guide Mission Control Jobs provides a production-ready web dashboard for monitoring and managing SolidQueue background jobs. This guide covers complete setup for development through production deployment with team access. ## Quick Start ### 1. Add Gem ```ruby # Gemfile gem "mission_control-jobs" ``` ```bash bundle install ``` ### 2. Mount Engine with Authentication ```ruby # config/routes.rb Rails.application.routes.draw do # Production: Require admin authentication if Rails.env.production? authenticate :user, ->(user) { user.admin? } do mount MissionControl::Jobs::Engine, at: "/jobs" end else # Development/Staging: Open access or HTTP Basic Auth mount MissionControl::Jobs::Engine, at: "/jobs" end end ``` ### 3. Configure (Optional) ```ruby # config/initializers/mission_control.rb MissionControl::Jobs.configure do |config| # Job retention periods config.finished_jobs_retention_period = 14.days # Default: 7 days config.failed_jobs_retention_period = 90.days # Default: 30 days # Filter sensitive arguments from dashboard display config.filter_parameters = [:password, :token, :secret, :api_key] end ``` ### 4. Access Dashboard Visit `http://localhost:3000/jobs` in your browser (development) or `https://yourapp.com/jobs` (production). --- ## Production Authentication Patterns ### Pattern 1: Devise Admin Users (Recommended) ```ruby # config/routes.rb Rails.application.routes.draw do authenticate :user, ->(user) { user.admin? } do mount MissionControl::Jobs::Engine, at: "/jobs" end end ``` **Requirements:** - User model with `admin?` method or `admin` boolean field - Devise authentication already configured **Example User Model:** ```ruby # app/models/user.rb class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # Option 1: Boolean field def admin? admin # Assumes `admin` boolean column exists end # Option 2: Role-based enum role: { user: 0, admin: 1, superadmin: 2 } def admin? admin? || superadmin? end end ``` ### Pattern 2: Custom Authentication Logic ```ruby # config/routes.rb Rails.application.routes.draw do authenticate :user, ->(user) { user.can_access_mission_control? } do mount MissionControl::Jobs::Engine, at: "/jobs" end end # app/models/user.rb class User < ApplicationRecord def can_access_mission_control? admin? || role == "operations" || email.end_with?("@yourcompany.com") end end ``` ### Pattern 3: HTTP Basic Auth (Staging/Internal Tools) ```ruby # config/routes.rb Rails.application.routes.draw do # Add constraint for HTTP Basic Auth constraints(->(req) { authenticate_mission_control(req) }) do mount MissionControl::Jobs::Engine, at: "/jobs" end end # config/application.rb or initializer def authenticate_mission_control(request) return true if Rails.env.development? authenticate_or_request_with_http_basic do |username, password| username == ENV['MISSION_CONTROL_USERNAME'] && password == ENV['MISSION_CONTROL_PASSWORD'] end end ``` **Set environment variables:** ```bash # .env or production secrets MISSION_CONTROL_USERNAME=admin MISSION_CONTROL_PASSWORD=secure_random_password_here ``` ### Pattern 4: IP Whitelist (Internal Networks) ```ruby # config/routes.rb Rails.application.routes.draw do constraints(->(req) { internal_ip?(req.remote_ip) }) do mount MissionControl::Jobs::Engine, at: "/jobs" end end # config/application.rb def internal_ip?(ip) allowed_ips = ENV.fetch('MISSION_CONTROL_IPS', '').split(',') allowed_ips.include?(ip) || ip.start_with?('10.', '192.168.') end ``` ### Pattern 5: Multi-Environment Configuration ```ruby # config/routes.rb Rails.application.routes.draw do case Rails.env when "production" # Production: Require admin user authenticate :user, ->(user) { user.admin? } do mount MissionControl::Jobs::Engine, at: "/jobs" end when "staging" # Staging: HTTP Basic Auth constraints(->(req) { authenticate_basic(req) }) do mount MissionControl::Jobs::Engine, at: "/jobs" end else # Development: Open access mount MissionControl::Jobs::Engine, at: "/jobs" end end ``` --- ## Team Access Management ### Granting Admin Access ```ruby # Rails console (production) rails console # Grant admin access to user user = User.find_by(email: "teammate@company.com") user.update!(admin: true) # Or using role enum user.update!(role: :admin) ``` ### Bulk Admin Creation ```ruby # db/seeds.rb or migration admin_emails = [ "ops_lead@company.com", "dev_lead@company.com", "support_manager@company.com" ] admin_emails.each do |email| user = User.find_or_create_by(email: email) user.update!(admin: true) end ``` ### Team Roles Pattern ```ruby # app/models/user.rb class User < ApplicationRecord enum role: { user: 0, developer: 1, operations: 2, admin: 3 } def can_access_jobs_dashboard? developer? || operations? || admin? end end # config/routes.rb authenticate :user, ->(user) { user.can_access_jobs_dashboard? } do mount MissionControl::Jobs::Engine, at: "/jobs" end ``` --- ## Dashboard Features & Usage ### Jobs Overview Tab **Features:** - View all jobs across all queues - Filter by status: pending, running, finished, failed - Real-time updates (auto-refresh) - Queue performance metrics **Common Operations:** - Search jobs by class name - Filter by date range - Sort by created/finished time ### Queues Tab **Metrics Displayed:** - Pending job count per queue - Active workers per queue - Throughput (jobs/minute) - Latency (average wait time) **Use Cases:** - Identify bottlenecked queues - Verify queue priority configuration - Monitor worker capacity ### Failed Jobs Tab **Features:** - Full error backtraces - Job arguments and context - Retry history and attempt counts - Bulk retry/discard operations **Workflows:** 1. **Investigating Failures:** - Click failed job to see full backtrace - Review job arguments for invalid data - Check retry history for transient vs persistent failures 2. **Bulk Recovery:** - Select multiple failed jobs - Click "Retry Selected" to requeue - Or "Discard Selected" for jobs that can't be fixed 3. **Pattern Detection:** - Group by error type to find systemic issues - Filter by time range to correlate with deployments - Search by class name to find job-specific problems ### Individual Job Details **Information Displayed:** - Job class and queue name - Enqueued/started/finished timestamps - Duration and execution time - Full arguments (with sensitive params filtered) - Error message and backtrace (if failed) - Retry count and next retry time **Available Actions:** - Retry job (failed jobs only) - Discard job (remove from queue) - View full execution context --- ## Configuration Options ### Job Retention Control how long finished and failed jobs are kept in the database: ```ruby # config/initializers/mission_control.rb MissionControl::Jobs.configure do |config| # Keep finished jobs for 2 weeks (default: 7 days) config.finished_jobs_retention_period = 14.days # Keep failed jobs for 3 months (default: 30 days) config.failed_jobs_retention_period = 90.days end ``` **Automatic Cleanup:** SolidQueue automatically cleans up old jobs based on these settings. No manual intervention needed. **Manual Cleanup:** ```ruby # Rails console SolidQueue::Job.finished.where("finished_at < ?", 14.days.ago).delete_all SolidQueue::Job.failed.where("failed_at < ?", 90.days.ago).delete_all ``` ### Parameter Filtering Prevent sensitive data from appearing in the dashboard: ```ruby MissionControl::Jobs.configure do |config| # Filter these parameter keys from display config.filter_parameters = [ :password, :token, :secret, :api_key, :private_key, :access_token, :refresh_token, :credit_card, :ssn ] end ``` **Example Job Arguments:** ```ruby # Job enqueued with: SendEmailJob.perform_later( user_id: 123, password: "secret123", api_token: "sk_live_abc123" ) # Displayed in Mission Control as: { user_id: 123, password: "[FILTERED]", api_token: "[FILTERED]" } ``` ### Custom Routes Mount at a different path: ```ruby # config/routes.rb mount MissionControl::Jobs::Engine, at: "/admin/background-jobs" ``` Access at: `https://yourapp.com/admin/background-jobs` --- ## Production Deployment Checklist - [ ] `mission_control-jobs` gem added to Gemfile - [ ] Bundle installed and Gemfile.lock committed - [ ] Routes configured with authentication - [ ] Authentication tested in staging environment - [ ] Admin users granted access - [ ] Parameter filtering configured for sensitive data - [ ] Job retention periods configured - [ ] Team members notified of dashboard URL - [ ] Dashboard access verified in production - [ ] Monitoring alerts configured (optional) --- ## Monitoring & Alerting Integration ### Health Check Endpoint Expose job queue health for external monitoring: ```ruby # app/controllers/health_controller.rb class HealthController < ApplicationController skip_before_action :authenticate_user! # Public endpoint def jobs pending_count = SolidQueue::Job.pending.count failed_count = SolidQueue::Job.failed.count oldest_pending = oldest_pending_job_age status = if oldest_pending > 30 || failed_count > 100 :service_unavailable else :ok end render json: { status: status == :ok ? "healthy" : "degraded", pending_jobs: pending_count, failed_jobs: failed_count, oldest_pending_minutes: oldest_pending }, status: status end private def oldest_pending_job_age oldest = SolidQueue::Job.pending.order(:created_at).first return 0 unless oldest ((Time.current - oldest.created_at) / 60).round end end # config/routes.rb get '/health/jobs', to: 'health#jobs' ``` ### External Monitoring Setup ```bash # Uptime monitoring (Pingdom, UptimeRobot, etc.) GET https://yourapp.com/health/jobs # Expected response (healthy): { "status": "healthy", "pending_jobs": 42, "failed_jobs": 3, "oldest_pending_minutes": 2 } # Alert on: # - status != "healthy" # - failed_jobs > threshold # - oldest_pending_minutes > 30 ``` --- ## Common Operations ### Retry All Failed Jobs ```ruby # Rails console SolidQueue::Job.failed.find_each(&:retry!) # Or with Mission Control UI: # 1. Navigate to Failed Jobs tab # 2. Select all jobs # 3. Click "Retry Selected" ``` ### Discard Specific Failed Jobs ```ruby # Rails console - discard jobs older than 1 week SolidQueue::Job.failed .where("failed_at < ?", 1.week.ago) .delete_all # Or by job class SolidQueue::Job.failed .where(class_name: "ProblematicJob") .delete_all ``` ### Pause/Resume Queue Processing ```ruby # Not directly supported by SolidQueue # Instead, scale workers to 0 in queue.yml and restart # config/queue.yml (temporary) production: workers: - queues: [critical, mailers] threads: 5 processes: 0 # Paused ``` ### Monitor Specific Queue ```ruby # Rails console SolidQueue::Job.where(queue_name: "mailers").pending.count SolidQueue::Job.where(queue_name: "mailers").failed.count ``` --- ## Troubleshooting ### Dashboard Not Loading **Symptom:** 404 or routing error **Solutions:** 1. Verify gem is installed: `bundle list | grep mission_control` 2. Check routes: `rails routes | grep jobs` 3. Restart server after adding gem 4. Check authentication constraints aren't blocking access ### Authentication Loop/Redirect **Symptom:** Redirected to login repeatedly **Solutions:** 1. Verify user is logged in: `current_user` in console 2. Check authentication lambda: `user.admin?` returns true 3. Verify Devise configuration allows access to mounted engines 4. Check for conflicting before_action filters ### Slow Dashboard Performance **Symptom:** Dashboard takes >5s to load **Solutions:** 1. Clean up old finished jobs: ```ruby SolidQueue::Job.finished.where("finished_at < ?", 7.days.ago).delete_all ``` 2. Add database indexes (if not present): ```ruby add_index :solid_queue_jobs, [:queue_name, :status] add_index :solid_queue_jobs, [:status, :created_at] ``` 3. Reduce retention periods in initializer ### Jobs Not Appearing **Symptom:** Dashboard shows 0 jobs but jobs are running **Solutions:** 1. Verify SolidQueue is configured: `Rails.configuration.active_job.queue_adapter` 2. Check queue database connection in `config/database.yml` 3. Run queue migrations: `rails db:migrate:queue` 4. Verify jobs are using SolidQueue, not inline adapter --- ## Security Considerations ### Production Hardening 1. **Always require authentication:** ```ruby # ❌ NEVER do this in production mount MissionControl::Jobs::Engine, at: "/jobs" # ✅ Always authenticate authenticate :user, ->(user) { user.admin? } do mount MissionControl::Jobs::Engine, at: "/jobs" end ``` 2. **Filter sensitive parameters:** ```ruby config.filter_parameters = [:password, :token, :secret, :api_key] ``` 3. **Use HTTPS only:** ```ruby # config/environments/production.rb config.force_ssl = true ``` 4. **Limit admin access:** - Grant admin rights only to operations team - Audit admin user list regularly - Use role-based access for granular control 5. **Monitor access logs:** ```ruby # Track who accesses Mission Control class ApplicationController < ActionController::Base before_action :log_mission_control_access, if: :mission_control_request? private def mission_control_request? request.path.start_with?('/jobs') end def log_mission_control_access Rails.logger.info( "Mission Control accessed by #{current_user&.email} " \ "from #{request.remote_ip}" ) end end ``` --- ## Additional Resources - [Mission Control Jobs GitHub](https://github.com/rails/mission_control-jobs) - [SolidQueue Documentation](https://github.com/rails/solid_queue) - [Rails Active Job Guide](https://guides.rubyonrails.org/active_job_basics.html) - [Rails 8 Release Notes - Solid Stack](https://edgeguides.rubyonrails.org/8_0_release_notes.html)