Skip to content
Back to skills

Background Job

ASecurity

Generate background jobs for async tasks using Solid Queue. Use when user needs email sending, notifications, data processing, scheduled tasks, or says "send email in background", "process async", "create job", "schedule task", "queue work".

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
code-qualitygorubybashrailstestingdebuggingperformance

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add David-Li0406/meta-skill-evloving --skill background-job --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Background Job?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Background Job
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/david-li0406-background-job/badge)](https://www.skillsdirectory.com/skills/david-li0406-background-job)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: background-job
description: Generate background jobs for async tasks using Solid Queue. Use when user needs email sending, notifications, data processing, scheduled tasks, or says "send email in background", "process async", "create job", "schedule task", "queue work".
---

# Background Job Generator

Generate background jobs using Solid Queue for asynchronous task processing.

## Quick Start

```
Task Progress (copy and check off):
- [ ] 1. Identify async task needed
- [ ] 2. Create job class
- [ ] 3. Add job logic
- [ ] 4. Enqueue job from controller/model
- [ ] 5. Test job execution
- [ ] 6. Monitor queue dashboard
```

## Project Setup

**Solid Queue** (Rails 8 기본 탑재):
```ruby
# Gemfile (already included)
gem "solid_queue"

# config/queue.yml (already configured)
# Solid Queue runs in-process or separate worker
```

**Queue Dashboard**:
```ruby
# config/routes.rb
require "solid_queue/engine"
mount SolidQueue::Engine => "/queue"
```

## Job Structure

```
app/jobs/
├── application_job.rb      # Base class
├── email_job.rb            # Email sending
├── notification_job.rb     # Push notifications
├── data_import_job.rb      # Data processing
└── cleanup_job.rb          # Scheduled cleanup
```

## Basic Job Template

```ruby
# app/jobs/example_job.rb
class ExampleJob < ApplicationJob
  queue_as :default

  def perform(arg1, arg2)
    # Job logic here
    puts "Processing #{arg1} and #{arg2}"
  end
end
```

**Enqueue**:
```ruby
# Execute immediately (in background)
ExampleJob.perform_later("value1", "value2")

# Execute at specific time
ExampleJob.set(wait: 1.hour).perform_later("value1", "value2")
ExampleJob.set(wait_until: Date.tomorrow.noon).perform_later("value1", "value2")
```

## Common Job Types

### 1. Email Job

```ruby
# app/jobs/send_email_job.rb
class SendEmailJob < ApplicationJob
  queue_as :default

  retry_on Net::SMTPServerBusy, wait: 5.minutes, attempts: 3

  def perform(user_id, email_type)
    user = User.find(user_id)

    case email_type
    when "welcome"
      UserMailer.welcome_email(user).deliver_now
    when "notification"
      UserMailer.notification_email(user).deliver_now
    else
      raise ArgumentError, "Unknown email type: #{email_type}"
    end

    Rails.logger.info "Sent #{email_type} email to user #{user.id}"
  end
end
```

**Usage**:
```ruby
# In controller or model
SendEmailJob.perform_later(user.id, "welcome")
```

### 2. Notification Job

```ruby
# app/jobs/send_notification_job.rb
class SendNotificationJob < ApplicationJob
  queue_as :notifications

  def perform(notification_id)
    notification = Notification.find(notification_id)

    # Send push notification
    send_push_notification(notification)

    # Mark as sent
    notification.update(sent_at: Time.current)
  end

  private

  def send_push_notification(notification)
    # Integration with FCM, APNs, etc.
    Rails.logger.info "Sending notification #{notification.id}"
  end
end
```

### 3. Data Processing Job

```ruby
# app/jobs/process_import_job.rb
class ProcessImportJob < ApplicationJob
  queue_as :heavy

  # Retry with exponential backoff
  retry_on StandardError, wait: :exponentially_longer, attempts: 5

  def perform(file_path)
    CSV.foreach(file_path, headers: true) do |row|
      User.create!(
        email: row["email"],
        name: row["name"]
      )
    end

    # Cleanup temp file
    File.delete(file_path) if File.exist?(file_path)
  rescue StandardError => e
    Rails.logger.error "Import failed: #{e.message}"
    raise
  end
end
```

### 4. Scheduled Job (Cleanup)

```ruby
# app/jobs/cleanup_old_posts_job.rb
class CleanupOldPostsJob < ApplicationJob
  queue_as :maintenance

  def perform
    deleted_count = Post.where("created_at < ?", 1.year.ago)
                        .where(status: :draft)
                        .delete_all

    Rails.logger.info "Cleaned up #{deleted_count} old draft posts"
  end
end
```

**Schedule with cron**:
```ruby
# config/recurring.yml (Solid Queue)
production:
  cleanup_old_posts:
    class: CleanupOldPostsJob
    queue: maintenance
    schedule: "0 2 * * *" # 2 AM daily
```

## Queue Priority

```ruby
# app/jobs/high_priority_job.rb
class HighPriorityJob < ApplicationJob
  queue_as :urgent
  queue_with_priority 10 # Higher = more priority
end

# app/jobs/low_priority_job.rb
class LowPriorityJob < ApplicationJob
  queue_as :background
  queue_with_priority 1
end
```

**Queue Configuration** (config/queue.yml):
```yaml
production:
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: urgent
      threads: 3
      processes: 2
      polling_interval: 0.1
    - queues: default,notifications
      threads: 5
      processes: 3
    - queues: heavy,maintenance
      threads: 2
      processes: 1
```

## Error Handling

### Retry Strategies

```ruby
class RetryableJob < ApplicationJob
  # Retry specific errors
  retry_on ConnectionError, wait: 5.seconds, attempts: 3

  # Exponential backoff
  retry_on TimeoutError, wait: :exponentially_longer, attempts: 10

  # Custom retry logic
  retry_on CustomError do |job, exception|
    Rails.logger.error "Job #{job.class} failed: #{exception.message}"
    Sentry.capture_exception(exception)
  end

  # Discard on certain errors
  discard_on ActiveJob::DeserializationError

  def perform
    # Job logic
  end
end
```

### Error Callbacks

```ruby
class MonitoredJob < ApplicationJob
  around_perform :log_execution

  rescue_from StandardError do |exception|
    Rails.logger.error "Job failed: #{exception.message}"
    # Send to error tracking service
    Sentry.capture_exception(exception)
  end

  def perform
    # Job logic
  end

  private

  def log_execution
    start_time = Time.current
    Rails.logger.info "Starting job: #{self.class.name}"

    yield

    duration = Time.current - start_time
    Rails.logger.info "Job completed in #{duration}s"
  end
end
```

## Testing

```ruby
# test/jobs/send_email_job_test.rb
require "test_helper"

class SendEmailJobTest < ActiveJob::TestCase
  test "enqueues email job" do
    assert_enqueued_with(job: SendEmailJob, args: [users(:one).id, "welcome"]) do
      SendEmailJob.perform_later(users(:one).id, "welcome")
    end
  end

  test "sends welcome email" do
    assert_emails 1 do
      SendEmailJob.perform_now(users(:one).id, "welcome")
    end
  end

  test "handles missing user gracefully" do
    assert_raises(ActiveRecord::RecordNotFound) do
      SendEmailJob.perform_now(99999, "welcome")
    end
  end
end
```

## Monitoring

### Dashboard
```
Visit: http://localhost:3000/queue
- View pending jobs
- Monitor failed jobs
- See queue statistics
```

### Logging
```ruby
# config/environments/production.rb
config.active_job.logger = ActiveSupport::Logger.new("log/jobs.log")

# In jobs
Rails.logger.info "Job started: #{job_id}"
Rails.logger.error "Job failed: #{error_message}"
```

### Metrics
```ruby
# app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
  around_perform :track_performance

  private

  def track_performance
    start_time = Time.current
    yield
  ensure
    duration = Time.current - start_time
    Rails.logger.info "Job #{self.class.name} took #{duration}s"
    # Send to monitoring service (e.g., StatsD)
  end
end
```

## Best Practices

1. **Idempotent Jobs**: Jobs should be safe to run multiple times
2. **Small Arguments**: Pass IDs, not full objects
3. **Queue Separation**: Different queues for different priorities
4. **Error Handling**: Always handle errors gracefully
5. **Logging**: Log important events for debugging
6. **Timeouts**: Set appropriate timeouts for long-running jobs
7. **Monitoring**: Track job performance and failures

## Integration Examples

### After Create Hook
```ruby
# app/models/user.rb
class User < ApplicationRecord
  after_create_commit :send_welcome_email

  private

  def send_welcome_email
    SendEmailJob.perform_later(id, "welcome")
  end
end
```

### Controller Action
```ruby
# app/controllers/imports_controller.rb
class ImportsController < ApplicationController
  def create
    file = params[:file]
    temp_path = Rails.root.join("tmp", "import_#{Time.current.to_i}.csv")

    File.open(temp_path, "wb") do |f|
      f.write(file.read)
    end

    ProcessImportJob.perform_later(temp_path.to_s)

    redirect_to root_path, notice: "Import started. You'll be notified when complete."
  end
end
```

### Batch Processing
```ruby
# app/jobs/batch_notification_job.rb
class BatchNotificationJob < ApplicationJob
  queue_as :notifications

  def perform(user_ids)
    User.where(id: user_ids).find_each do |user|
      SendNotificationJob.perform_later(user.id)
    end
  end
end

# Usage
user_ids = User.active.pluck(:id)
user_ids.each_slice(100) do |batch|
  BatchNotificationJob.perform_later(batch)
end
```

## Troubleshooting

**Jobs not processing?**
```bash
# Check Solid Queue is running
bin/rails solid_queue:start

# Or in separate process
bundle exec rake solid_queue:work
```

**Failed jobs?**
- Check `/queue` dashboard
- Review logs: `tail -f log/jobs.log`
- Retry manually: `SolidQueue::Job.find(id).retry`

**Performance issues?**
- Increase worker threads in config/queue.yml
- Split heavy jobs into smaller ones
- Use queue priorities effectively

## Examples

- [Email Job Example](examples/send_email_job.rb)
- [Data Processing Example](examples/process_data_job.rb)
- [Scheduled Job Example](examples/cleanup_job.rb)

## Checklist

- [ ] Job class created in app/jobs/
- [ ] Inherits from ApplicationJob
- [ ] Queue specified with `queue_as`
- [ ] Error handling added (retry_on/discard_on)
- [ ] Enqueued from appropriate location
- [ ] Tested with perform_now
- [ ] Logged important events
- [ ] Monitored in /queue dashboard

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…