From 989106dcb128e32fe6c035a6434087508e2c639e Mon Sep 17 00:00:00 2001 From: Nick Elser Date: Wed, 6 May 2015 23:23:45 -0700 Subject: [PATCH] version 0.1.1 --- CHANGELOG.md | 5 +++++ README.md | 20 +++++++++++++++++--- lib/zhong/at.rb | 17 +++++++++++++++++ lib/zhong/job.rb | 2 +- lib/zhong/scheduler.rb | 33 +++++++++++++++++++++++++++++---- lib/zhong/version.rb | 2 +- 6 files changed, 70 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec92866..6944dd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.1 + +- Handle multiple ats (at: ["mon 8:00, tues 9:30"]). +- Job callbacks (:before_tick, :after_tick, etc). + ## 0.1.0 - First release. diff --git a/README.md b/README.md index 2439bc4..75610df 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Zhong -Useful, reliable distributed cron. +Useful, reliable distributed cron. Tired of your cron-like scheduler running key jobs twice? Would you like to be able to run your cron server on multiple machines and have it "just work"? Have we got the gem for you. + +Zhong uses Redis to acquire exclusive locks on jobs, as well as recording when they last ran. This means that you can rest easy at night, knowing that your customers are getting their monthly Goat Fancy magazine subscriptions and you are rolling around in your piles of money without a care in the world. # Installation @@ -18,13 +20,25 @@ r = Redis.new Zhong.schedule(redis: r) do |s| s.category "stuff" do s.every(5.seconds, "foo") { puts "foo" } - s.every(1.week, "baz", at: "mon 22:45") { puts "baz" } + s.every(1.week, "baz", at: ["mon 22:45", "wed 23:13"]) { puts "baz" } end s.category "clutter" do - s.every(1.second, "compute", if: -> (t) { rand < 0.5 }) { puts "something happened" } + s.every(1.second, "compute", if: -> (t) { t.wday == 3 && rand < 0.5 }) { puts "something happened on wednesday" } + end + + # note: callbacks that return nil or false will cause event to not run + s.on(:before_tick) do + puts "ding" + true + end + + s.on(:after_tick) do + puts "dong" + true end end + ``` ## TODO diff --git a/lib/zhong/at.rb b/lib/zhong/at.rb index 43ff4c1..8309ea7 100644 --- a/lib/zhong/at.rb +++ b/lib/zhong/at.rb @@ -43,6 +43,11 @@ def next_at(time = Time.now) def self.parse(at, grace: 0) return unless at + # TODO: refactor this mess + if at.respond_to?(:each) + return MultiAt.new(at.map { |a| parse(a, grace: grace) }) + end + case at when /\A([[:alpha:]]+)\s+(.*)\z/ wday = WDAYS[$1] @@ -67,4 +72,16 @@ def self.parse(at, grace: 0) throw FailedToParse, at end end + + class MultiAt + attr_accessor :ats + + def initialize(ats = []) + @ats = ats + end + + def next_at(time = Time.now) + ats.map { |at| at.next_at(time) }.min + end + end end diff --git a/lib/zhong/job.rb b/lib/zhong/job.rb index 08e904b..1bdd11b 100644 --- a/lib/zhong/job.rb +++ b/lib/zhong/job.rb @@ -95,7 +95,7 @@ def to_s def next_at every_time = @every.next_at(@last_ran) if @last_ran && @every - at_time = @at.next_at(time) if @at + at_time = @at.next_at(Time.now) if @at [every_time, at_time, Time.now].compact.max || "now" end diff --git a/lib/zhong/scheduler.rb b/lib/zhong/scheduler.rb index 6c06495..3581df8 100644 --- a/lib/zhong/scheduler.rb +++ b/lib/zhong/scheduler.rb @@ -12,6 +12,7 @@ class Scheduler def initialize(config = {}) @jobs = {} + @callbacks = {} @config = DEFAULT_CONFIG.merge(config) @logger = @config[:logger] ||= Util.default_logger @redis = @config[:redis] ||= Redis.new(ENV["REDIS_URL"]) @@ -28,7 +29,18 @@ def category(name) end def every(period, name, opts = {}, &block) - add(Job.new(name, opts.merge(@config).merge(every: period, category: @category), &block)) + job = Job.new(name, opts.merge(@config).merge(every: period, category: @category), &block) + add(job) + end + + def error_handler(&block) + @error_handler = block if block_given? + @error_handler + end + + def on(event, &block) + fail "Unsupported callback #{event}" unless [:before_tick, :after_tick, :before_run, :after_run].include?(event.to_sym) + (@callbacks[event.to_sym] ||= []) << block end def start @@ -39,11 +51,20 @@ def start @logger.info "starting at #{redis_time}" loop do - now = redis_time + if fire_callbacks(:before_tick) + now = redis_time - jobs.each { |_, job| job.run(now) } + jobs.each do |_, job| + if fire_callbacks(:before_run, job, now) + job.run(now) + fire_callbacks(:after_run, job, now) + end + end - sleep(interval) + fire_callbacks(:after_tick) + + sleep(interval) + end break if @stop end @@ -56,6 +77,10 @@ def stop Thread.new { @logger.info "stopped" } end + def fire_callbacks(event, *args) + @callbacks[event].to_a.all? { |h| h.call(*args) } + end + private def add(job) diff --git a/lib/zhong/version.rb b/lib/zhong/version.rb index 1bacfef..eb25575 100644 --- a/lib/zhong/version.rb +++ b/lib/zhong/version.rb @@ -1,3 +1,3 @@ module Zhong - VERSION = "0.1.0" + VERSION = "0.1.1" end