say_when 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,4 +3,5 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  *.DS_Store
6
- .rvmrc
6
+ spec/db/test.db
7
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ script: bundle exec spec spec/
@@ -7,8 +7,8 @@ module SayWhen
7
7
 
8
8
  @@scheduler = nil
9
9
  @@lock = nil
10
-
11
- attr_accessor :storage_strategy, :processor_class, :tick_length
10
+
11
+ attr_accessor :storage_strategy, :processor_class, :tick_length, :reset_acquired_length
12
12
 
13
13
  attr_accessor :running
14
14
 
@@ -40,11 +40,11 @@ module SayWhen
40
40
  def start
41
41
  self.scheduler.start
42
42
  end
43
-
44
43
  end
45
44
 
46
45
  def initialize
47
46
  self.tick_length = 1
47
+ self.reset_acquired_length = 3600
48
48
  end
49
49
 
50
50
  def processor
@@ -54,7 +54,7 @@ module SayWhen
54
54
  end
55
55
  @processor
56
56
  end
57
-
57
+
58
58
  def start
59
59
  logger.info "SayWhen::Scheduler starting"
60
60
 
@@ -68,42 +68,69 @@ module SayWhen
68
68
 
69
69
  logger.info "SayWhen::Scheduler running"
70
70
  job = nil
71
+ reset_next_at = Time.now
71
72
  while running
72
73
  begin
73
74
  time_now = Time.now
74
- logger.debug "SayWhen:: Looking for job that should be ready to fire before #{time_now}"
75
- job = job_class.acquire_next(time_now)
75
+
76
+ if reset_acquired_length > 0 && reset_next_at <= time_now
77
+ reset_next_at = time_now + reset_acquired_length
78
+ logger.debug "SayWhen:: reset acquired at #{time_now}, try again at #{reset_next_at}"
79
+ job_class.reset_acquired(reset_acquired_length)
80
+ end
81
+
82
+ begin
83
+ logger.debug "SayWhen:: Looking for job that should be ready to fire before #{time_now}"
84
+ job = job_class.acquire_next(time_now)
85
+ rescue StandardError => ex
86
+ job_error("Failure to acquire job", job, ex)
87
+ job = nil
88
+ end
89
+
76
90
  if job.nil?
77
91
  logger.debug "SayWhen:: no jobs to acquire, sleep"
78
92
  sleep(tick_length)
79
- else
93
+ next
94
+ end
95
+
96
+ begin
80
97
  logger.debug "SayWhen:: got a job: #{job.inspect}"
81
98
  # delegate processing the trigger to the processor
82
99
  self.processor.process(job)
83
100
  logger.debug "SayWhen:: job processed"
84
101
 
85
- # this should update next fire at, and put back in list of scheduled jobs
102
+ # if successful, update next fire at, put back to waiting / ended
86
103
  job.fired(time_now)
87
104
  logger.debug "SayWhen:: job fired complete"
105
+ rescue StandardError=>ex
106
+ job_error("Failure to process", job, ex)
88
107
  end
89
- rescue Interrupt
90
- job_msg = job && " job:'#{job.inspect}'"
91
- logger.error "\nSayWhen:: Interrupt! #{job_msg}"
92
- exit
93
- rescue StandardError=>ex
94
- job_msg = job && " job:'#{job.inspect}'"
95
- logger.error "SayWhen:: Failure to process#{job_msg}: #{ex.message}\n\t#{ex.backtrace.join("\t\n")}"
96
- job.release if job
97
- rescue Exception=>ex
98
- logger.error "SayWhen:: Exception in process#{job_msg}: #{ex.message}\n\t#{ex.backtrace.join("\t\n")}"
99
- exit
108
+
109
+ rescue Interrupt => ex
110
+ job_error("Interrupt!", job, ex)
111
+ raise ex
112
+ rescue StandardError => ex
113
+ job_error("Error!", job, ex)
114
+ sleep(tick_length)
115
+ rescue Exception => ex
116
+ job_error("Exception!", job, ex)
117
+ raise ex
100
118
  end
