Class: Wurk::Cron::Manager

Inherits:
Object
  • Object
show all
Defined in:
lib/wurk/cron.rb

Overview

Registration DSL. One Manager per config.periodic block; multiple blocks accumulate. mgr.tz= sets the default tz for subsequent register calls; per-call tz: overrides.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = nil, prune: true, deferred: false) ⇒ Manager

prune: false registers without superseding — ConfigTester uses it so validating a config never deletes a live loop. deferred: true holds every write until #apply!: config.periodic runs while the initializers load, before the app has finished configuring Redis, and in processes that never boot a server (Ent registers on startup).



476
477
478
479
480
481
482
483
484
# File 'lib/wurk/cron.rb', line 476

def initialize(config = nil, prune: true, deferred: false)
  @config = config
  @loops = []
  @tz = nil
  @prune = prune
  @deferred = deferred
  @authoritative = false
  @pruned_klasses = {}
end

Instance Attribute Details

#loops ⇒ Object (readonly)

Returns the value of attribute loops.



469
470
471
# File 'lib/wurk/cron.rb', line 469

def loops
  @loops
end

#tz ⇒ Object

Returns the value of attribute tz.



468
469
470
# File 'lib/wurk/cron.rb', line 468

def tz
  @tz
end

Instance Method Details

#apply! ⇒ Object

Writes the registrations held back since config.periodic ran, then prunes as #register and #authoritative! would have. Launcher#boot calls it; once applied, later registrations are written as they come.



514
515
516
517
518
519
520
521
522
# File 'lib/wurk/cron.rb', line 514

def apply!
  return self unless @deferred

  @deferred = false
  Cron.persist_all(@loops)
  prune_superseded(@loops.map(&:klass).uniq)
  prune_unregistered if @authoritative
  self
end

#authoritative! ⇒ Object

Call after the last registration to make this block the whole truth: every other loop in the cluster is deleted. Off unless asked because a process registering only part of the schedule would delete the rest, and because during a rolling deploy old and new code then disagree.



545
546
547
548
549
550
551
# File 'lib/wurk/cron.rb', line 545

def authoritative!
  return self unless @prune

  @authoritative = true
  prune_unregistered unless @deferred
  self
end

#grace=(seconds) ⇒ Object

How late a slot may fire, in seconds, instead of being skipped, for every loop of this cluster's pollers. Default MISSED_TICK_THRESHOLD. A process-wide setting and not a loop option, which would enter the loop's identity.

Coerced here, because the poller rescues per loop: a value it could not compare would stop every loop on every tick with nothing failing at boot.



492
493
494
495
496
497
498
499
# File 'lib/wurk/cron.rb', line 492

def grace=(seconds)
  grace = Float(seconds, exception: false)
  unless grace&.finite? && grace.positive?
    raise ArgumentError, "periodic grace must be a positive, finite number of seconds: #{seconds.inspect}"
  end

  (@config || Wurk.configuration)[:cron_grace] = grace
end

#load_file(path) ⇒ Object

The same shape from a YAML file (ERB allowed), like a config/schedule.yml.



535
536
537
538
539
# File 'lib/wurk/cron.rb', line 535

def load_file(path)
  require 'erb'
  require 'yaml'
  load_hash(::YAML.safe_load(::ERB.new(::File.read(path)).result, aliases: true) || {})
end

#load_hash(schedule) ⇒ Object

Registers a sidekiq-cron shaped schedule: { name => { cron:, class:, queue:, args:, retry:, tz:, status: } }, or a list of those carrying name:. Other keys (description, ...) are ignored. Raises ArgumentError on an entry with no cron or class, and on anything but a 5-field crontab.



528
529
530
531
532
# File 'lib/wurk/cron.rb', line 528

def load_hash(schedule)
  entries = schedule
  entries = schedule.map { |name, entry| { 'name' => name }.merge(entry.to_h) } if schedule.is_a?(::Hash)
  entries.map { |entry| register_entry(entry.to_h.transform_keys(&:to_s)) }
end

#register(cron, klass, **opts) ⇒ Object



501
502
503
504
505
506
507
508
509
# File 'lib/wurk/cron.rb', line 501

def register(cron, klass, **opts)
  klass_name = klass.is_a?(String) ? klass : klass.to_s
  tz = opts.delete(:tz) || @tz
  normalized = opts.transform_keys(&:to_s)
  loop_obj = Loop.new(schedule: cron, klass: klass_name, options: normalized, tz: tz)
  @loops << loop_obj
  persist(loop_obj) unless @deferred
  loop_obj
end