Class: Wurk::Cron::Loop
- Inherits:
-
Object
- Object
- Wurk::Cron::Loop
- Defined in:
- lib/wurk/cron.rb
Overview
One registered loop. Carries identity, schedule, options, and the
cached parser. Immutable after register! — re-registering the same
(schedule, klass, options) triple no-ops.
Instance Attribute Summary collapse
-
#klass ⇒ Object
readonly
Returns the value of attribute klass.
-
#lid ⇒ Object
readonly
Returns the value of attribute lid.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#schedule ⇒ Object
readonly
Returns the value of attribute schedule.
-
#tz ⇒ Object
readonly
Returns the value of attribute tz.
Class Method Summary collapse
Instance Method Summary collapse
- #args ⇒ Object
- #history ⇒ Object
-
#hourly? ⇒ Boolean
True when the schedule fires every hour (hour field is
*/ all 24). -
#initialize(schedule:, klass:, options: {}, tz: nil, lid: nil) ⇒ Loop
constructor
A new instance of Loop.
-
#last_fired_at ⇒ Object
Epoch of the most recent fire, or nil if this loop has never run.
- #local_components(epoch) ⇒ Object
-
#next_fire_after(fired_slot, advance_from = fired_slot) ⇒ Object
Next scheduled fire after firing at
fired_slot. - #next_fire_at(from_epoch = ::Time.now.to_i) ⇒ Object
- #parser ⇒ Object
- #paused? ⇒ Boolean
- #queue ⇒ Object
- #retry_value ⇒ Object
- #to_redis_hash ⇒ Object
- #tz_name ⇒ Object
Constructor Details
#initialize(schedule:, klass:, options: {}, tz: nil, lid: nil) ⇒ Loop
Returns a new instance of Loop.
273 274 275 276 277 278 279 280 281 282 |
# File 'lib/wurk/cron.rb', line 273 def initialize(schedule:, klass:, options: {}, tz: nil, lid: nil) raise ArgumentError, 'klass must be a String' unless klass.is_a?(String) && !klass.empty? @schedule = schedule @klass = klass @options = () @tz = tz @parser = Parser.new(schedule) @lid = lid || Cron.lid(schedule, klass, @options) end |
Instance Attribute Details
#klass ⇒ Object (readonly)
Returns the value of attribute klass.
271 272 273 |
# File 'lib/wurk/cron.rb', line 271 def klass @klass end |
#lid ⇒ Object (readonly)
Returns the value of attribute lid.
271 272 273 |
# File 'lib/wurk/cron.rb', line 271 def lid @lid end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
271 272 273 |
# File 'lib/wurk/cron.rb', line 271 def @options end |
#schedule ⇒ Object (readonly)
Returns the value of attribute schedule.
271 272 273 |
# File 'lib/wurk/cron.rb', line 271 def schedule @schedule end |
#tz ⇒ Object (readonly)
Returns the value of attribute tz.
271 272 273 |
# File 'lib/wurk/cron.rb', line 271 def tz @tz end |
Class Method Details
.from_redis(lid, hash) ⇒ Object
378 379 380 381 382 383 384 |
# File 'lib/wurk/cron.rb', line 378 def self.from_redis(lid, hash) h = hash.is_a?(Array) ? hash.each_slice(2).to_h : hash opts = h['options'] ? JSON.parse(h['options']) : {} opts['paused'] = '1' if h['paused'] == '1' tz = h['tz'].to_s.empty? ? nil : h['tz'] new(lid: lid, schedule: h['schedule'], klass: h['klass'], options: opts, tz: tz) end |
Instance Method Details
#args ⇒ Object
296 297 298 |
# File 'lib/wurk/cron.rb', line 296 def args Array(@options['args']) end |
#history ⇒ Object
304 305 306 307 308 309 |
# File 'lib/wurk/cron.rb', line 304 def history Wurk.redis do |c| entries = c.call('LRANGE', "#{HISTORY_PREFIX}#{@lid}", 0, -1) entries.map { |e| JSON.parse(e) } end end |
#hourly? ⇒ Boolean
True when the schedule fires every hour (hour field is * / all 24).
Only such a schedule legitimately runs in both repeated fall-back hours;
a fixed-hour slot (even a multi-hour one like 0 1,2 * * *) must dedup.
366 367 368 |
# File 'lib/wurk/cron.rb', line 366 def hourly? parser.fields[1].size == 24 end |
#last_fired_at ⇒ Object
Epoch of the most recent fire, or nil if this loop has never run. The
poller LPUSHes [fired_at, jid] tuples so index 0 is newest; we read
only that one entry instead of the whole history list.
314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/wurk/cron.rb', line 314 def last_fired_at raw = Wurk.redis { |c| c.call('LINDEX', "#{HISTORY_PREFIX}#{@lid}", 0) } return nil if raw.nil? entry = JSON.parse(raw) return nil unless entry.is_a?(Array) fired_at = entry[0] fired_at.is_a?(Numeric) ? fired_at.to_i : nil rescue JSON::ParserError nil end |
#local_components(epoch) ⇒ Object
359 360 361 |
# File 'lib/wurk/cron.rb', line 359 def local_components(epoch) parser.local_components(epoch, @tz) end |
#next_fire_after(fired_slot, advance_from = fired_slot) ⇒ Object
Next scheduled fire after firing at fired_slot. advance_from (the
poller passes now) is the search origin so missed ticks are not
backfilled. The DST guard: on a fall-back the clock rolls back, so the
same wall-clock minute recurs at a later UTC instant. That equality
alone isn't enough to skip — it would also drop a legitimate hourly run
whose repeated hour is real. So we only suppress the duplicate for a
FIXED daily slot; an hourly schedule (wildcard hour) keeps both fold
hours. A sub-hourly schedule (e.g. */30) lands on a different minute,
so the equality never even triggers. Spec: no DST double-fire without
skipping legitimate hourly runs (sidekiq-ent.md §2.6).
351 352 353 354 355 356 357 |
# File 'lib/wurk/cron.rb', line 351 def next_fire_after(fired_slot, advance_from = fired_slot) nxt = next_fire_at(advance_from) return nxt if nxt.nil? || local_components(nxt) != local_components(fired_slot) return nxt if hourly? # repeated fall-back hour is a genuine second run next_fire_at(nxt) # fixed daily slot: drop the fold duplicate end |
#next_fire_at(from_epoch = ::Time.now.to_i) ⇒ Object
337 338 339 |
# File 'lib/wurk/cron.rb', line 337 def next_fire_at(from_epoch = ::Time.now.to_i) parser.next_fire_at(from_epoch, @tz) end |
#parser ⇒ Object
284 285 286 |
# File 'lib/wurk/cron.rb', line 284 def parser @parser ||= Parser.new(@schedule) end |
#paused? ⇒ Boolean
288 289 290 |
# File 'lib/wurk/cron.rb', line 288 def paused? @options['paused'].to_s == '1' || @options['paused'] == true end |
#queue ⇒ Object
292 293 294 |
# File 'lib/wurk/cron.rb', line 292 def queue @options['queue'] || 'default' end |
#retry_value ⇒ Object
300 301 302 |
# File 'lib/wurk/cron.rb', line 300 def retry_value @options.fetch('retry', true) end |
#to_redis_hash ⇒ Object
327 328 329 330 331 332 333 334 335 |
# File 'lib/wurk/cron.rb', line 327 def to_redis_hash { 'schedule' => @schedule, 'klass' => @klass, 'options' => Wurk.dump_json(@options), 'tz' => tz_name.to_s, 'paused' => paused? ? '1' : '0' } end |
#tz_name ⇒ Object
370 371 372 373 374 375 376 |
# File 'lib/wurk/cron.rb', line 370 def tz_name return nil if @tz.nil? return @tz.name if @tz.respond_to?(:name) return @tz.identifier if @tz.respond_to?(:identifier) @tz.to_s end |