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 +4 -4
- data/README.md +5 -0
- data/lib/elected/scheduler/poller.rb +32 -32
- data/lib/elected/scheduler/schedule.rb +39 -93
- data/lib/elected/scheduler/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65e11fc03001777e3fc15d10cf8f617ccfc7c76e
|
4
|
+
data.tar.gz: b21d8aa9b54dd3858a097e45bacdc2c3333227d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0261d2074e5ac4e60f0f2bdc28f719775668eb6c403cb66c2663779c543da6bbc87db6ad646f785375b06052d5bc29f1e2cdc050c227096b1ddeda0a02299730
|
7
|
+
data.tar.gz: b946425a46a34668698a89555ffbe345d79e60a417a6dbb07f2462069d924d233a343ef3d54560a357e4ce5a89f3afdd68c6fb4d36ee469cf43bc63438494e1d
|
data/README.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
[](https://travis-ci.org/simple-finance/elected_scheduler)
|
2
|
+
[](https://codeclimate.com/github/simple-finance/elected_scheduler)
|
3
|
+
[](https://hakiri.io/github/simple-finance/elected_scheduler/master)
|
4
|
+
[](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
|
-
|
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
|
-
|
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
|
-
|
101
|
-
|
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
|
-
|
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
|
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,
|
73
|
+
convert_numbers field, ary
|
71
74
|
when :months
|
72
|
-
|
75
|
+
convert_numbers :months, ary, :convert_month_name
|
73
76
|
when :dows
|
74
|
-
|
77
|
+
convert_numbers :dows, ary, :convert_dow_name
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
78
|
-
def
|
79
|
-
|
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,
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
99
|
-
|
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
|
-
|
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
|
120
|
-
|
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
|
-
|
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
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
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.
|
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-
|
11
|
+
date: 2015-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: elected
|