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.
219 220 221 222 223 224 225 226 227 228 |
# File 'lib/wurk/cron.rb', line 219 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.
217 218 219 |
# File 'lib/wurk/cron.rb', line 217 def klass @klass end |
#lid ⇒ Object (readonly)
Returns the value of attribute lid.
217 218 219 |
# File 'lib/wurk/cron.rb', line 217 def lid @lid end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
217 218 219 |
# File 'lib/wurk/cron.rb', line 217 def @options end |
#schedule ⇒ Object (readonly)
Returns the value of attribute schedule.
217 218 219 |
# File 'lib/wurk/cron.rb', line 217 def schedule @schedule end |
#tz ⇒ Object (readonly)
Returns the value of attribute tz.
217 218 219 |
# File 'lib/wurk/cron.rb', line 217 def tz @tz end |
Class Method Details
.from_redis(lid, hash) ⇒ Object
324 325 326 327 328 329 330 |
# File 'lib/wurk/cron.rb', line 324 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
242 243 244 |
# File 'lib/wurk/cron.rb', line 242 def args Array(@options['args']) end |
#history ⇒ Object
250 251 252 253 254 255 |
# File 'lib/wurk/cron.rb', line 250 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.
312 313 314 |
# File 'lib/wurk/cron.rb', line 312 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.
260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/wurk/cron.rb', line 260 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
305 306 307 |
# File 'lib/wurk/cron.rb', line 305 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).
297 298 299 300 301 302 303 |
# File 'lib/wurk/cron.rb', line 297 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
283 284 285 |
# File 'lib/wurk/cron.rb', line 283 def next_fire_at(from_epoch = ::Time.now.to_i) parser.next_fire_at(from_epoch, @tz) end |
#parser ⇒ Object
230 231 232 |
# File 'lib/wurk/cron.rb', line 230 def parser @parser ||= Parser.new(@schedule) end |
#paused? ⇒ Boolean
234 235 236 |
# File 'lib/wurk/cron.rb', line 234 def paused? @options['paused'].to_s == '1' || @options['paused'] == true end |
#queue ⇒ Object
238 239 240 |
# File 'lib/wurk/cron.rb', line 238 def queue @options['queue'] || 'default' end |
#retry_value ⇒ Object
246 247 248 |
# File 'lib/wurk/cron.rb', line 246 def retry_value @options.fetch('retry', true) end |
#to_redis_hash ⇒ Object
273 274 275 276 277 278 279 280 281 |
# File 'lib/wurk/cron.rb', line 273 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
316 317 318 319 320 321 322 |
# File 'lib/wurk/cron.rb', line 316 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 |