101
119
  end
120
+ rescue Exception=>ex
121
+ logger.error "SayWhen::Scheduler stopping, error: #{ex.class.name}: #{ex.message}"
122
+ exit
102
123
  end
103
124
 
104
125
  logger.info "SayWhen::Scheduler stopped"
105
126
  end
106
127
 
128
+ def job_error(msg, job, ex)
129
+ job_msg = job && " job:'#{job.inspect}'"
130
+ logger.error "SayWhen::Scheduler #{msg}#{job_msg}: #{ex.message}\n\t#{ex.backtrace.join("\t\n")}"
131
+ job.release if job
132
+ end
133
+
107
134
  def stop
108
135
  logger.info "SayWhen::Scheduler stopping..."
109
136
  self.running = false
@@ -8,18 +8,23 @@ module SayWhen
8
8
  module ActiveRecord
9
9
 
10
10
  class Job < ::ActiveRecord::Base
11
-
12
11
  include SayWhen::BaseJob
13
-
14
12
  self.table_name = "say_when_jobs"
15
-
16
-
17
13
  serialize :trigger_options
18
14
  serialize :data
19
15
  belongs_to :scheduled, :polymorphic => true
20
16
  has_many :job_executions, :class_name=>'SayWhen::Storage::ActiveRecord::JobExecution'
21
17
  before_create :set_defaults
22
18
 
19
+ def self.reset_acquired(older_than_seconds)
20
+ return unless older_than_seconds.to_i > 0
21
+ older_than = (Time.now - older_than_seconds.to_i)
22
+ update_all(
23
+ "status = '#{STATE_WAITING}'",
24
+ ["status = ? and updated_at < ?", STATE_ACQUIRED, older_than]
25
+ )
26
+ end
27
+
23
28
  def self.acquire_next(no_later_than)
24
29
  @next_job = nil
25
30
  hide_logging do
