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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6614568ab73c78b0be1866b355ba1cd288cce5a6
4
- data.tar.gz: a083d36f8fc30fb7b55f86278b1fd1e3577d4915
3
+ metadata.gz: 8b02d894da4c82ef8a9799121d77b83390b0a27f
4
+ data.tar.gz: 7f70396d6f476bf30e3c8f7b1e82c66da165afb5
5
5
  SHA512:
6
- metadata.gz: dffa0ee3bcd26a2febaafebac063f71a46857001654b3748b292601f4134a9f64c0186b626bb72bc4c4d39eaec1a41e3c162d6ca5b64d53de248176854cd7915
7
- data.tar.gz: 169d1db6499306e74dd86c89767e92818ffcc3d55873ac9dd04ddec9fe8b2d5648cb685e1dfbac4d3ad7bff4e467fcd2046e9c4cb0c680aef079906b354fd76a
6
+ metadata.gz: 12574d7a801709ee5bc6d0215eab7c1c7fe8cfe227f641bee7a8d01c2f805eb6b5043b6ad052f5e4f97c12734b0ad575c8095b163a4ce39075c4dcda92f06aa6
7
+ data.tar.gz: 1bb1cea7c8346e9dcf98b76d582c5a54802fe1769ab1065fb8aaa1396767fb627040b7a9486daad52036bdc05eb6d3debfd15761a8fa500ba2e488a3379825df
@@ -6,6 +6,7 @@ rvm:
6
6
  - 2.0.0
7
7
  - 2.1
8
8
  - 2.2
9
+ - ruby-2.3.1
9
10
  - jruby-19mode
10
11
  - # rbx-2.2.7
11
12
  gemfile:
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- Clockwork - a clock process to replace cron [![Build Status](https://secure.travis-ci.org/tomykaira/clockwork.png?branch=master)](http://travis-ci.org/tomykaira/clockwork) [![Dependency Status](https://gemnasium.com/tomykaira/clockwork.png)](https://gemnasium.com/tomykaira/clockwork)
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.heroku.com/past/2010/4/13/rethinking_cron/)
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.heroku.com/past/2010/4/24/beanstalk_a_simple_and_fast_queueing_backend/),
73
- [RabbitMQ/Minion](http://adam.heroku.com/past/2009/9/28/background_jobs_with_rabbitmq_and_minion/),
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 enviroment, and use per-event blocks to call DJ or Resque
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 Clcockwork output)
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` and `after_tick` callbacks.
467
- They are optional, and run every tick (a tick being whatever your `:sleep_timeout`
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:
@@ -11,9 +11,4 @@ file = "./#{file}" unless file.match(/^[\/.]/)
11
11
 
12
12
  require file
13
13
 
14
- trap('INT') do
15
- puts "\rExiting"
16
- exit
17
- end
18
-
19
14
  Clockwork::run
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "clockwork"
3
- s.version = "2.0.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/tomykaira/clockwork"
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
@@ -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
- loop do
64
- tick
65
- interval = config[:sleep_timeout] - Time.now.subsec + 0.001
66
- sleep(interval) if interval > 0
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
@@ -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.expects(:loop).yields.then.returns
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.expects(:loop).yields.then.returns
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.expects(:loop).yields.then.returns
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.expects(:loop).yields.then.returns
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.expects(:loop).yields.then.returns
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.expects(:loop).yields.then.returns
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
+
@@ -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.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: 2016-04-15 00:00:00.000000000 Z
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
- homepage: http://github.com/tomykaira/clockwork
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