clockwork 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +16 -8
- data/bin/clockwork +0 -5
- data/clockwork.gemspec +3 -2
- data/lib/clockwork/manager.rb +69 -4
- data/test/clockwork_test.rb +7 -6
- data/test/samples/signal_test.rb +19 -0
- data/test/signal_test.rb +34 -0
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b02d894da4c82ef8a9799121d77b83390b0a27f
|
4
|
+
data.tar.gz: 7f70396d6f476bf30e3c8f7b1e82c66da165afb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12574d7a801709ee5bc6d0215eab7c1c7fe8cfe227f641bee7a8d01c2f805eb6b5043b6ad052f5e4f97c12734b0ad575c8095b163a4ce39075c4dcda92f06aa6
|
7
|
+
data.tar.gz: 1bb1cea7c8346e9dcf98b76d582c5a54802fe1769ab1065fb8aaa1396767fb627040b7a9486daad52036bdc05eb6d3debfd15761a8fa500ba2e488a3379825df
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
Clockwork - a clock process to replace cron [![Build Status](https://
|
1
|
+
Clockwork - a clock process to replace cron [![Build Status](https://api.travis-ci.org/Rykian/clockwork.png?branch=master)](https://travis-ci.org/Rykian/clockwork) [![Dependency Status](https://gemnasium.com/Rykian/clockwork.png)](https://gemnasium.com/Rykian/clockwork)
|
2
2
|
===========================================
|
3
3
|
|
4
4
|
Cron is non-ideal for running scheduled application tasks, especially in an app
|
5
|
-
deployed to multiple machines. [More details.](http://adam.
|
5
|
+
deployed to multiple machines. [More details.](http://adam.herokuapp.com/past/2010/4/13/rethinking_cron/)
|
6
6
|
|
7
7
|
Clockwork is a cron replacement. It runs as a lightweight, long-running Ruby
|
8
8
|
process which sits alongside your web processes (Mongrel/Thin) and your worker
|
@@ -69,8 +69,8 @@ to do the work. It avoids locking by running as a single process, but this
|
|
69
69
|
makes it impossible to parallelize. For doing the work, you should be using a
|
70
70
|
job queueing system, such as
|
71
71
|
[Delayed Job](http://www.therailsway.com/2009/7/22/do-it-later-with-delayed-job),
|
72
|
-
[Beanstalk/Stalker](http://adam.
|
73
|
-
[RabbitMQ/Minion](http://adam.
|
72
|
+
[Beanstalk/Stalker](http://adam.herokuapp.com/past/2010/4/24/beanstalk_a_simple_and_fast_queueing_backend/),
|
73
|
+
[RabbitMQ/Minion](http://adam.herokuapp.com/past/2009/9/28/background_jobs_with_rabbitmq_and_minion/),
|
74
74
|
[Resque](http://github.com/blog/542-introducing-resque), or
|
75
75
|
[Sidekiq](https://github.com/mperham/sidekiq). This design allows a
|
76
76
|
simple clock process with no locks, but also offers near infinite horizontal
|
@@ -92,7 +92,7 @@ end
|
|
92
92
|
Using a queueing system which doesn't require that your full application be
|
93
93
|
loaded is preferable, because the clock process can keep a tiny memory
|
94
94
|
footprint. If you're using DJ or Resque, however, you can go ahead and load
|
95
|
-
your full application
|
95
|
+
your full application environment, and use per-event blocks to call DJ or Resque
|
96
96
|
enqueue methods. For example, with DJ/Rails:
|
97
97
|
|
98
98
|
```ruby
|
@@ -162,7 +162,7 @@ When one of the events is ready to be run (based on it's `frequency`, and possib
|
|
162
162
|
|
163
163
|
- (optionally) `at` return any acceptable clockwork `:at` string
|
164
164
|
|
165
|
-
- (optionally) `name` returning the name for the event (used to identify it in the
|
165
|
+
- (optionally) `name` returning the name for the event (used to identify it in the Clockwork output)
|
166
166
|
|
167
167
|
- (optionally) `if?` returning either true or false, depending on whether the database event should run at the given time (this method will be passed the time as a parameter, much like the standard clockwork `:if`)
|
168
168
|
|
@@ -463,8 +463,8 @@ every(1.day, 'check.leap.year') do
|
|
463
463
|
end
|
464
464
|
```
|
465
465
|
|
466
|
-
In addition, Clockwork also supports `:before_tick
|
467
|
-
|
466
|
+
In addition, Clockwork also supports `:before_tick`, `:after_tick`, `:before_run`, and `:after_run` callbacks.
|
467
|
+
All callbacks are optional. The `tick` callbacks run every tick (a tick being whatever your `:sleep_timeout`
|
468
468
|
is set to, default is 1 second):
|
469
469
|
|
470
470
|
```ruby
|
@@ -475,6 +475,14 @@ end
|
|
475
475
|
on(:after_tick) do
|
476
476
|
puts "tock"
|
477
477
|
end
|
478
|
+
|
479
|
+
on(:before_run) do |event, t|
|
480
|
+
puts "job_started: #{event}"
|
481
|
+
end
|
482
|
+
|
483
|
+
on(:after_run) do |event, t|
|
484
|
+
puts "job_finished: #{event}"
|
485
|
+
end
|
478
486
|
```
|
479
487
|
|
480
488
|
Finally, you can use tasks synchronised from a database as described in detail above:
|
data/bin/clockwork
CHANGED
data/clockwork.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "clockwork"
|
3
|
-
s.version = "2.0.
|
3
|
+
s.version = "2.0.1"
|
4
4
|
|
5
5
|
s.authors = ["Adam Wiggins", "tomykaira"]
|
6
6
|
s.license = 'MIT'
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.extra_rdoc_files = [
|
10
10
|
"README.md"
|
11
11
|
]
|
12
|
-
s.homepage = "http://github.com/
|
12
|
+
s.homepage = "http://github.com/Rykian/clockwork"
|
13
13
|
s.summary = "A scheduler process to replace cron."
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split($/)
|
@@ -25,4 +25,5 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_development_dependency "daemons"
|
26
26
|
s.add_development_dependency "minitest", "~> 5.8"
|
27
27
|
s.add_development_dependency "mocha"
|
28
|
+
s.add_development_dependency "test-unit"
|
28
29
|
end
|
data/lib/clockwork/manager.rb
CHANGED
@@ -9,6 +9,9 @@ module Clockwork
|
|
9
9
|
@callbacks = {}
|
10
10
|
@config = default_configuration
|
11
11
|
@handler = nil
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@condvar = ConditionVariable.new
|
14
|
+
@finish = false
|
12
15
|
end
|
13
16
|
|
14
17
|
def thread_available?
|
@@ -60,10 +63,68 @@ module Clockwork
|
|
60
63
|
|
61
64
|
def run
|
62
65
|
log "Starting clock for #{@events.size} events: [ #{@events.map(&:to_s).join(' ')} ]"
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
66
|
+
|
67
|
+
sig_read, sig_write = IO.pipe
|
68
|
+
|
69
|
+
%w[INT TERM HUP].each do |sig|
|
70
|
+
trap sig do
|
71
|
+
sig_write.puts(sig)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
run_tick_loop
|
76
|
+
|
77
|
+
while io = IO.select([sig_read])
|
78
|
+
sig = io.first[0].gets.chomp
|
79
|
+
handle_signal(sig)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_signal(sig)
|
84
|
+
logger.debug "Got #{sig} signal"
|
85
|
+
case sig
|
86
|
+
when 'INT'
|
87
|
+
shutdown
|
88
|
+
when 'TERM'
|
89
|
+
# Heroku sends TERM signal, and waits 10 seconds before exit
|
90
|
+
graceful_shutdown
|
91
|
+
when 'HUP'
|
92
|
+
graceful_shutdown
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def shutdown
|
97
|
+
logger.info 'Shutting down'
|
98
|
+
stop_tick_loop
|
99
|
+
exit(0)
|
100
|
+
end
|
101
|
+
|
102
|
+
def graceful_shutdown
|
103
|
+
logger.info 'Gracefully shutting down'
|
104
|
+
stop_tick_loop
|
105
|
+
wait_tick_loop_finishes
|
106
|
+
exit(0)
|
107
|
+
end
|
108
|
+
|
109
|
+
def stop_tick_loop
|
110
|
+
@finish = true
|
111
|
+
end
|
112
|
+
|
113
|
+
def wait_tick_loop_finishes
|
114
|
+
@mutex.synchronize do # wait by synchronize
|
115
|
+
@condvar.signal
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def run_tick_loop
|
120
|
+
Thread.new do
|
121
|
+
@mutex.synchronize do
|
122
|
+
until @finish
|
123
|
+
tick
|
124
|
+
interval = config[:sleep_timeout] - Time.now.subsec + 0.001
|
125
|
+
@condvar.wait(@mutex, interval) if interval > 0
|
126
|
+
end
|
127
|
+
end
|
67
128
|
end
|
68
129
|
end
|
69
130
|
|
@@ -81,6 +142,10 @@ module Clockwork
|
|
81
142
|
events
|
82
143
|
end
|
83
144
|
|
145
|
+
def logger
|
146
|
+
config[:logger]
|
147
|
+
end
|
148
|
+
|
84
149
|
def log_error(e)
|
85
150
|
config[:logger].error(e)
|
86
151
|
end
|
data/test/clockwork_test.rb
CHANGED
@@ -9,6 +9,7 @@ describe Clockwork do
|
|
9
9
|
config[:sleep_timeout] = 0
|
10
10
|
config[:logger] = Logger.new(@log_output)
|
11
11
|
end
|
12
|
+
IO.stubs(:select)
|
12
13
|
end
|
13
14
|
|
14
15
|
after do
|
@@ -21,7 +22,7 @@ describe Clockwork do
|
|
21
22
|
run = job == 'myjob'
|
22
23
|
end
|
23
24
|
Clockwork.every(1.minute, 'myjob')
|
24
|
-
Clockwork.manager.
|
25
|
+
Clockwork.manager.stubs(:run_tick_loop).returns(Clockwork.manager.tick)
|
25
26
|
Clockwork.run
|
26
27
|
|
27
28
|
assert run
|
@@ -34,7 +35,7 @@ describe Clockwork do
|
|
34
35
|
run = job == 'an event'
|
35
36
|
end
|
36
37
|
Clockwork.every(1.minute, 'an event')
|
37
|
-
Clockwork.manager.
|
38
|
+
Clockwork.manager.stubs(:run_tick_loop).returns(Clockwork.manager.tick)
|
38
39
|
Clockwork.run
|
39
40
|
assert run
|
40
41
|
assert @log_output.string.include?("Triggering 'an event'")
|
@@ -47,7 +48,7 @@ describe Clockwork do
|
|
47
48
|
run = job == event_object
|
48
49
|
end
|
49
50
|
Clockwork.every(1.minute, event_object)
|
50
|
-
Clockwork.manager.
|
51
|
+
Clockwork.manager.stubs(:run_tick_loop).returns(Clockwork.manager.tick)
|
51
52
|
Clockwork.run
|
52
53
|
assert run
|
53
54
|
end
|
@@ -59,14 +60,14 @@ describe Clockwork do
|
|
59
60
|
config[:sleep_timeout] = 0
|
60
61
|
config[:logger] = Logger.new(@log_output)
|
61
62
|
end
|
62
|
-
Clockwork.manager.
|
63
|
+
Clockwork.manager.stubs(:run_tick_loop).returns(Clockwork.manager.tick)
|
63
64
|
Clockwork.run
|
64
65
|
assert @log_output.string.include?('0 events')
|
65
66
|
end
|
66
67
|
|
67
68
|
it 'should pass all arguments to every' do
|
68
69
|
Clockwork.every(1.second, 'myjob', if: lambda { |_| false }) { }
|
69
|
-
Clockwork.manager.
|
70
|
+
Clockwork.manager.stubs(:run_tick_loop).returns(Clockwork.manager.tick)
|
70
71
|
Clockwork.run
|
71
72
|
assert @log_output.string.include?('1 events')
|
72
73
|
assert !@log_output.string.include?('Triggering')
|
@@ -77,7 +78,7 @@ describe Clockwork do
|
|
77
78
|
module ::Clockwork
|
78
79
|
every(1.second, 'myjob') { $called = true }
|
79
80
|
end
|
80
|
-
Clockwork.manager.
|
81
|
+
Clockwork.manager.stubs(:run_tick_loop).returns(Clockwork.manager.tick)
|
81
82
|
Clockwork.run
|
82
83
|
assert $called
|
83
84
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'clockwork'
|
2
|
+
|
3
|
+
module Clockwork
|
4
|
+
LOGFILE = File.expand_path('../../tmp/signal_test.log', __FILE__)
|
5
|
+
|
6
|
+
handler do |job|
|
7
|
+
File.write(LOGFILE, 'start')
|
8
|
+
sleep 0.1
|
9
|
+
File.write(LOGFILE, 'done')
|
10
|
+
end
|
11
|
+
|
12
|
+
configure do |config|
|
13
|
+
config[:sleep_timeout] = 0
|
14
|
+
config[:logger] = Logger.new(StringIO.new)
|
15
|
+
end
|
16
|
+
|
17
|
+
every(1.seconds, 'run.me.every.1.seconds')
|
18
|
+
end
|
19
|
+
|
data/test/signal_test.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'mocha/setup'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class SignalTest < Test::Unit::TestCase
|
6
|
+
CMD = File.expand_path('../../bin/clockwork', __FILE__)
|
7
|
+
SAMPLE = File.expand_path('../samples/signal_test.rb', __FILE__)
|
8
|
+
LOGFILE = File.expand_path('../tmp/signal_test.log', __FILE__)
|
9
|
+
|
10
|
+
setup do
|
11
|
+
FileUtils.mkdir_p(File.dirname(LOGFILE))
|
12
|
+
@pid = spawn(CMD, SAMPLE)
|
13
|
+
until File.exist?(LOGFILE)
|
14
|
+
sleep 0.1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
teardown do
|
19
|
+
FileUtils.rm_r(File.dirname(LOGFILE))
|
20
|
+
end
|
21
|
+
|
22
|
+
test 'should gracefully shutdown with SIGTERM' do
|
23
|
+
Process.kill(:TERM, @pid)
|
24
|
+
sleep 0.2
|
25
|
+
assert_equal 'done', File.read(LOGFILE)
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'should forcely shutdown with SIGINT' do
|
29
|
+
Process.kill(:INT, @pid)
|
30
|
+
sleep 0.2
|
31
|
+
assert_equal 'start', File.read(LOGFILE)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clockwork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-02-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: tzinfo
|
@@ -109,6 +109,20 @@ dependencies:
|
|
109
109
|
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: test-unit
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
112
126
|
description: A scheduler process to replace cron, using a more flexible Ruby syntax
|
113
127
|
running as a single long-running process. Inspired by rufus-scheduler and resque-scheduler.
|
114
128
|
email:
|
@@ -152,7 +166,9 @@ files:
|
|
152
166
|
- test/database_events/test_helpers.rb
|
153
167
|
- test/event_test.rb
|
154
168
|
- test/manager_test.rb
|
155
|
-
|
169
|
+
- test/samples/signal_test.rb
|
170
|
+
- test/signal_test.rb
|
171
|
+
homepage: http://github.com/Rykian/clockwork
|
156
172
|
licenses:
|
157
173
|
- MIT
|
158
174
|
metadata: {}
|
@@ -185,3 +201,5 @@ test_files:
|
|
185
201
|
- test/database_events/test_helpers.rb
|
186
202
|
- test/event_test.rb
|
187
203
|
- test/manager_test.rb
|
204
|
+
- test/samples/signal_test.rb
|
205
|
+
- test/signal_test.rb
|