@@ -28,13 +33,13 @@ module SayWhen
28
33
  @next_job = find(:first,
29
34
  :lock => true,
30
35
  :order => 'next_fire_at ASC',
31
- :conditions => ['status = ? and ? >= next_fire_at',
36
+ :conditions => ['status = ? and ? >= next_fire_at',
32
37
  STATE_WAITING,
33
38
  no_later_than.in_time_zone('UTC')])
34
39
 
35
40
  # set status to acquired to take it out of rotation
36
41
  @next_job.update_attribute(:status, STATE_ACQUIRED) unless @next_job.nil?
37
-
42
+
38
43
  end
39
44
  end
40
45
  @next_job
@@ -94,9 +99,9 @@ module SayWhen
94
99
  ::ActiveRecord::Base.logger = old_logger
95
100
  end
96
101
  end
97
-
102
+
98
103
  end
99
-
104
+
100
105
  end
101
106
  end
102
107
  end
@@ -17,21 +17,45 @@ module SayWhen
17
17
  has_properties :trigger_strategy, :trigger_options, :last_fire_at, :next_fire_at
18
18
  has_properties :job_class, :job_method, :data
19
19
  has_properties :scheduled
20
+ has_properties :updated_at
21
+
22
+ def self.class_lock
23
+ @@_lock ||= Mutex.new
24
+ end
25
+
26
+ def self._reset
27
+ @@jobs = SortedSet.new
28
+ end
29
+
30
+ def self.reset_acquired(older_than_seconds)
31
+ return unless older_than_seconds.to_i > 0
32
+ older_than = (Time.now - older_than_seconds.to_i)
33
+ self.class_lock.synchronize {
34
+ jobs.select do |j|
35
+ j.status == STATE_ACQUIRED && j.updated_at < older_than
36
+ end.each{ |j| j.status = STATE_WAITING }
37
+ }
38
+ end
20
39
 
21
40
  def self.acquire_next(no_later_than)
22
- self.lock.synchronize {
23
-
41
+ self.class_lock.synchronize {
42
+
24
43
  next_job = jobs.detect(nil) do |j|
25
44
  (j.status == STATE_WAITING) && (j.next_fire_at.to_i <= no_later_than.to_i)
26
45
  end
27
46
 
28
- next_job.status = STATE_ACQUIRED if next_job
47
+ if next_job
48
+ next_job.status = STATE_ACQUIRED
49
+ next_job.updated_at = Time.now
50
+ end
51
+
29
52
  next_job
30
- }
53
+ }
31
54
  end
32
55
 
33
56
  def initialize(options={})
34
57
  super
58
+ self.updated_at = Time.now
35
59
  self.status = STATE_WAITING unless self.status
36
60
  self.next_fire_at = trigger.next_fire_at
37
61
  self.class.jobs << self
@@ -41,6 +65,15 @@ module SayWhen
41
65
  self.next_fire_at.to_i <=> job.next_fire_at.to_i
42
66
  end
43
67
 
68
+ def fired(fired_at=Time.now)
69
+ super
70
+ self.updated_at = Time.now
71
+ end
72
+
73
+ def release
74
+ super
75
+ self.updated_at = Time.now
76
+ end
44
77
  end
45
78
 
46
79
  end
@@ -6,17 +6,4 @@ namespace :say_when do
6
6
  SayWhen::Scheduler.start
7
7
  end
8
8
 
9
- # # Preload app files if this is Rails
10
- # # thanks resque
11
- # task :preload => :setup do
12
- # if defined?(Rails) && Rails.respond_to?(:application)
13
- # # Rails 3
14
- # Rails.application.eager_load!
15
- # elsif defined?(Rails::Initializer)
16
- # # Rails 2.3
17
- # $rails_rake_task = false
18
- # Rails::Initializer.run :load_application_classes
19
- # end
20
- # end
21
-
22
- end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module SayWhen
2
- VERSION = "0.2.6"
2
+ VERSION = "0.3.0"
3
3
  end
data/say_when.gemspec CHANGED
@@ -18,9 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.add_development_dependency "activemessaging", '~> 0.9.0'
19
19
  s.add_development_dependency "activesupport", '~> 2.3.14'
20
20
  s.add_development_dependency "activerecord", '~> 2.3.14'
21
- s.add_development_dependency "mongoid", '~> 1.9.5'
22
21
  s.add_development_dependency 'rspec', "~> 1.3"
23
22
  s.add_development_dependency 'sqlite3'
24
- s.add_development_dependency 'rake'
23
+ s.add_development_dependency 'rake', '~> 0.8.7'
25
24
 
26
25
  end
@@ -5,6 +5,8 @@ require File.dirname(__FILE__) + '/../../../../lib/say_when/storage/active_recor
5
5
  describe SayWhen::Storage::ActiveRecord::Job do
6
6
 
7
7
  before(:each) do
8
+ SayWhen::Storage::ActiveRecord::Job.delete_all
9
+
8
10
  @valid_attributes = {
9
11
  :trigger_strategy => :cron,
10
12
  :trigger_options => {:expression => '0 0 12 ? * * *', :time_zone => 'Pacific Time (US & Canada)'},
@@ -48,6 +50,18 @@ describe SayWhen::Storage::ActiveRecord::Job do
48
50
  j.next_fire_at.should == ce.next_fire_at
49
51
  end
50
52
 
53
+ it "resets acquired jobs" do
54
+ old = 2.hours.ago
55
+ j = SayWhen::Storage::ActiveRecord::Job.create!(@valid_attributes.merge({
56
+ :status => 'acquired', :updated_at => old, :created_at => old
57
+ }))
58
+
59
+ SayWhen::Storage::ActiveRecord::Job.reset_acquired(3600)
60
+
61
+ j.reload
62
+ j.status.should == 'waiting'
63
+ end
64
+
51
65
  it "can find the next job" do
52
66
  j2_opts = {
53
67
  :trigger_strategy => :cron,
@@ -4,6 +4,7 @@ require File.dirname(__FILE__) + '/../../../../lib/say_when/storage/memory/job'
4
4
  describe SayWhen::Store::Memory::Job do
5
5
 
6
6
  before(:each) do
7
+ SayWhen::Store::Memory::Job._reset
7
8
  @valid_attributes = {
8
9
  :name => 'Memory::Job::Test',
9
10
  :group => 'Test',
@@ -28,4 +29,17 @@ describe SayWhen::Store::Memory::Job do
28
29
  j.execute.should == 1
29
30
  end
30
31
 
32
+ it "can reset acquired jobs" do
33
+ j = SayWhen::Store::Memory::Job.new(@valid_attributes)
34
+ j.status = 'acquired'
35
+ j.updated_at = 2.hours.ago
36
+ SayWhen::Store::Memory::Job.reset_acquired(3600)
37
+ j.status.should == 'waiting'
38
+ end
39
+
40
+ it "can find the next job" do
41
+ j = SayWhen::Store::Memory::Job.new(@valid_attributes)
42
+ next_job = SayWhen::Store::Memory::Job.acquire_next(1.day.since)
43
+ next_job.should == j
44
+ end
31
45
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: say_when
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 6
10
- version: 0.2.6
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Andrew Kuklewicz
@@ -15,10 +15,10 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-10-30 00:00:00 Z
18
+ date: 2016-05-17 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: activemessaging
21
+ prerelease: false
22
22
  version_requirements: &id001 !ruby/object:Gem::Requirement
23
23
  none: false
24
24
  requirements:
@@ -30,11 +30,11 @@ dependencies:
30
30
  - 9
31
31
  - 0
32
32
  version: 0.9.0
33
- type: :development
34
- prerelease: false
35
33
  requirement: *id001
34
+ name: activemessaging
35
+ type: :development
36
36
  - !ruby/object:Gem::Dependency
37
- name: activesupport
37
+ prerelease: false
38
38
  version_requirements: &id002 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
@@ -46,11 +46,11 @@ dependencies:
46
46
  - 3
47
47
  - 14
48
48
  version: 2.3.14
49
- type: :development
50
- prerelease: false
51
49
  requirement: *id002
50
+ name: activesupport
51
+ type: :development
52
52
  - !ruby/object:Gem::Dependency
53
- name: activerecord
53
+ prerelease: false
54
54
  version_requirements: &id003 !ruby/object:Gem::Requirement
55
55
  none: false
56
56
  requirements:
@@ -62,28 +62,12 @@ dependencies:
62
62
  - 3
63
63
  - 14
64
64
  version: 2.3.14
65
- type: :development
66
- prerelease: false
67
65
  requirement: *id003
68
- - !ruby/object:Gem::Dependency
69
- name: mongoid
70
- version_requirements: &id004 !ruby/object:Gem::Requirement
71
- none: false
72
- requirements:
73
- - - ~>
74
- - !ruby/object:Gem::Version
75
- hash: 57
76
- segments:
77
- - 1
78
- - 9
79
- - 5
80
- version: 1.9.5
66
+ name: activerecord
81
67
  type: :development
82
- prerelease: false
83
- requirement: *id004
84
68
  - !ruby/object:Gem::Dependency
85
- name: rspec
86
- version_requirements: &id005 !ruby/object:Gem::Requirement
69
+ prerelease: false
70
+ version_requirements: &id004 !ruby/object:Gem::Requirement
87
71
  none: false
88
72
  requirements:
89
73
  - - ~>
@@ -93,12 +77,12 @@ dependencies:
93
77
  - 1
94
78
  - 3
95
79
  version: "1.3"
80
+ requirement: *id004
81
+ name: rspec
96
82
  type: :development
97
- prerelease: false
98
- requirement: *id005
99
83
  - !ruby/object:Gem::Dependency
100
- name: sqlite3
101
- version_requirements: &id006 !ruby/object:Gem::Requirement
84
+ prerelease: false
85
+ version_requirements: &id005 !ruby/object:Gem::Requirement
102
86
  none: false
103
87
  requirements:
104
88
  - - ">="
@@ -107,23 +91,25 @@ dependencies:
107
91
  segments:
108
92
  - 0
109
93
  version: "0"
94
+ requirement: *id005
95
+ name: sqlite3
110
96
  type: :development
111
- prerelease: false
112
- requirement: *id006
113
97
  - !ruby/object:Gem::Dependency
114
- name: rake
115
- version_requirements: &id007 !ruby/object:Gem::Requirement
98
+ prerelease: false
99
+ version_requirements: &id006 !ruby/object:Gem::Requirement
116
100
  none: false
117
101
  requirements:
118
- - - ">="
102
+ - - ~>
119
103
  - !ruby/object:Gem::Version
120
- hash: 3
104
+ hash: 49
121
105
  segments:
122
106
  - 0
123
- version: "0"
107
+ - 8
108
+ - 7
109
+ version: 0.8.7
110
+ requirement: *id006
111
+ name: rake
124
112
  type: :development
125
- prerelease: false
126
- requirement: *id007
127
113
  description:
128
114
  email:
129
115
  - andrew@prx.org
@@ -135,6 +121,7 @@ extra_rdoc_files: []
135
121
 
136
122
  files:
137
123
  - .gitignore
124
+ - .travis.yml
138
125
  - Gemfile
139
126
  - Rakefile
140
127
  - generators/say_when_migration/say_when_migration_generator.rb
@@ -154,7 +141,6 @@ files:
154
141
  - lib/say_when/storage/active_record/job_execution.rb
155
142
  - lib/say_when/storage/memory/base.rb
156
143
  - lib/say_when/storage/memory/job.rb
157
- - lib/say_when/storage/mongoid/job.rb
158
144
  - lib/say_when/tasks.rb
159
145
  - lib/say_when/triggers/base.rb
160
146
  - lib/say_when/triggers/cron_strategy.rb
@@ -165,15 +151,12 @@ files:
165
151
  - say_when.gemspec
166
152
  - spec/active_record_spec_helper.rb
167
153
  - spec/db/schema.rb
168
- - spec/db/test.db
169
- - spec/mongoid_spec_helper.rb
170
154
  - spec/say_when/cron_expression_spec.rb
171
155
  - spec/say_when/processor/active_messaging_spec.rb
172
156
  - spec/say_when/scheduler_spec.rb
173
157
  - spec/say_when/storage/active_record/job_spec.rb
174
158
  - spec/say_when/storage/memory/job_spec.rb
175
159
  - spec/say_when/storage/memory/trigger_spec.rb
176
- - spec/say_when/storage/mongoid/trigger_spec.rb
177
160
  - spec/say_when/triggers/once_strategy_spec.rb
178
161
  - spec/spec.opts
179
162
  - spec/spec_helper.rb
@@ -207,22 +190,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
190
  requirements: []
208
191
 
209
192
  rubyforge_project:
210
- rubygems_version: 1.8.22
193
+ rubygems_version: 1.8.15
211
194
  signing_key:
212
195
  specification_version: 3
213
196
  summary: Scheduling system for programmatically defined and stored jobs.
214
197
  test_files:
215
198
  - spec/active_record_spec_helper.rb
216
199
  - spec/db/schema.rb
217
- - spec/db/test.db
218
- - spec/mongoid_spec_helper.rb
219
200
  - spec/say_when/cron_expression_spec.rb
220
201
  - spec/say_when/processor/active_messaging_spec.rb
221
202
  - spec/say_when/scheduler_spec.rb
222
203
  - spec/say_when/storage/active_record/job_spec.rb
223
204
  - spec/say_when/storage/memory/job_spec.rb
224
205
  - spec/say_when/storage/memory/trigger_spec.rb
225
- - spec/say_when/storage/mongoid/trigger_spec.rb
226
206
  - spec/say_when/triggers/once_strategy_spec.rb
227
207
  - spec/spec.opts
228
208
  - spec/spec_helper.rb
@@ -1,15 +0,0 @@
1
- require 'mongoid'
2
-
3
- module SayWhen
4
- module Store
5
- module Mongoid
6
-
7
- class Job
8
-
9
- include Mongoid::Document
10
-
11
- end
12
-
13
- end
14
- end
15
- end
data/spec/db/test.db DELETED
Binary file
@@ -1,7 +0,0 @@
1
- require 'mongoid'
2
-
3
- Mongoid.configure do |config|
4
- config.master = Mongo::Connection.new.db("say_when_test")
5
- end
6
-
7
- # Mongoid.logger = Logger.new($stdout)
@@ -1,57 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../../spec_helper'
2
- require File.dirname(__FILE__) + '/../../../mongoid_spec_helper'
3
- # require File.dirname(__FILE__) + '/../../../../lib/say_when/store/mongoid/trigger'
4
-
5
- # describe SayWhen::Store::Mongoid::Trigger do
6
-
7
- # before(:each) do
8
- # SayWhen::Store::Mongoid::Trigger.delete_all
9
- # @valid_attributes = {
10
- # :expression => '0 0 12 ? * * *',
11
- # :time_zone => 'Pacific Time (US & Canada)'
12
- # }
13
- # end
14
-
15
- # it "can be created using new and save" do
16
- # t = SayWhen::Store::Mongoid::Trigger.new(@valid_attributes)
17
- # t.should be_valid
18
- # t.save
19
- # end
20
-
21
- # it "sets a cron_expression" do
22
- # t = SayWhen::Store::Mongoid::Trigger.create(@valid_attributes)
23
- # t.cron_expression.should_not be_nil
24
- # t.cron_expression.expression.should == '0 0 12 ? * * *'
25
- # t.cron_expression.time_zone.should == 'Pacific Time (US & Canada)'
26
- # end
27
-
28
- # it "has a waiting state on create" do
29
- # t = SayWhen::Store::Mongoid::Trigger.create(@valid_attributes)
30
- # t.status.should == SayWhen::BaseTrigger::STATE_WAITING
31
- # end
32
-
33
- # it "has a next fire at set on create" do
34
- # ce = SayWhen::CronExpression.new(@valid_attributes[:expression], @valid_attributes[:time_zone])
35
- # t = SayWhen::Store::Mongoid::Trigger.create(@valid_attributes)
36
- # t.status.should == SayWhen::BaseTrigger::STATE_WAITING
37
- # t.next_fire_at.should == ce.next_fire_at(t.created_at)
38
- # end
39
-
40
- # it "can be fired" do
41
- # ce = SayWhen::CronExpression.new(@valid_attributes[:expression], @valid_attributes[:time_zone])
42
- # t = SayWhen::Store::Mongoid::Trigger.create(@valid_attributes)
43
- # nfa = ce.last_fire_at(t.created_at - 1.second)
44
- # lfa = ce.last_fire_at(nfa - 1.second)
45
- # t.next_fire_at = nfa
46
- # t.last_fire_at = lfa
47
-
48
- # now = Time.now
49
- # Time.stub!(:now).and_return(now)
50
-
51
- # t.fired
52
- # t.next_fire_at.should == ce.next_fire_at(now)
53
- # t.last_fire_at.should == now
54
- # t.status.should == SayWhen::BaseTrigger::STATE_WAITING
55
- # end
56
-
57
- # end