I believe this can be done without Middleware, but not sure if there is benefit to this method over the Middleware approaches. However, here is what I did:
NOTE: This approach assumes you are using Redis to queue your jobs
I give my jobs access to Redis via:
def redis
# You may need to disable SSL, depending on your environment, if so use this to do so:
# @redis = Redis.new(ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
@redis = Redis.new
end
# You can do this directly on your job, in your BaseJob or in a module / concern, reader's choice on implementation.
def retry_count
# retry_count is not present on the first attempt
redis_work_json.dig("payload","retry_count").then do |count|
# the correction factor will give us the retry counts that you would expect, since the actual counts lag by 1, as described in the other answers
# NOTE: nil.to_i => 0
(count.nil? ? 0 : 1).then { |correction_factor| count.to_i + correction_factor }
end
end
Helper Methods:
# convert the Redis data for the current job to JSON
def redis_work_json
# we may be in a race condition with Redis to save the key we are looking for
sleep(100) until redis.keys.any? { |key| key.include? ":work" }
redis.keys.each do |key|
next unless key.include? ":work"
return nested_redis_value_with_jid(key).then do |value|
next if value.nil?
json_from(value)
end
end
end
# find the data stored in Redis for the current Job
def nested_redis_value_with_jid(key)
# the work key will have a hash value so it needs to be fetched via Redis::Commands::Hashes
# hvals will skip the random key that Redis nested this hash in
# find the hash value that matches this job's jid
redis.hvals(key).find { |v| v.include?(jid) }
end
def flatten_json_str(str)
# This may seem gnarly but it allows `JSON.parse` to work at it's full potential
# instead of manually using JSON.parse on nested JSON strings in a loop
str.gsub(":\"{", ":{").gsub("}\",", "},").delete("\\")
end
def json_from(value)
JSON.parse(flatten_json_str(value))
end