resque-scheduler

repository·master·Indexed 23 days ago

https://github.com/resque/resque-scheduler

An extension for Resque that adds support for recurring (cron-like) and delayed (one-off future) job scheduling. It provides capabilities for static schedules via YAML, dynamic scheduling via API, and integration with resque-web and Rake.

Tokens
6.4K
Snippets
10
Records
51
Agent score
83%

What's inside resque-scheduler

  1. Configure dynamic schedules

    master

    Dynamic schedules can be set, updated, or removed programmatically while the scheduler is running without requiring a restart.

    To enable dynamic scheduling, you must set Resque::Scheduler.dynamic = true during initialization.

    Dynamic schedules can optionally persist across scheduler restarts by setting the persist: true option in the configuration hash.

    # Enabling dynamic scheduling
    Resque::Scheduler.dynamic = true
    
    # Setting a dynamic schedule
    name = 'send_emails'
    config = {}
    config[:class] = 'SendEmail'
    config[:args] = 'POC email subject'
    config[:every] = ['1h', {first_in: 5.minutes}]
    config[:persist] = true
    Resque.set_schedule(name, config)
    
    # Removing a dynamic schedule
    Resque.remove_schedule(name)
  2. Configure Rake integration for resque-scheduler

    master

    To integrate resque-scheduler with Rake, you should add the resque:scheduler task. This task depends on resque:setup and resque:setup_schedule. You must load your schedule (usually from a YAML file) and require your job classes within the setup_schedule task.

    # Resque tasks
    require 'resque/tasks'
    require 'resque/scheduler/tasks'
    
    namespace :resque do
      task :setup do
        require 'resque'
        # Configure your Redis connection here
        Resque.redis = 'localhost:6379'
      end
    
      task :setup_schedule => :setup do
        require 'resque-scheduler'
    
        # Enable dynamic scheduling if needed (>= 2.0.0)
        # Resque::Scheduler.dynamic = true
    
        # Load your schedule hash (YAML is common)
        Resque.schedule = YAML.load_file('your_resque_schedule.yml')
    
        # Require your job classes
        require 'jobs'
      end
    
      task :scheduler => :setup_schedule
    end
  3. Add resque-scheduler tabs to resque-web

    master

    To view and manage the schedule and delayed queue in the resque-web UI, you must include the resque-scheduler plugin and the server extension in your resque-web configuration file.

    # In your resque-web config file
    require 'resque'
    Resque.redis = "redis_server:6379"
    
    require 'resque-scheduler'
    require 'resque/scheduler/server'
    
    # Load the schedule so the tabs are populated
    Resque.schedule = YAML.load_file(File.join(RAILS_ROOT, 'config/resque_schedule.yml'))

    Then run resque-web pointing to that file:

    resque-web ~/yourapp/config/resque_config.rb
  4. Run the resque-scheduler process

    master

    The scheduler process is responsible for queueing scheduled items and polling the delayed queue. This process is intended to run continuously.

    Use the Rake task:

    rake resque:scheduler

    Or, if you need to load your application environment first:

    rake environment resque:scheduler

    Alternatively, use the standalone executable:

    resque-scheduler --help
  5. Implement a workaround for lost dynamic schedules on restart

    master

    By default, resque-scheduler wipes dynamically added jobs when the process restarts. To prevent this, you can implement a hybrid scheduling strategy:

    1. Dynamic Schedule: Add jobs dynamically during application events (e.g., when a new user is created, schedule a daily email job using Resque::Scheduler.set_schedule).
    2. Static Schedule: Define a recurring job in your static schedule (e.g., running hourly) that acts as a 'reconciliation' task. This task should check your primary database for any missing dynamic schedules and recreate them using Resque::Scheduler.set_schedule.

    This ensures that even if Redis data is lost or the scheduler restarts, the static job will eventually restore the necessary dynamic schedules based on the source of truth in your database.

  6. Configure static scheduled (recurring) jobs

    master

    Static schedules are defined in a YAML file and loaded at startup. They function like cron jobs. The schedule is a hash where keys are job names and values are configuration hashes.

    Supported frequency formats include standard cron syntax (including 6-parameter cron for seconds) and rufus-scheduler's every syntax.

    # Example YAML schedule
    CancelAbandonedOrders:
      cron: "*/5 * * * *"
    
    queue_documents_for_indexing:
      cron: "0 0 * * *"
      class: "QueueDocuments"
      queue: high
      args:
        foo: "bar"
        a: "b"
      parameters:
        foo:
          description: "value of foo"
          default: "baz"
    
    clear_leaderboards_moderator:
      every:
        - "30s"
        - :first_in: '120s'
      class: "CheckDaemon"
      queue: daemons
  7. Filter scheduled jobs by environment

    master

    Jobs in the schedule can be restricted to specific environments using the rails_env or env keys in the job configuration.

    A job will only be scheduled if the configured environment matches the current Resque::Scheduler.env.

    Example configuration structure:

    my_job:
      class: MyWorker
      every: 1h
      env: production,staging

    If env is a comma-separated string, the scheduler checks if the current environment is included in that list.

  8. Implement scheduling hooks in Resque jobs

    master

    You can control the behavior of scheduled jobs by defining specific hook methods within your job class. resque-scheduler looks for methods matching certain patterns to execute logic before or after a job is enqueued via the scheduler.

    Hook Types

    • before_schedule(*args): Executed before the job is enqueued. Crucially, if this method returns false, the job will not be enqueued.
    • after_schedule(*args): Executed after the job has been successfully enqueued.
    • before_delayed_enqueue(*args): Executed before a job is enqueued using a delayed mechanism.

    All hook methods receive the same arguments that are passed to the job itself.

  9. How the scheduler master lock works

    master

    To run redundant resque-scheduler processes without queuing the same scheduled (cron-like) jobs multiple times, the system uses a distributed master lock.

    The Locking Mechanism

    1. Acquisition: Each process attempts to acquire the master lock via SETNX in Redis.
    2. Expiration & Renewal: Once a process becomes the master, it sets an expiration (defaulting to 3 minutes). The master process continually updates this expiration during its loops and when jobs are loaded from rufus-scheduler to ensure it remains the master.
    3. Failover: If the master fails to update the expiration for the duration of the timeout, the key expires, and another process can claim the lock.

    Trade-offs and Clock Drift

    The timeout duration represents a trade-off between job reliability and duplication prevention:

    • Higher Timeout: Reduces the risk of jobs firing twice during a master change (even with significant clock drift between machines), but increases the window where no jobs are queued if the master fails.
    • Lower Timeout: Reduces the window of missed jobs during failover, but increases the risk of jobs being queued twice if machine clocks are not perfectly synchronized.

    Note: This locking logic primarily affects scheduled (cron-like) jobs. Delayed jobs (created via enqueue_at/enqueue_in) are not at risk of being lost or skipped, as a new master will eventually process all ready delayed jobs regardless of when it comes online.

  10. Integrate resque-scheduler into Resque::Server

    master
    To add the scheduler management interface (tabs for 'Schedule' and 'Delayed') to your existing Resque web interface, include Resque::Scheduler::Server into your Resque::Server class. This automatically registers several HTTP routes for managing schedules, requeueing jobs, and inspecting delayed jobs.
  11. Use an initializer with resque-scheduler

    master

    You can provide a custom Ruby file to be loaded before the scheduler starts using the --initializer-path (or -I) flag. This is useful for setting up specific configurations or environment requirements before the main scheduler loop begins.

    resque-scheduler --initializer-path /path/to/my_initializer.rb