say_when 0.2.6 → 0.3.0

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.
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