symphony-metronome 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/History.md +27 -0
- data/README.md +224 -0
- data/lib/symphony/metronome/intervalexpression.rb +2 -2
- data/lib/symphony/metronome/scheduledevent.rb +8 -11
- data/lib/symphony/metronome/scheduler.rb +12 -14
- data/lib/symphony/metronome.rb +5 -8
- data.tar.gz.sig +0 -0
- metadata +131 -54
- metadata.gz.sig +0 -0
- data/README.rdoc +0 -207
- data/bin/metronome-exp +0 -33
- data/lib/symphony/metronome/intervalexpression.rl +0 -599
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7924d613c9657d90bf4214bb9408d1f23bb9ecb677fdad75e47517fb8f30846d
|
4
|
+
data.tar.gz: e1ee7ba0db1f90c3d0e71ab05209493590d00f00d560b743938f986e869a872c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4146c565cf102bf1adf6979a4f57ffd5c2f8baedc8f6570da3654b0d65fd5183ae53778ee17d489de735c2f235875951d03347a84fe6143cf7fae86656ce95f
|
7
|
+
data.tar.gz: d9e6dd409f5206a1d98c8899626c2921465b013c2c160c84ffc43add998c3e32a672f5e75dbaa2f0c68f082dc6329f8b6cbaa9ac1def09847add88f7ca7549a2
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/History.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Release History for Symphony-Metronome
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## v0.3.0 [2023-03-20] Mahlon E. Smith <mahlon@martini.nu>
|
6
|
+
|
7
|
+
- Updates for Ruby 3.
|
8
|
+
- Dependency updates.
|
9
|
+
- Conversion to more modern ruby development tooling.
|
10
|
+
|
11
|
+
|
12
|
+
## v0.2.1 [2015-07-08] Mahlon E. Smith <mahlon@martini.nu>
|
13
|
+
|
14
|
+
Enhancements:
|
15
|
+
|
16
|
+
- Add "last run" timestamps for recurring events.
|
17
|
+
|
18
|
+
Fixes:
|
19
|
+
|
20
|
+
- Repair exact start times for recurring events, ie
|
21
|
+
'starting at 2015-01-01 09:00:00 run every other minute for 2 days'
|
22
|
+
|
23
|
+
|
24
|
+
## v0.1.0 [2014-04-22] Mahlon E. Smith <mahlon@martini.nu>
|
25
|
+
|
26
|
+
Initial release.
|
27
|
+
|
data/README.md
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
# Metronome
|
2
|
+
|
3
|
+
home
|
4
|
+
: https://code.martini.nu/fossil/symphony-metronome
|
5
|
+
|
6
|
+
docs
|
7
|
+
: https://martini.nu/docs/symphony-metronome
|
8
|
+
|
9
|
+
github_mirror
|
10
|
+
: https://github.com/mahlonsmith/symphony-metronome
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
## Description
|
15
|
+
|
16
|
+
Metronome is an interval scheduler and task runner. It can be used locally as a
|
17
|
+
cron replacement, or as a network-wide job executor. It is intended to be run
|
18
|
+
alongside Symphony, a Ruby AMQP event consumer.
|
19
|
+
|
20
|
+
Events are stored via simple database rows, and optionally themselves managed
|
21
|
+
via AMQP events. Interval/time values are expressed with intuitive English
|
22
|
+
phrases, ie.: 'at 2pm', or 'Starting in 20 minutes, run every 10 seconds and
|
23
|
+
then finish in 2 days', or 'execute 12 times during the next minute'.
|
24
|
+
|
25
|
+
## Synopsis
|
26
|
+
|
27
|
+
Here's an example of a simple cron clone:
|
28
|
+
|
29
|
+
```
|
30
|
+
#!ruby
|
31
|
+
|
32
|
+
require 'symphony/metronome'
|
33
|
+
|
34
|
+
Symphony.load_config
|
35
|
+
|
36
|
+
Symphony::Metronome.run do |opts, id|
|
37
|
+
Thread.new do
|
38
|
+
pid = fork { exec opts.delete('command') }
|
39
|
+
Process.waitpid( pid )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
|
45
|
+
And here's a simplistic timed AMQP message broadcaster, using existing Symphony
|
46
|
+
connection information:
|
47
|
+
|
48
|
+
```
|
49
|
+
#!ruby
|
50
|
+
|
51
|
+
require 'symphony/metronome'
|
52
|
+
|
53
|
+
Symphony.load_config
|
54
|
+
|
55
|
+
Symphony::Metronome.run do |opts, id|
|
56
|
+
key = opts.delete( 'routing_key' ) or next
|
57
|
+
exchange = Symphony::Queue.amqp_exchange
|
58
|
+
exchange.publish( 'hi from Metronome!', routing_key: key )
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
## Adding Actions
|
63
|
+
|
64
|
+
There are two primary components to Metronome -- getting actions into
|
65
|
+
its database, and performing some task with those actions when the time
|
66
|
+
is appropriate.
|
67
|
+
|
68
|
+
By default, Metronome will start up an AMQP listener, attached to your
|
69
|
+
Symphony exchange, and wait for new scheduling messages. There are two
|
70
|
+
events it will take action on:
|
71
|
+
|
72
|
+
metronome.create:
|
73
|
+
|
74
|
+
Create a new scheduled event. The payload should be a hash. An
|
75
|
+
'expression' key is required, that provides the interval description.
|
76
|
+
Anything additional is serialized to 'options', that are passed to the
|
77
|
+
block when the interval fires. You can populate it with anything
|
78
|
+
your task requires to execute.
|
79
|
+
|
80
|
+
metronome.delete:
|
81
|
+
|
82
|
+
The payload is the row ID of the action. Metronome removes it from
|
83
|
+
the database.
|
84
|
+
|
85
|
+
If you'd prefer not to use the AMQP listener, you can put actions into
|
86
|
+
Metronome using any database methodology you please. When the daemon
|
87
|
+
starts up or receives a HUP signal, it will re-read and schedule out
|
88
|
+
upcoming work.
|
89
|
+
|
90
|
+
|
91
|
+
## Options
|
92
|
+
|
93
|
+
Metronome uses
|
94
|
+
[Configurability](https://rubygems.org/gems/configurability) to determine
|
95
|
+
behavior. The configuration is a [YAML](http://www.yaml.org/) file. It
|
96
|
+
shares AMQP configuration with Symphony, and adds metronome specific
|
97
|
+
controls in the 'metronome' key.
|
98
|
+
|
99
|
+
metronome:
|
100
|
+
splay: 0
|
101
|
+
listen: true
|
102
|
+
db: sqlite:///tmp/metronome.db
|
103
|
+
|
104
|
+
|
105
|
+
### splay
|
106
|
+
|
107
|
+
Randomize all start times for actions by this many seconds on either
|
108
|
+
side of the original execution time. Defaults to none.
|
109
|
+
|
110
|
+
### listen
|
111
|
+
|
112
|
+
Start up an AMQP listener using Symphony configuration, for remote
|
113
|
+
administration of schedule events. Defaults to true.
|
114
|
+
|
115
|
+
### db
|
116
|
+
|
117
|
+
A [Sequel](https://rubygems.org/gems/sequel) connection URI. Currently,
|
118
|
+
Metronome is tested under SQLite and PostgreSQL. Defaults to a SQLite
|
119
|
+
file at /tmp/metronome.db.
|
120
|
+
|
121
|
+
|
122
|
+
## Scheduling Examples
|
123
|
+
|
124
|
+
Note that Metronome is designed as an interval scheduler, not a
|
125
|
+
calendaring app. It doesn't have any concepts around phrases like "next
|
126
|
+
tuesday", or "the 3rd sunday after christmas". If that's what you're
|
127
|
+
after, check out the [chronic](http://rubygems.org/gems/chronic)
|
128
|
+
library instead.
|
129
|
+
|
130
|
+
Here are a small set of example expressions. Feel free to use the
|
131
|
+
*metronome-exp* utility to get a feel for what Metronome anticipates.
|
132
|
+
|
133
|
+
```
|
134
|
+
in 30.5 minutes
|
135
|
+
once an hour
|
136
|
+
every 15 minutes for 2 days
|
137
|
+
at 2014-05-01
|
138
|
+
at 2014-04-01 14:00:25
|
139
|
+
at 2pm
|
140
|
+
starting at 2pm once a day
|
141
|
+
start in 1 hour from now run every 5 seconds end at 11:15pm
|
142
|
+
every other hour
|
143
|
+
run every 7th minute for a day
|
144
|
+
once a day ending in 1 week
|
145
|
+
run once a minute for an hour starting in 6 days
|
146
|
+
10 times a minute for 2 days
|
147
|
+
run 45 times every hour
|
148
|
+
30 times per day
|
149
|
+
start at 2010-01-02 run 12 times and end on 2010-01-03
|
150
|
+
starting in an hour from now run 6 times a minute for 2 hours
|
151
|
+
beginning a day from now, run 30 times per minute and finish in 2 weeks
|
152
|
+
execute 12 times during the next 2 minutes
|
153
|
+
once a minute beginning in 5 minutes
|
154
|
+
```
|
155
|
+
|
156
|
+
In general, you can use reasonably intuitive phrasings. Capitalization,
|
157
|
+
whitespace, and punctuation doesn't matter. When describing numbers,
|
158
|
+
use digit/integer form instead of words, ie: '1 hour' instead of 'one
|
159
|
+
hour'.
|
160
|
+
|
161
|
+
|
162
|
+
## Installation
|
163
|
+
|
164
|
+
```
|
165
|
+
gem install symphony-metronome
|
166
|
+
```
|
167
|
+
|
168
|
+
## Contributing
|
169
|
+
|
170
|
+
You can check out the source via Fossil from the following uri:
|
171
|
+
|
172
|
+
% fossil clone https://code.martini.nu/fossil/symphony-metronome
|
173
|
+
|
174
|
+
or via its GitHub mirror at:
|
175
|
+
|
176
|
+
% git clone https://github.com/mahlonsmith/Symphony-Metronome
|
177
|
+
|
178
|
+
After checking out the source, run:
|
179
|
+
|
180
|
+
$ gem install -Ng
|
181
|
+
$ rake setup
|
182
|
+
|
183
|
+
This will install dependencies, and do any other necessary setup for
|
184
|
+
development.
|
185
|
+
|
186
|
+
Please report any issues
|
187
|
+
[here](https://code.martini.nu/fossil/symphony-metronome/tktnew).
|
188
|
+
|
189
|
+
|
190
|
+
## Authors
|
191
|
+
|
192
|
+
- Mahlon E. Smith <mahlon@martini.nu>
|
193
|
+
|
194
|
+
|
195
|
+
## License
|
196
|
+
|
197
|
+
Copyright (c) 2014-2023 Mahlon E. Smith
|
198
|
+
All rights reserved.
|
199
|
+
|
200
|
+
Redistribution and use in source and binary forms, with or without
|
201
|
+
modification, are permitted provided that the following conditions are met:
|
202
|
+
|
203
|
+
* Redistributions of source code must retain the above copyright notice,
|
204
|
+
this list of conditions and the following disclaimer.
|
205
|
+
|
206
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
207
|
+
this list of conditions and the following disclaimer in the documentation
|
208
|
+
and/or other materials provided with the distribution.
|
209
|
+
|
210
|
+
* Neither the name of the author/s, nor the names of the project's
|
211
|
+
contributors may be used to endorse or promote products derived from this
|
212
|
+
software without specific prior written permission.
|
213
|
+
|
214
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
215
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
216
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
217
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
218
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
219
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
220
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
221
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
222
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
223
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
224
|
+
|
@@ -1916,7 +1916,7 @@ self.interval_expression_en_main = 1;
|
|
1916
1916
|
### an expression was generated, you can 'reconstitute' an interval
|
1917
1917
|
### object this way.
|
1918
1918
|
###
|
1919
|
-
def self::parse( exp, time=
|
1919
|
+
def self::parse( exp, time=Time.now )
|
1920
1920
|
|
1921
1921
|
# Normalize the expression before parsing
|
1922
1922
|
#
|
@@ -1927,7 +1927,7 @@ self.interval_expression_en_main = 1;
|
|
1927
1927
|
gsub( /([:\-])+/, '\1' ). # collapse multiple - or : chars
|
1928
1928
|
gsub( /\.+$/, '' ) # trailing periods
|
1929
1929
|
|
1930
|
-
event = new( exp, time
|
1930
|
+
event = new( exp, time )
|
1931
1931
|
data = event.instance_variable_get( :@data )
|
1932
1932
|
|
1933
1933
|
# Ragel interface variables
|
@@ -21,19 +21,17 @@ class Symphony::Metronome::ScheduledEvent
|
|
21
21
|
config_key :metronome
|
22
22
|
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
30
|
-
|
31
|
-
class << self
|
24
|
+
### Configurability API.
|
25
|
+
###
|
26
|
+
configurability do
|
27
|
+
|
28
|
+
##
|
32
29
|
# A Sequel-style DB connection URI.
|
33
|
-
|
30
|
+
setting :db, default: 'sqlite:///tmp/metronome.db'
|
34
31
|
|
32
|
+
##
|
35
33
|
# Adjust recurring intervals by a random window.
|
36
|
-
|
34
|
+
setting :splay, default: 0
|
37
35
|
end
|
38
36
|
|
39
37
|
|
@@ -62,7 +60,6 @@ class Symphony::Metronome::ScheduledEvent
|
|
62
60
|
### Delete any rows that are invalid expressions.
|
63
61
|
###
|
64
62
|
def self::load
|
65
|
-
now = Time.now
|
66
63
|
events = SortedSet.new
|
67
64
|
|
68
65
|
# Force reset the DB handle.
|
@@ -17,22 +17,15 @@ class Symphony::Metronome::Scheduler
|
|
17
17
|
# Signals the daemon responds to.
|
18
18
|
SIGNALS = [ :HUP, :INT, :TERM ]
|
19
19
|
|
20
|
-
CONFIG_DEFAULTS = {
|
21
|
-
:listen => false
|
22
|
-
}
|
23
20
|
|
24
|
-
|
21
|
+
### Configurability API.
|
22
|
+
###
|
23
|
+
configurability do
|
24
|
+
|
25
25
|
# Should Metronome register and schedule events via AMQP?
|
26
26
|
# If +false+, you'll need a separate way to add event actions
|
27
27
|
# to the database, and manually HUP the daemon.
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
### Configurability API
|
32
|
-
###
|
33
|
-
def self::configure( config=nil )
|
34
|
-
config = self.defaults.merge( config || {} )
|
35
|
-
@listen = config.delete( :listen )
|
28
|
+
setting :listen, default: false
|
36
29
|
end
|
37
30
|
|
38
31
|
|
@@ -131,17 +124,19 @@ class Symphony::Metronome::Scheduler
|
|
131
124
|
def process_events
|
132
125
|
now = Time.now
|
133
126
|
|
127
|
+
events_to_delete = []
|
128
|
+
events_to_add = []
|
134
129
|
self.queue.each do |ev|
|
135
130
|
next unless now >= ev.runtime
|
136
131
|
|
137
|
-
|
132
|
+
events_to_delete << ev
|
138
133
|
rv = ev.fire( &@proc )
|
139
134
|
|
140
135
|
# Reschedule the event and place it back on the queue.
|
141
136
|
#
|
142
137
|
if ev.event.recurring
|
143
138
|
ev.reset_runtime
|
144
|
-
|
139
|
+
events_to_add << ev unless rv.nil?
|
145
140
|
|
146
141
|
# It was a single run event, torch it!
|
147
142
|
#
|
@@ -150,6 +145,9 @@ class Symphony::Metronome::Scheduler
|
|
150
145
|
|
151
146
|
end
|
152
147
|
end
|
148
|
+
|
149
|
+
events_to_delete.map{|e| self.queue.delete( e ) }
|
150
|
+
events_to_add.map{|e| self.queue.add( e ) }
|
153
151
|
end
|
154
152
|
|
155
153
|
end # Symphony::Metronome::Scheduler
|
data/lib/symphony/metronome.rb
CHANGED
@@ -9,10 +9,7 @@ module Symphony::Metronome
|
|
9
9
|
Configurability
|
10
10
|
|
11
11
|
# Library version constant
|
12
|
-
VERSION = '0.
|
13
|
-
|
14
|
-
# Version-control revision constant
|
15
|
-
REVISION = %q$Revision: 87e45dca63b3 $
|
12
|
+
VERSION = '0.3.0'
|
16
13
|
|
17
14
|
# The name of the environment variable to check for config file overrides
|
18
15
|
CONFIG_ENV = 'METRONOME_CONFIG'
|
@@ -23,11 +20,11 @@ module Symphony::Metronome
|
|
23
20
|
# The data directory that contains migration files.
|
24
21
|
#
|
25
22
|
DATADIR = if ENV['METRONOME_DATADIR']
|
26
|
-
Pathname
|
27
|
-
elsif Gem.
|
28
|
-
Pathname
|
23
|
+
Pathname( ENV['METRONOME_DATADIR'] )
|
24
|
+
elsif Gem.loaded_specs[ 'symphony-metronome' ] && File.exist?( Gem.loaded_specs['symphony-metronome'].datadir )
|
25
|
+
Pathname( Gem.loaded_specs['symphony-metronome'].datadir )
|
29
26
|
else
|
30
|
-
Pathname
|
27
|
+
Pathname( __FILE__ ).dirname.parent.parent + 'data/symphony-metronome'
|
31
28
|
end
|
32
29
|
|
33
30
|
|
data.tar.gz.sig
CHANGED
Binary file
|