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

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Priority

Returns a new instance of Priority.



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

def initialize(value)
  super()
  value = self.class.names_to_priority[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)



196
197
198
199
200
201
202
# File 'lib/delayed/priority.rb', line 196

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

Class Attribute Details

.assign_at_midpoint=(value) ⇒ Object (writeonly)

Sets the attribute assign_at_midpoint

Parameters:

  • value

    the value to set the attribute assign_at_midpoint to.



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

def assign_at_midpoint=(value)
  @assign_at_midpoint = value
end

Instance Attribute Details

#valueObject (readonly)

Returns the value of attribute value.



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

def value
  @value
end

Class Method Details

.alertsObject



66
67
68
# File 'lib/delayed/priority.rb', line 66

def alerts
  @alerts || default_alerts
end

.alerts=(alerts) ⇒ Object



79
80
81
82
83
84
85
86
# File 'lib/delayed/priority.rb', line 79

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

.assign_at_midpoint?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/delayed/priority.rb', line 88

def assign_at_midpoint?
  @assign_at_midpoint || false
end

.namesObject



62
63
64
# File 'lib/delayed/priority.rb', line 62

def names
  @names || default_names
end

.names=(names) ⇒ Object



70
71
72
73
74
75
76
77
# File 'lib/delayed/priority.rb', line 70

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

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

.names_to_priorityObject



98
99
100
101
102
103
104
105
# File 'lib/delayed/priority.rb', line 98

def names_to_priority
  @names_to_priority ||=
    if assign_at_midpoint?
      names_to_midpoint_priority
    else
      names
    end
end

.rangesObject



92
93
94
95
96
# File 'lib/delayed/priority.rb', line 92

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



181
182
183
184
# File 'lib/delayed/priority.rb', line 181

def +(other)
  other = other.to_i if other.is_a?(self.class)
  self.class.new(to_i + other)
end

#-(other) ⇒ Object



176
177
178
179
# File 'lib/delayed/priority.rb', line 176

def -(other)
  other = other.to_i if other.is_a?(self.class)
  self.class.new(to_i - other)
end

#<=>(other) ⇒ Object



171
172
173
174
# File 'lib/delayed/priority.rb', line 171

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

#alert_ageObject



155
156
157
# File 'lib/delayed/priority.rb', line 155

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

#alert_attemptsObject



163
164
165
# File 'lib/delayed/priority.rb', line 163

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

#alert_run_timeObject



159
160
161
# File 'lib/delayed/priority.rb', line 159

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

#coerce(other) ⇒ Object



167
168
169
# File 'lib/delayed/priority.rb', line 167

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

#nameObject



151
152
153
# File 'lib/delayed/priority.rb', line 151

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

#to_dObject



186
187
188
# File 'lib/delayed/priority.rb', line 186

def to_d
  to_i.to_d
end