Module: Gouda::Scheduler

Defined in:
lib/gouda/scheduler.rb

Defined Under Namespace

Classes: Entry

Class Method Summary collapse

Class Method Details

.build_scheduler_entries_list!(cron_table_hash = nil) ⇒ Object

Takes in a Hash formatted with cron entries in the format similar to good_job, and builds a table of scheduler entries. A scheduler entry references a particular job class name, the set of arguments to be passed to the job when performing it, and either the interval to repeat the job after or a cron pattern. This method does not insert the actual Workloads into the database but just builds the table of the entries. That table gets consulted when workloads finish to determine whether the workload that just ran was scheduled or ad-hoc, and whether the subsequent workload has to be enqueued.

If no table is given the method will attempt to read the table from Rails application config from ‘[:gouda]`.

The table is a Hash of entries, and the keys are the names of the workload to be enqueued - those keys are also used to ensure scheduled workloads only get scheduled once.

Parameters:

  • cron_table_hash (Hash) (defaults to: nil)

    a hash of the following shape: {

    download_invoices_every_minute: {
      cron: "* * * * *",
      class: "DownloadInvoicesJob",
      args: ["immediate"]
    }
    

    }



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/gouda/scheduler.rb', line 82

def self.build_scheduler_entries_list!(cron_table_hash = nil)
  Gouda.logger.info "Updating scheduled workload entries..."
  if cron_table_hash.blank?
    config_from_rails = Rails.application.config.try(:gouda)

    cron_table_hash = if config_from_rails.present?
      config_from_rails.dig(:cron).to_h if config_from_rails.dig(:enable_cron)
    elsif Gouda.config.enable_cron
      Gouda.config.cron
    end

    return unless cron_table_hash
  end

  defaults = {cron: nil, interval_seconds: nil, kwargs: nil, args: nil}
  @cron_table = cron_table_hash.map do |(name, cron_entry_params)|
    # `class` is a reserved keyword and a method that exists on every Ruby object so...
    cron_entry_params[:job_class] ||= cron_entry_params.delete(:class)
    params_with_defaults = defaults.merge(cron_entry_params)
    Entry.new(name: name, **params_with_defaults)
  end
end

.enqueue_next_scheduled_workload_for(finished_workload) ⇒ Object

Once a workload has finished (doesn’t matter whether it raised an exception or completed successfully), it is going to be passed to this method to enqueue the next scheduled workload

Parameters:

Returns:

  • void



111
112
113
114
115
116
117
118
119
# File 'lib/gouda/scheduler.rb', line 111

def self.enqueue_next_scheduled_workload_for(finished_workload)
  return unless finished_workload.scheduler_key

  timer_table = @cron_table.to_a.index_by(&:scheduler_key)
  timer_entry = timer_table[finished_workload.scheduler_key]
  return unless timer_entry

  Gouda.enqueue_jobs_via_their_adapters([timer_entry.build_active_job])
end

.entriesObject

Returns the list of entries of the scheduler which are currently known. Normally the scheduler will hold the list of entries loaded from the Rails config.



125
126
127
# File 'lib/gouda/scheduler.rb', line 125

def self.entries
  @cron_table || []
end

.upsert_workloads_from_entries_list!Object

Will upsert (‘INSERT … ON CONFLICT UPDATE`) workloads for all entries which are in the scheduler entries table (the table needs to be read or hydrated first using `build_scheduler_entries_list!`). This is done in a transaction. Any workloads which have been previously inserted from the scheduled entries, but no longer have a corresponding scheduler entry, will be deleted from the database. If there already are workloads with the corresponding scheduler key they will not be touched and will be performed with their previously-defined arguments.

Returns:

  • void



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/gouda/scheduler.rb', line 137

def self.upsert_workloads_from_entries_list!
  table_entries = @cron_table || []

  # Remove any cron keyed workloads which no longer match config-wise
  known_keys = table_entries.map(&:scheduler_key).uniq
  Gouda::Workload.transaction do
    Gouda::Workload.where.not(scheduler_key: known_keys).delete_all

    # Insert the next iteration for every "next" entry in the crontab.
    active_jobs_to_enqueue = table_entries.filter_map(&:build_active_job)
    Gouda.logger.info "#{active_jobs_to_enqueue.size} job(s) to enqueue from the scheduler."
    enqjobs = Gouda.enqueue_jobs_via_their_adapters(active_jobs_to_enqueue)
    Gouda.logger.info "#{enqjobs.size} scheduled job(s) enqueued."
  end
end