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.
274 275 276 277 278 279 280 281 282 283 |
# File 'lib/wurk/cron.rb', line 274 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.
272 273 274 |
# File 'lib/wurk/cron.rb', line 272 def klass @klass end |
#lid ⇒ Object (readonly)
Returns the value of attribute lid.
272 273 274 |
# File 'lib/wurk/cron.rb', line 272 def lid @lid end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
272 273 274 |
# File 'lib/wurk/cron.rb', line 272 def @options end |
#schedule ⇒ Object (readonly)
Returns the value of attribute schedule.
272 273 274 |
# File 'lib/wurk/cron.rb', line 272 def schedule @schedule end |
#tz ⇒ Object (readonly)
Returns the value of attribute tz.
272 273 274 |
# File 'lib/wurk/cron.rb', line 272 def tz @tz end |
Class Method Details
.from_redis(lid, hash) ⇒ Object
379 380 381 382 383 384 385 |
# File 'lib/wurk/cron.rb', line 379 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
297 298 299 |
# File 'lib/wurk/cron.rb', line 297 def args Array(@options['args']) end |
#history ⇒ Object
305 306 307 308 309 310 |
# File 'lib/wurk/cron.rb', line 305 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.
367 368 369 |
# File 'lib/wurk/cron.rb', line 367 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.
315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/wurk/cron.rb', line 315 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
360 361 362 |
# File 'lib/wurk/cron.rb', line 360 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).
352 353 354 355 356 357 358 |
# File 'lib/wurk/cron.rb', line 352 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
338 339 340 |
# File 'lib/wurk/cron.rb', line 338 def next_fire_at(from_epoch = ::Time.now.to_i) parser.next_fire_at(from_epoch, @tz) end |
#parser ⇒ Object
285 286 287 |
# File 'lib/wurk/cron.rb', line 285 def parser @parser ||= Parser.new(@schedule) end |
#paused? ⇒ Boolean
289 290 291 |
# File 'lib/wurk/cron.rb', line 289 def paused? @options['paused'].to_s == '1' || @options['paused'] == true end |
#queue ⇒ Object
293 294 295 |
# File 'lib/wurk/cron.rb', line 293 def queue @options['queue'] || 'default' end |
#retry_value ⇒ Object
301 302 303 |
# File 'lib/wurk/cron.rb', line 301 def retry_value @options.fetch('retry', true) end |
#to_redis_hash ⇒ Object
328 329 330 331 332 333 334 335 336 |
# File 'lib/wurk/cron.rb', line 328 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
371 372 373 374 375 376 377 |
# File 'lib/wurk/cron.rb', line 371 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 |