elected_scheduler 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80e36753deee16c6d4ac311880ddf160076f5d60
4
- data.tar.gz: 899d8490b59f93ebc242788a303f5084ebe3775a
3
+ metadata.gz: 65e11fc03001777e3fc15d10cf8f617ccfc7c76e
4
+ data.tar.gz: b21d8aa9b54dd3858a097e45bacdc2c3333227d7
5
5
  SHA512:
6
- metadata.gz: 938a658a6c1f60910b09e0f7ca214f8b96b139b15b251593e4bef4ae11919bb352eb29cf1b5d07ec53bcaed9337a163baed2cad4d761ac7b32668cafdee8388d
7
- data.tar.gz: 9cf6aace4ec6d692d300f1af6b292a579697fa2fbb4cd22756e2fb4ae67dd17fcd357c8889afbfbdf9f2c2bdd6d8db6a6019e2adce6023d6ee8c29011181f3e0
6
+ metadata.gz: 0261d2074e5ac4e60f0f2bdc28f719775668eb6c403cb66c2663779c543da6bbc87db6ad646f785375b06052d5bc29f1e2cdc050c227096b1ddeda0a02299730
7
+ data.tar.gz: b946425a46a34668698a89555ffbe345d79e60a417a6dbb07f2462069d924d233a343ef3d54560a357e4ce5a89f3afdd68c6fb4d36ee469cf43bc63438494e1d
data/README.md CHANGED
@@ -1,3 +1,8 @@
1
+ [![Build Status](https://travis-ci.org/simple-finance/elected_scheduler.svg?branch=master)](https://travis-ci.org/simple-finance/elected_scheduler)
2
+ [![Code Climate](https://codeclimate.com/github/simple-finance/elected_scheduler/badges/gpa.svg)](https://codeclimate.com/github/simple-finance/elected_scheduler)
3
+ [![security](https://hakiri.io/github/simple-finance/elected_scheduler/master.svg)](https://hakiri.io/github/simple-finance/elected_scheduler/master)
4
+ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/simple-finance/elected_scheduler?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
5
+
1
6
  # ElectedScheduler - A ruby distributed cron-like scheduler that only runs jobs on leader processes.
2
7
 
3
8
  > Schedule your jobs at cron-like times (seconds included) and run the jobs only if the process is the current leader.
@@ -5,7 +5,7 @@ module Elected
5
5
  include Logging
6
6
  extend Forwardable
7
7
 
8
- attr_reader :key, :jobs
8
+ attr_reader :key, :jobs, :stats
9
9
 
10
10
  def_delegators :@senado, :timeout
11
11
 
@@ -13,6 +13,7 @@ module Elected
13
13
  @senado ||= Senado.new key, timeout
14
14
  @key = key
15
15
  @jobs = Concurrent::Hash.new
16
+ @stats = Stats.new :processed_job, :no_match, :sleep_slave
16
17
  end
17
18
 
18
19
  def add(job)
@@ -26,27 +27,17 @@ module Elected
26
27
  @status ||= :stopped
27
28
  end
28
29
 
29
- def starting?
30
- status == :starting
31
- end
32
-
33
30
  def running?
34
31
  status == :running
35
32
  end
36
33
 
37
- def stopping?
38
- status == :stopping
39
- end
40
-
41
34
  def stopped?
42
35
  status == :stopped
43
36
  end
44
37
 
45
38
  def start
46
- unless stopped?
47
- debug "cannot start on #{status} ..."
48
- return false
49
- end
39
+ return false unless stopped?
40
+ raise 'No jobs to run!' if jobs.empty?
50
41
 
51
42
  debug "#{label} starting ..."
52
43
  @status = :starting
@@ -54,24 +45,24 @@ module Elected
54
45
  @status = :running
55
46
  debug "#{label} running poller!"
56
47
  @status
57
- rescue Exception => e
58
- error "#{label} Exception thrown: #{e.class.name} : #{e.message}\n #{e.backtrace[0, 5].join("\n ")}"
59
48
  end
60
49
 
61
50
  def stop
62
- unless running?
63
- warn "cannot stop on #{status} ..."
64
- return false
65
- end
51
+ return false unless running?
66
52
 
67
53
  debug "#{label} stopping poller ..."
68
54
  @status = :stopping
69
55
  stop_polling_loop
56
+ senado.release
70
57
  @status = :stopped
71
58
  debug "#{label} stopped poller!"
72
59
  @status
73
60
  end
74
61
 
62
+ def leader?
63
+ senado.leader?
64
+ end
65
+
75
66
  def to_s
76
67
  %{#<#{self.class.name} key="#{key}" timeout="#{timeout}" jobs=#{jobs.size}>}
77
68
  end
@@ -85,11 +76,7 @@ module Elected
85
76
  def start_polling_loop
86
77
  debug 'starting process loop ...'
87
78
  @polling_loop_thread = Thread.new do
88
- begin
89
- poll_and_process_loop
90
- rescue Exception => e
91
- error "Exception: #{e.class.name} : #{e.message}\n #{e.backtrace[0, 10].join("\n ")}"
92
- end
79
+ poll_and_process_loop
93
80
  end
94
81
  end
95
82
 
@@ -97,29 +84,32 @@ module Elected
97
84
  cnt = 0
98
85
  while running?
99
86
  cnt += 1
100
- begin
101
- debug "#{label(cnt)} calling poll_and_process_jobs while running?:#{running?} ..."
102
- poll_and_process_jobs
103
- rescue Exception => e
104
- error "#{label(cnt)} Exception: #{e.class.name} : #{e.message}\n #{e.backtrace[0, 5].join("\n ")}"
105
- end
87
+ debug "#{label(cnt)} calling poll_and_process_jobs while running?:#{running?} ..."
88
+ poll_and_process_jobs
106
89
  sleep_until_next_tick
107
90
  end
108
91
  end
109
92
 
110
93
  def poll_and_process_jobs
111
94
  if senado.leader?
95
+ debug "#{label} leader, processing jobs ..."
112
96
  start_time = Time.now
113
97
  jobs.values.each { |job| process_job job, start_time }
114
98
  else
99
+ debug "#{label} not a leader, sleeping ..."
100
+ @stats.increment :sleep_slave
115
101
  sleep_for_slave
116
102
  end
117
103
  end
118
104
 
119
105
  def process_job(job, time)
120
- return false unless job.matches? time
106
+ unless job.matches? time
107
+ @stats.increment :no_match
108
+ return false
109
+ end
121
110
 
122
111
  Concurrent::Future.execute { job.execute }
112
+ @stats.increment :processed_job
123
113
  end
124
114
 
125
115
  def stop_polling_loop
@@ -147,8 +137,18 @@ module Elected
147
137
 
148
138
  extend self
149
139
 
140
+ attr_writer :key, :timeout
141
+
142
+ def key
143
+ @key || 'elected_scheduler_poller'
144
+ end
145
+
146
+ def timeout
147
+ @timeout || Elected.timeout
148
+ end
149
+
150
150
  def poller
151
- @poller ||= Poller.new
151
+ @poller ||= Poller.new key, timeout
152
152
  end
153
153
  end
154
154
  end
@@ -6,11 +6,14 @@ module Elected
6
6
 
7
7
  FIELDS = { seconds: :sec, minutes: :min, hours: :hour, days: :day, months: :month, dows: :wday }.freeze
8
8
 
9
- ABBR_DAYNAMES = %w{ sun mon tue wed thu fri sat }
10
- DAYNAMES = %w{ sunday monday tuesday wednesday thursday friday saturday }
9
+ ABBR_DAYNAMES = %w{ sun mon tue wed thu fri sat }.freeze
10
+ DAYNAMES = %w{ sunday monday tuesday wednesday thursday friday saturday }.freeze
11
11
 
12
- MONTHNAMES = %w{ january february march april may june july august september october november december }
13
- ABBR_MONTHNAMES = %w{ jan feb mar apr may jun jul aug sep oct nov dec }
12
+ MONTHNAMES = %w{ '' january february march april may june july august september october november december }.freeze
13
+ ABBR_MONTHNAMES = %w{ '' jan feb mar apr may jun jul aug sep oct nov dec }.freeze
14
+
15
+ MIN_NUMBERS = { seconds: 0, minutes: 0, hours: 0, days: 1, months: 1, dows: 0 }.freeze
16
+ MAX_NUMBERS = { seconds: 59, minutes: 59, hours: 23, days: 31, months: 12, dows: 6 }.freeze
14
17
 
15
18
  def self.defaults
16
19
  @defaults ||= { seconds: [0] }
@@ -18,7 +21,7 @@ module Elected
18
21
 
19
22
  def initialize(options = {})
20
23
  @options = options
21
- setup *FIELDS.keys
24
+ setup *(FIELDS.keys)
22
25
  end
23
26
 
24
27
  def matches?(time)
@@ -26,15 +29,7 @@ module Elected
26
29
  end
27
30
 
28
31
  def to_cron_s
29
- FIELDS.keys.map do |field|
30
- value = get field
31
- case value
32
- when :all
33
- '*'
34
- when Range, Array
35
- value.map { |x| x.to_s }.join(',')
36
- end
37
- end.join(' ')
32
+ FIELDS.keys.map { |field| get_cron_s(field) }.join(' ')
38
33
  end
39
34
 
40
35
  alias :to_s :to_cron_s
@@ -48,6 +43,11 @@ module Elected
48
43
 
49
44
  alias :[] :get
50
45
 
46
+ def get_cron_s(field)
47
+ value = get(field)
48
+ value === :all ? '*' : value.map { |x| x.to_s }.join(',')
49
+ end
50
+
51
51
  def set(field, value)
52
52
  instance_variable_set "@#{field}", convert(field, value)
53
53
  end
@@ -65,102 +65,48 @@ module Elected
65
65
  end
66
66
 
67
67
  def convert(field, value)
68
+ return value if value === :all
69
+
70
+ ary = simplify_enum Array(value).flatten
68
71
  case field
69
72
  when :seconds, :minutes, :hours, :days
70
- convert_numbers field, value
73
+ convert_numbers field, ary
71
74
  when :months
72
- convert_months value
75
+ convert_numbers :months, ary, :convert_month_name
73
76
  when :dows
74
- convert_dows value
77
+ convert_numbers :dows, ary, :convert_dow_name
75
78
  end
76
79
  end
77
80
 
78
- def match?(field, exp_value)
79
- value = get field
80
- return true if value === :all
81
- value.include? exp_value
81
+ def simplify_enum(ary)
82
+ ary.map { |x| x.is_a?(Range) ? x.to_a : x }.flatten
82
83
  end
83
84
 
84
- def convert_numbers(field, value)
85
- max = { seconds: 60, minutes: 60, hours: 23, days: 31 }[field]
86
- case value
87
- when :all
88
- value
89
- when Integer
90
- simplify_enum [value], 0, max
91
- when Array, Range
92
- simplify_enum value, 0, max
93
- else
94
- raise "Unknown value (#{value.class.name}) #{value.inspect}] for #{field}"
95
- end
85
+ def convert_numbers(field, ary, converter_name = nil)
86
+ ary.map do |x|
87
+ converter_name ? send(converter_name, x) : x
88
+ end.select do |x|
89
+ x.is_a?(Integer) && x >= MIN_NUMBERS[field] && x <= MAX_NUMBERS[field]
90
+ end.uniq.sort
96
91
  end
97
92
 
98
- def simplify_enum(ary, min, max)
99
- ary.to_a.flatten.map do |x|
100
- x.is_a?(Range) ? simplify_enum(x.to_a, min, max) : x.to_i
101
- end.flatten.uniq.sort.
102
- select { |x| x >= min && x <= max }
103
- end
93
+ def convert_month_name(value)
94
+ return value unless value.is_a?(String) || value.is_a?(Symbol)
104
95
 
105
- def convert_months(value)
106
- case value
107
- when :all
108
- value
109
- when Integer, Symbol, String
110
- convert_months [value]
111
- when Array, Range
112
- ary = value.map { |x| convert_month x }
113
- simplify_enum ary, 1, 12
114
- else
115
- raise "Unknown value (#{value.class.name}) #{value.inspect}] for month"
116
- end
96
+ [ABBR_MONTHNAMES, MONTHNAMES].map { |ary| ary.index value.to_s.downcase }.compact.first
117
97
  end
118
98
 
119
- def convert_month(value)
120
- case value
121
- when Integer
122
- raise "Unknown value (#{value.class.name}) #{value.inspect}] for month" if value < 1 || value > 12
123
- value
124
- when Array, Range
125
- ary = value.map { |x| convert_month x }
126
- simplify_enum ary, 1, 12
127
- when String, Symbol
128
- idx = [ABBR_MONTHNAMES, MONTHNAMES].map { |ary| ary.index value.to_s.downcase }.compact.first
129
- raise "[#{idx}] Unknown value (#{value.class.name}) #{value.inspect}] for month" if idx.nil? || idx < 0 || idx > 11
130
- idx + 1
131
- else
132
- raise "Unknown value (#{value.class.name}) #{value.inspect}] for month"
133
- end
134
- end
99
+ def convert_dow_name(value)
100
+ return value unless value.is_a?(String) || value.is_a?(Symbol)
135
101
 
136
- def convert_dows(value)
137
- case value
138
- when :all
139
- value
140
- when Integer, Symbol, String
141
- convert_dows [value]
142
- when Array, Range
143
- ary = value.map { |x| convert_dow x }
144
- simplify_enum ary, 0, 6
145
- else
146
- raise "Unknown value (#{value.class.name}) #{value.inspect}] for dow"
147
- end
102
+ [ABBR_DAYNAMES, DAYNAMES].map { |ary| ary.index value.to_s.downcase }.compact.first
148
103
  end
149
104
 
150
- def convert_dow(value)
151
- case value
152
- when Integer
153
- value
154
- when Array, Range
155
- ary = value.map { |x| convert_dow x }
156
- simplify_enum ary, 0, 6
157
- when String, Symbol
158
- idx = [ABBR_DAYNAMES, DAYNAMES].map { |ary| ary.index value.to_s.downcase }.compact.first
159
- raise "[#{idx}] Unknown value (#{value.class.name}) #{value.inspect}] for dow" if idx.nil? || idx < 0 || idx > 6
160
- idx
161
- else
162
- raise "Unknown value (#{value.class.name}) #{value.inspect}] for dow"
163
- end
105
+ def match?(field, exp_value)
106
+ value = get field
107
+ return true if value === :all
108
+
109
+ value.include? exp_value
164
110
  end
165
111
 
166
112
  end
@@ -1,5 +1,5 @@
1
1
  module Elected
2
2
  module Scheduler
3
- VERSION = '0.1.0'
3
+ VERSION = '0.1.1'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elected_scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Madrid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-30 00:00:00.000000000 Z
11
+ date: 2015-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elected