Add 'last run' timestamps for recurring events, and take these into
account when firing events on daemon rescheduling (via HUP.) FossilOrigin-Name: 1620e80e8bddd27741569dea0ff4bd84ba29429861ed49d17f5777c238ae4876
This commit is contained in:
parent
b18647f6a5
commit
7c2954b0ad
6 changed files with 79 additions and 8 deletions
11
README.rdoc
11
README.rdoc
|
|
@ -50,7 +50,7 @@ Symphony connection information:
|
||||||
|
|
||||||
== Adding Actions
|
== Adding Actions
|
||||||
|
|
||||||
There are two primary components to Metronome -- getting actions into
|
There are two primary components to Metronome -- getting schedules into
|
||||||
its database, and performing some task with those actions when the time
|
its database, and performing some task with those actions when the time
|
||||||
is appropriate.
|
is appropriate.
|
||||||
|
|
||||||
|
|
@ -76,6 +76,15 @@ starts up or receives a HUP signal, it will re-read and schedule out
|
||||||
upcoming work.
|
upcoming work.
|
||||||
|
|
||||||
|
|
||||||
|
== Performing Work
|
||||||
|
|
||||||
|
Calling 'run' on the Metronome class is a blocking call, waking up upon
|
||||||
|
a scheduled event. The run method expects a ruby block, and it receives
|
||||||
|
the payload and the database ID of the scheduled event. Metronome is
|
||||||
|
unopinioned, what you do within this block is entirely up to you. See
|
||||||
|
the Synopsis section above for some examples.
|
||||||
|
|
||||||
|
|
||||||
== Options
|
== Options
|
||||||
|
|
||||||
Metronome uses
|
Metronome uses
|
||||||
|
|
|
||||||
1
Rakefile
1
Rakefile
|
|
@ -140,4 +140,5 @@ lib/symphony/metronome/mixins.rb
|
||||||
lib/symphony/metronome/intervalexpression.rb
|
lib/symphony/metronome/intervalexpression.rb
|
||||||
lib/symphony/metronome/scheduledevent.rb
|
lib/symphony/metronome/scheduledevent.rb
|
||||||
data/symphony-metronome/migrations/20140419_initial.rb
|
data/symphony-metronome/migrations/20140419_initial.rb
|
||||||
|
data/symphony-metronome/migrations/20141028_lastrun.rb
|
||||||
README.rdoc
|
README.rdoc
|
||||||
|
|
|
||||||
23
data/symphony-metronome/migrations/20141028_lastrun.rb
Normal file
23
data/symphony-metronome/migrations/20141028_lastrun.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# vim: set nosta noet ts=4 sw=4:
|
||||||
|
|
||||||
|
### Add a 'lastrun' time stamp for recurring events.
|
||||||
|
###
|
||||||
|
class Lastrun < Sequel::Migration
|
||||||
|
|
||||||
|
def initialize( db )
|
||||||
|
@db = db
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
if @db.adapter_scheme == :postgres
|
||||||
|
add_column :metronome, :lastrun, timestamptz
|
||||||
|
else
|
||||||
|
add_column :metronome, :lastrun, DateTime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_column :metronome, :lastrun
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@ module Symphony::Metronome
|
||||||
Configurability
|
Configurability
|
||||||
|
|
||||||
# Library version constant
|
# Library version constant
|
||||||
VERSION = '0.1.0'
|
VERSION = '0.2.0'
|
||||||
|
|
||||||
# Version-control revision constant
|
# Version-control revision constant
|
||||||
REVISION = %q$Revision$
|
REVISION = %q$Revision$
|
||||||
|
|
@ -74,6 +74,5 @@ module Symphony::Metronome
|
||||||
return Symphony::Metronome::Scheduler.run( &block )
|
return Symphony::Metronome::Scheduler.run( &block )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end # Symphony::Metronome
|
end # Symphony::Metronome
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
# Configure defaults.
|
# Configure defaults.
|
||||||
#
|
#
|
||||||
CONFIG_DEFAULTS = {
|
CONFIG_DEFAULTS = {
|
||||||
db: 'sqlite:///tmp/metronome.db',
|
:db => 'sqlite:///tmp/metronome.db',
|
||||||
splay: 0
|
:splay => 0
|
||||||
}
|
}
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
@ -52,6 +52,7 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
#
|
#
|
||||||
migrations_dir = Symphony::Metronome::DATADIR + 'migrations'
|
migrations_dir = Symphony::Metronome::DATADIR + 'migrations'
|
||||||
unless Sequel::Migrator.is_current?( self.db, migrations_dir.to_s )
|
unless Sequel::Migrator.is_current?( self.db, migrations_dir.to_s )
|
||||||
|
Loggability[ Symphony ].info "Installing database schema..."
|
||||||
Sequel::Migrator.apply( self.db, migrations_dir.to_s )
|
Sequel::Migrator.apply( self.db, migrations_dir.to_s )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -97,6 +98,8 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
@event = Symphony::Metronome::IntervalExpression.parse( row[:expression], row[:created] )
|
@event = Symphony::Metronome::IntervalExpression.parse( row[:expression], row[:created] )
|
||||||
@options = row.delete( :options )
|
@options = row.delete( :options )
|
||||||
@id = row.delete( :id )
|
@id = row.delete( :id )
|
||||||
|
@ds = self.class.db[ :metronome ].filter( :id => self.id )
|
||||||
|
|
||||||
self.reset_runtime
|
self.reset_runtime
|
||||||
|
|
||||||
unless self.class.splay.zero?
|
unless self.class.splay.zero?
|
||||||
|
|
@ -105,6 +108,9 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The sequel dataset representing this event.
|
||||||
|
attr_reader :ds
|
||||||
|
|
||||||
# The parsed interval expression.
|
# The parsed interval expression.
|
||||||
attr_reader :event
|
attr_reader :event
|
||||||
|
|
||||||
|
|
@ -133,7 +139,21 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
# Otherwise, the event should already be running (start time has already
|
# Otherwise, the event should already be running (start time has already
|
||||||
# elapsed), so schedule it forward on it's next interval iteration.
|
# elapsed), so schedule it forward on it's next interval iteration.
|
||||||
#
|
#
|
||||||
@runtime = now + self.event.interval
|
# If it's a recurring event that has run before, consider the elapsed time
|
||||||
|
# as part of the next calculation.
|
||||||
|
#
|
||||||
|
row = self.ds.first
|
||||||
|
if self.event.recurring && row
|
||||||
|
last = row[ :lastrun ]
|
||||||
|
if last && now > last
|
||||||
|
@runtime = now + self.event.interval - ( now - last )
|
||||||
|
else
|
||||||
|
@runtime = now + self.event.interval
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
@runtime = now + self.event.interval
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -141,13 +161,32 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
### deserialized options, the action ID to the supplied block if
|
### deserialized options, the action ID to the supplied block if
|
||||||
### this event is okay to execute.
|
### this event is okay to execute.
|
||||||
###
|
###
|
||||||
|
### If the event is recurring, perform additional checks against the
|
||||||
|
### last run time.
|
||||||
|
###
|
||||||
### Automatically remove the event if it has expired.
|
### Automatically remove the event if it has expired.
|
||||||
###
|
###
|
||||||
def fire
|
def fire
|
||||||
rv = self.event.fire?
|
rv = self.event.fire?
|
||||||
|
|
||||||
|
# Just based on the expression parser, is this event ready to fire?
|
||||||
|
#
|
||||||
if rv
|
if rv
|
||||||
opts = Yajl.load( self.options )
|
opts = Yajl.load( self.options )
|
||||||
|
|
||||||
|
# Don't fire recurring events unless their interval has elapsed.
|
||||||
|
# This prevents events from triggering when the daemon receives
|
||||||
|
# a HUP.
|
||||||
|
#
|
||||||
|
if self.event.recurring
|
||||||
|
now = Time.now
|
||||||
|
last = self.ds.first[ :lastrun ]
|
||||||
|
return false if last && now - last < self.event.interval
|
||||||
|
|
||||||
|
# Mark the time this recurring event was fired.
|
||||||
|
self.ds.update( :lastrun => Time.now )
|
||||||
|
end
|
||||||
|
|
||||||
yield opts, self.id
|
yield opts, self.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -160,7 +199,7 @@ class Symphony::Metronome::ScheduledEvent
|
||||||
###
|
###
|
||||||
def delete
|
def delete
|
||||||
self.log.debug "Removing action %p" % [ self.id ]
|
self.log.debug "Removing action %p" % [ self.id ]
|
||||||
self.class.db[ :metronome ].filter( :id => self.id ).delete
|
self.ds.delete
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class Symphony::Metronome::Scheduler
|
||||||
SIGNALS = [ :HUP, :INT, :TERM ]
|
SIGNALS = [ :HUP, :INT, :TERM ]
|
||||||
|
|
||||||
CONFIG_DEFAULTS = {
|
CONFIG_DEFAULTS = {
|
||||||
:listen => true
|
:listen => false
|
||||||
}
|
}
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue