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.
261 262 263 264 265 266 267 268 269 270 |
# File 'lib/wurk/cron.rb', line 261 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.
259 260 261 |
# File 'lib/wurk/cron.rb', line 259 def klass @klass end |
#lid ⇒ Object (readonly)
Returns the value of attribute lid.
259 260 261 |
# File 'lib/wurk/cron.rb', line 259 def lid @lid end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
259 260 261 |
# File 'lib/wurk/cron.rb', line 259 def @options end |
#schedule ⇒ Object (readonly)
Returns the value of attribute schedule.
259 260 261 |
# File 'lib/wurk/cron.rb', line 259 def schedule @schedule end |
#tz ⇒ Object (readonly)
Returns the value of attribute tz.
259 260 261 |
# File 'lib/wurk/cron.rb', line 259 def tz @tz end |
Class Method Details
.from_redis(lid, hash) ⇒ Object
366 367 368 369 370 371 372 |
# File 'lib/wurk/cron.rb', line 366 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
284 285 286 |
# File 'lib/wurk/cron.rb', line 284 def args Array(@options['args']) end |
#history ⇒ Object
292 293 294 295 296 297 |
# File 'lib/wurk/cron.rb', line 292 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.
354 355 356 |
# File 'lib/wurk/cron.rb', line 354 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.
302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/wurk/cron.rb', line 302 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
347 348 349 |
# File 'lib/wurk/cron.rb', line 347 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).
339 340 341 342 343 344 345 |
# File 'lib/wurk/cron.rb', line 339 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
325 326 327 |
# File 'lib/wurk/cron.rb', line 325 def next_fire_at(from_epoch = ::Time.now.to_i) parser.next_fire_at(from_epoch, @tz) end |
#parser ⇒ Object
272 273 274 |
# File 'lib/wurk/cron.rb', line 272 def parser @parser ||= Parser.new(@schedule) end |
#paused? ⇒ Boolean
276 277 278 |
# File 'lib/wurk/cron.rb', line 276 def paused? @options['paused'].to_s == '1' || @options['paused'] == true end |
#queue ⇒ Object
280 281 282 |
# File 'lib/wurk/cron.rb', line 280 def queue @options['queue'] || 'default' end |
#retry_value ⇒ Object
288 289 290 |
# File 'lib/wurk/cron.rb', line 288 def retry_value @options.fetch('retry', true) end |
#to_redis_hash ⇒ Object
315 316 317 318 319 320 321 322 323 |
# File 'lib/wurk/cron.rb', line 315 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
358 359 360 361 362 363 364 |
# File 'lib/wurk/cron.rb', line 358 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 |