9.2 KiB
SolidQueue, SolidCache, and SolidCable Setup
This application uses Rails 8's built-in background job processing, caching, and ActionCable features with separate dedicated databases.
Database Configuration
We use separate databases for the main application, SolidQueue, SolidCache, and SolidCable. This ensures complete separation and avoids any conflicts or performance issues.
In config/database.yml, we have the following setup:
development:
primary:
database: db/development.sqlite3
queue:
database: db/development-queue.sqlite3
cache:
database: db/development-cache.sqlite3
cable:
database: db/development-cable.sqlite3
test:
primary:
database: db/test.sqlite3
queue:
database: db/test-queue.sqlite3
cache:
database: db/test-cache.sqlite3
cable:
database: db/test-cable.sqlite3
production:
primary:
database: <%= ENV['WRESTLINGDEV_DB_NAME'] %>
queue:
database: <%= ENV['WRESTLINGDEV_DB_NAME'] %>-queue
cache:
database: <%= ENV['WRESTLINGDEV_DB_NAME'] %>-cache
cable:
database: <%= ENV['WRESTLINGDEV_DB_NAME'] %>-cable
Migration Structure
Migrations for each database are stored in their respective directories:
- Main application migrations:
db/migrate/ - SolidQueue migrations:
db/queue/migrate/ - SolidCache migrations:
db/cache/migrate/ - SolidCable migrations:
db/cable/migrate/
Running Migrations
When deploying the application, you need to run migrations for each database separately:
# Run main application migrations
rails db:migrate
# Run SolidQueue migrations
rails db:migrate:queue
# Run SolidCache migrations
rails db:migrate:cache
# Run SolidCable migrations
rails db:migrate:cable
Environment Configuration
In the environment configuration files (config/environments/*.rb), we've configured the paths for migrations and set up the appropriate adapters:
# SolidCache configuration
config.cache_store = :solid_cache_store
config.paths["db/migrate"] << "db/cache/migrate"
# SolidQueue configuration
config.active_job.queue_adapter = :solid_queue
config.paths["db/migrate"] << "db/queue/migrate"
# ActionCable configuration
config.paths["db/migrate"] << "db/cable/migrate"
The database connections are configured in their respective YAML files:
config/cache.yml
production:
database: cache
# other options...
config/queue.yml
production:
database: queue
# other options...
config/cable.yml
production:
adapter: solid_cable
database: cable
# other options...
SolidQueue Configuration
SolidQueue is used for background job processing in all environments except test. The application is configured to run jobs as follows:
Development and Production
In both development and production environments, SolidQueue is configured to process jobs asynchronously. This provides consistent behavior across environments while maintaining performance.
Test
In the test environment only, jobs are executed synchronously using the inline adapter. This makes testing more predictable and avoids the need for separate worker processes during tests.
Configuration is in config/initializers/solid_queue.rb:
# Configure ActiveJob queue adapter based on environment
if Rails.env.test?
# In test, use inline adapter for simplicity and predictability
Rails.application.config.active_job.queue_adapter = :inline
else
# In development and production, use solid_queue with async execution
Rails.application.config.active_job.queue_adapter = :solid_queue
# Configure for regular async processing
Rails.application.config.active_job.queue_adapter_options = {
execution_mode: :async,
logger: Rails.logger
}
end
Running with Puma
By default, the application is configured to run SolidQueue workers within the Puma processes. This is done by setting the SOLID_QUEUE_IN_PUMA environment variable to true in the production Dockerfile, which enables the Puma plugin for SolidQueue.
This means you don't need to run separate worker processes in production - the same Puma processes that handle web requests also handle background jobs. This simplifies deployment and reduces resource requirements.
The application uses an intelligent auto-scaling configuration for SolidQueue when running in Puma:
- Auto Detection: The Puma configuration automatically detects available CPU cores and memory
- Worker Scaling: Puma workers are calculated based on available memory and CPU cores
- SolidQueue Integration: When enabled, SolidQueue simply runs within the Puma process
You can enable SolidQueue in Puma by setting:
SOLID_QUEUE_IN_PUMA=true
In config/puma.rb:
# Run the Solid Queue supervisor inside of Puma for single-server deployments
if ENV["SOLID_QUEUE_IN_PUMA"] == "true" && !Rails.env.test?
# Simply load the SolidQueue plugin with default settings
plugin :solid_queue
# Log that SolidQueue is enabled
puts "SolidQueue plugin enabled in Puma"
end
On startup, you'll see a log message confirming that SolidQueue is enabled in Puma.
Job Owner Tracking
Jobs in this application include metadata about their owner (typically a tournament) to allow tracking and displaying job status to tournament directors. Each job includes:
job_owner_id: Usually the tournament IDjob_owner_type: A description of the job (e.g., "Create a backup")
This information is serialized with the job and can be used to filter and display jobs on tournament pages.
Job Pattern: Simplified Job Enqueuing
Our job classes follow a simplified pattern that works consistently across all environments:
- Service classes always use
perform_laterto enqueue jobs - The execution mode is determined centrally by the ActiveJob adapter configuration
- Each job finds the needed records and calls the appropriate method on the service class or model
Example service class method:
def create_backup
# Set up job owner information for tracking
job_owner_id = @tournament.id
job_owner_type = "Create a backup"
# Use perform_later which will execute based on centralized adapter config
TournamentBackupJob.perform_later(@tournament.id, @reason, job_owner_id, job_owner_type)
end
Example job class:
class TournamentBackupJob < ApplicationJob
queue_as :default
# For storing job owner metadata
attr_accessor :job_owner_id, :job_owner_type
# For execution via job queue with IDs
def perform(tournament_id, reason, job_owner_id = nil, job_owner_type = nil)
# Store job owner metadata
self.job_owner_id = job_owner_id
self.job_owner_type = job_owner_type
# Find the record
tournament = Tournament.find_by(id: tournament_id)
return unless tournament
# Create the service class and call the raw method
TournamentBackupService.new(tournament, reason).create_backup_raw
end
end
Job Classes
The following job classes are available:
AdvanceWrestlerJob: For advancing wrestlers in bracketsTournamentBackupJob: For creating tournament backupsWrestlingdevImportJob: For importing from wrestlingdevGenerateTournamentMatchesJob: For generating tournament matchesCalculateSchoolScoreJob: For calculating school scores
Job Status
Jobs in this application can have the following statuses:
-
Running: Job is currently being executed. This is determined by checking if a record exists in the
solid_queue_claimed_executionstable for the job. -
Scheduled: Job is scheduled to run at a future time. This is determined by checking if
scheduled_atis in the future. -
Error: Job has failed. This is determined by:
- Checking if a record exists in the
solid_queue_failed_executionstable for the job - Checking if
failed_atis present
- Checking if a record exists in the
-
Completed: Job has finished successfully. This is determined by checking if
finished_atis present and no error records exist. -
Pending: Job is waiting to be picked up by a worker. This is the default status when none of the above conditions are met.
Testing Job Status
To help with testing the job status display in the UI, several rake tasks are provided:
# Create a test "Running" job for the first tournament
rails jobs:create_running
# Create a test "Completed" job for the first tournament
rails jobs:create_completed
# Create a test "Error" job for the first tournament
rails jobs:create_failed
Troubleshooting
If you encounter issues with SolidQueue or the separate databases:
-
Make sure all databases exist:
CREATE DATABASE IF NOT EXISTS wrestlingtourney; CREATE DATABASE IF NOT EXISTS wrestlingtourney-queue; CREATE DATABASE IF NOT EXISTS wrestlingtourney-cache; CREATE DATABASE IF NOT EXISTS wrestlingtourney-cable; -
Ensure all migrations have been run for each database.
-
Check that environment configurations properly connect to the right databases.
-
Verify that the database user has appropriate permissions for all databases.
-
If jobs aren't processing in production, check that
SOLID_QUEUE_IN_PUMAis set totruein your environment.