module QPush module Job class << self def included(base) _register_job(base) Server.log.info("* Job loaded: #{base.name}") end def _register_job(base) Server.jobs << base.name Server.redis { |c| c.sadd("#{QPush::Base::KEY}:jobs", base.name) } end end end module Server class << self def jobs @jobs ||= [] end end class Job < QPush::Base::Job include QPush::Server::ApiWrapper include ObjectValidator::Validate def initialize(options) super end def mark_success @failed = false @total_success += 1 end def mark_failed @failed = true @total_fail += 1 end def retry_job? @retry_max > @total_fail end def perform_job? @start_at < Time.now.to_i && @cron.empty? end def delay_job? (@start_at > Time.now.to_i && @cron.empty?) || cron_job? end def cron_job? @start_at < Time.now.to_i && !@cron.empty? end def dead_job? @total_fail >= @retry_max end def cron_at CronParser.new(@cron).next(Time.now).to_i rescue => e raise ServerError, e.message end def delay_until @cron.empty? ? @start_at : cron_at end def retry_at Time.now.to_i + ((@total_fail**4) + 15 + (rand(30) * (@total_fail + 1))) end end class JobValidator include ObjectValidator::Validator validates :klass, with: { proc: proc { |j| Server.jobs.include?(j.klass) }, msg: 'has not been defined' } validates :cron, with: { proc: proc { |j| j.cron.empty? ? true : CronParser.new(j.cron) }, msg: 'is not a valid expression' } validates :id, type: String validates :args, type: Hash validates :created_at, type: Integer validates :start_at, type: Integer validates :retry_max, type: Integer validates :total_fail, type: Integer validates :total_success, type: Integer end end end