Class: Delayed::Priority

Inherits:
Numeric
  • Object
show all
Defined in:
lib/delayed/priority.rb

Constant Summary collapse

DEFAULT_NAMES =

A Delayed::Priority represents a value that exists within a named range. Here are the default ranges and their names:

0-9: interactive

10-19: user_visible 20-29: eventual

30+: reporting

Ranges can be customized. They must be positive and must include a name for priority >= 0. The following config will produce ranges 0-99 (high), 100-499 (medium) and 500+ (low):

> Delayed::Priority.names = { high: 0, medium: 100, low: 500 }

{
  interactive: 0, # These jobs will actively hinder end-user interactions until they are complete, e.g. work behind a loading spinner
  user_visible: 10, # These jobs have end-user-visible side effects that will not obviously impact customers, e.g. welcome emails
  eventual: 20, # These jobs affect business process that are tolerant to some degree of queue backlog, e.g. syncing with other services
  reporting: 30, # These jobs are for processes that can complete on a slower timeline, e.g. daily report generation
}.freeze
DEFAULT_ALERTS =

Priorities can be mapped to alerting thresholds for job age (time since run_at), runtime, and attempts. These thresholds can be used to emit events or metrics. Here are the default values (for the default priorities):

Age Alerts ==========

 interactive: 1 minute
user_visible: 3 minutes
    eventual: 1.5 hours
   reporting: 4 hours

Run Time Alerts ======

 interactive: 30 seconds
user_visible: 90 seconds
    eventual: 5 minutes
   reporting: 10 minutes

Attempts Alerts =====

 interactive: 3 attempts
user_visible: 5 attempts
    eventual: 8 attempts
   reporting: 8 attempts

Alerting thresholds can be customized. The keys must match ‘Delayed::Priority.names`.

Delayed::Priority.alerts = {

high: { age: 30.seconds, run_time: 15.seconds, attempts: 3 },
medium: { age: 2.minutes, run_time: 1.minute, attempts: 6 },
low: { age: 10.minutes, run_time: 2.minutes, attempts: 9 },

}

{
  interactive: { age: 1.minute, run_time: 30.seconds, attempts: 3 },
  user_visible: { age: 3.minutes, run_time: 90.seconds, attempts: 5 },
  eventual: { age: 1.5.hours, run_time: 5.minutes, attempts: 8 },
  reporting: { age: 4.hours, run_time: 10.minutes, attempts: 8 },
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Priority

Returns a new instance of Priority.



119
120
121
122
123
# File 'lib/delayed/priority.rb', line 119

def initialize(value)
  super()
  value = self.class.names[value] if value.is_a?(Symbol)
  @value = value.to_i
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args) ⇒ Object (private)



156
157
158
159
160
161
162
# File 'lib/delayed/priority.rb', line 156

def method_missing(method_name, *args)
  if method_name.to_s.end_with?('?') && self.class.names.key?(method_name.to_s[0..-2].to_sym)
    method_name.to_s[0..-2] == to_s
  else
    super
  end
end

Instance Attribute Details

#valueObject (readonly)

Returns the value of attribute value.



114
115
116
# File 'lib/delayed/priority.rb', line 114

def value
  @value
end

Class Method Details

.alertsObject



64
65
66
# File 'lib/delayed/priority.rb', line 64

def alerts
  @alerts || default_alerts
end

.alerts=(alerts) ⇒ Object



76
77
78
79
80
81
82
83
# File 'lib/delayed/priority.rb', line 76

def alerts=(alerts)
  if alerts
    unknown_names = alerts.keys - names.keys
    raise "unknown priority name(s): #{unknown_names}" if unknown_names.any?
  end

  @alerts = alerts&.sort_by { |k, _| names.keys.index(k) }&.to_h
end

.namesObject



60
61
62
# File 'lib/delayed/priority.rb', line 60

def names
  @names || default_names
end

.names=(names) ⇒ Object



68
69
70
71
72
73
74
# File 'lib/delayed/priority.rb', line 68

def names=(names)
  raise "must include a name for priority >= 0" if names && !names.value?(0)

  @ranges = nil
  @alerts = nil
  @names = names&.sort_by(&:last)&.to_h&.transform_values { |v| new(v) }
end

.rangesObject



85
86
87
88
89
# File 'lib/delayed/priority.rb', line 85

def ranges
  @ranges ||= names.zip(names.except(names.keys.first)).each_with_object({}) do |((name, lower), (_, upper)), obj|
    obj[name] = (lower...(upper || Float::INFINITY))
  end
end

Instance Method Details

#<=>(other) ⇒ Object



145
146
147
148
# File 'lib/delayed/priority.rb', line 145

def <=>(other)
  other = other.to_i if other.is_a?(self.class)
  to_i <=> other
end

#alert_ageObject



129
130
131
# File 'lib/delayed/priority.rb', line 129

def alert_age
  self.class.alerts.dig(name, :age)
end

#alert_attemptsObject



137
138
139
# File 'lib/delayed/priority.rb', line 137

def alert_attempts
  self.class.alerts.dig(name, :attempts)
end

#alert_run_timeObject



133
134
135
# File 'lib/delayed/priority.rb', line 133

def alert_run_time
  self.class.alerts.dig(name, :run_time)
end

#coerce(other) ⇒ Object



141
142
143
# File 'lib/delayed/priority.rb', line 141

def coerce(other)
  [self.class.new(other), self]
end

#nameObject



125
126
127
# File 'lib/delayed/priority.rb', line 125

def name
  @name ||= self.class.ranges.find { |(_, r)| r.include?(to_i) }&.first
end