redis-scheduler 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +14 -0
- data/README +38 -0
- data/lib/redis-scheduler.rb +90 -0
- metadata +51 -0
data/COPYING
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Whistlepig is copyright (c) 2011-2012 William Morgan <wmorgan@masanjin.net>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
7
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
* Neither the name of Whistlepig nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
9
|
+
|
10
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
11
|
+
|
12
|
+
|
13
|
+
khash.h copyright (c) 2008, by Attractive Chaos <attractivechaos@aol.co.uk>
|
14
|
+
See khash.h for terms.
|
data/README
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
This is a basic chronological scheduler for Redis. It allows you to schedule
|
2
|
+
items to be processed at arbitrary points in time (via #schedule!), and then
|
3
|
+
to easily retrieve only those items that are due to be processed (via #each!).
|
4
|
+
Items are represented as strings.
|
5
|
+
|
6
|
+
It does everything you'd want from a production scheduler:
|
7
|
+
* You can schedule arbitrary items at arbitrary times.
|
8
|
+
* It supports multiple simultaneous readers and writers.
|
9
|
+
* An exception causes the in-process item to be rescheduled at the original time.
|
10
|
+
* A crash leaves the item in an error queue, from which it can later be recovered.
|
11
|
+
* You can iterate over ready items in either blocking or non-blocking mode.
|
12
|
+
|
13
|
+
In non-blocking mode (the default), #each will iterate only over those work items
|
14
|
+
whose scheduled time is less than or equal to the current time, and then stop.
|
15
|
+
In blocking mode, #each will iterate over the same items, but will also block
|
16
|
+
until items are available. In blocking mode, #each will never return.
|
17
|
+
|
18
|
+
== Synopsis
|
19
|
+
|
20
|
+
r = Redis.new
|
21
|
+
s = RedisScheduler.new r, blocking: true
|
22
|
+
startt = Time.now
|
23
|
+
s.schedule! "a", startt + 10
|
24
|
+
s.schedule! "b", startt + 15
|
25
|
+
s.schedule! "c", startt + 20
|
26
|
+
s.each { |item, at| puts "#{Time.now - startt}: #{item}" }
|
27
|
+
|
28
|
+
prints:
|
29
|
+
|
30
|
+
10.03481788: a
|
31
|
+
15.05255288: b
|
32
|
+
20.06058172: c
|
33
|
+
... waits forever ...
|
34
|
+
|
35
|
+
== Bug reports
|
36
|
+
|
37
|
+
Please file bugs here: https://github.com/wmorgan/redis-scheduler/issues
|
38
|
+
Please send comments to: wmorgan-redis-scheduler-readme@masanjin.net.
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class RedisScheduler
|
2
|
+
include Enumerable
|
3
|
+
|
4
|
+
POLL_DELAY = 1.0 # seconds
|
5
|
+
CAS_DELAY = 0.5 # seconds
|
6
|
+
|
7
|
+
## options:
|
8
|
+
## namespace: prefix for redis data, e.g. "scheduler/"
|
9
|
+
## blocking: whether #each should block or return immediately if
|
10
|
+
## there are items to be processed immediately.
|
11
|
+
##
|
12
|
+
## Note that nonblocking mode may still actually block as part of the
|
13
|
+
## check-and-set semantics, i.e. block during contention from multiple
|
14
|
+
## clients. "Nonblocking" mode just refers to whether the scheduler
|
15
|
+
## should wait until events in the schedule are ready, or only return
|
16
|
+
## those items that are ready currently.
|
17
|
+
def initialize redis, opts={}
|
18
|
+
@redis = redis
|
19
|
+
@namespace = opts[:namespace]
|
20
|
+
@blocking = opts[:blocking]
|
21
|
+
|
22
|
+
@queue = [@namespace, "q"].join
|
23
|
+
@error_queue = [@namespace, "errorq"].join
|
24
|
+
@counter = [@namespace, "counter"].join
|
25
|
+
end
|
26
|
+
|
27
|
+
## schedule an item at a specific time. item will be converted to a
|
28
|
+
## string.
|
29
|
+
def schedule! item, time
|
30
|
+
id = @redis.incr @counter
|
31
|
+
@redis.zadd @queue, time.to_f, "#{id}:#{item}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def reset!
|
35
|
+
[@queue, @error_queue, @counter].each { |k| @redis.del k }
|
36
|
+
end
|
37
|
+
|
38
|
+
## yields items along with their scheduled times. only returns items
|
39
|
+
## on or after their scheduled times. items returned as strings. if
|
40
|
+
## @blocking is false, will stop once there are no more items that can
|
41
|
+
## be processed immediately; if it's true, will wait until items
|
42
|
+
## become available (and never terminate).
|
43
|
+
def each
|
44
|
+
while(x = get)
|
45
|
+
item, at = x
|
46
|
+
begin
|
47
|
+
yield item, at
|
48
|
+
rescue Exception # back in the hole!
|
49
|
+
schedule! item, at
|
50
|
+
raise
|
51
|
+
ensure
|
52
|
+
cleanup! item
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def get; @blocking ? blocking_get : nonblocking_get end
|
60
|
+
|
61
|
+
def blocking_get
|
62
|
+
sleep POLL_DELAY until(x = nonblocking_get)
|
63
|
+
x
|
64
|
+
end
|
65
|
+
|
66
|
+
class InvalidEntryException < StandardError; end
|
67
|
+
def nonblocking_get
|
68
|
+
catch :cas_retry do
|
69
|
+
@redis.watch @queue
|
70
|
+
item, at = @redis.zrangebyscore @queue, 0, Time.now.to_f,
|
71
|
+
:withscores => true, :limit => [0, 1]
|
72
|
+
if item
|
73
|
+
@redis.multi do # try and grab it
|
74
|
+
@redis.zrem @queue, item
|
75
|
+
@redis.lpush @error_queue, item
|
76
|
+
end or begin
|
77
|
+
sleep CAS_DELAY
|
78
|
+
throw :cas_retry
|
79
|
+
end
|
80
|
+
item =~ /^\d+:(\S+)$/ or raise InvalidEntryException, item
|
81
|
+
item = $1
|
82
|
+
[item, Time.at(at.to_f)]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def cleanup! item
|
88
|
+
@redis.lrem @error_queue, 1, item
|
89
|
+
end
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis-scheduler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- William Morgan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-18 00:00:00.000000000 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
description: A basic chronological scheduler for Redis. Add work items to be processed
|
16
|
+
at specific times in the future, and easily retrieve all items that are ready for
|
17
|
+
processing.
|
18
|
+
email: wmorgan-redis-scheduler@masanjin.net
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- README
|
24
|
+
- COPYING
|
25
|
+
- lib/redis-scheduler.rb
|
26
|
+
has_rdoc: true
|
27
|
+
homepage: http://gitub.com/wmorgan/redis-scheduler
|
28
|
+
licenses: []
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 1.6.0
|
48
|
+
signing_key:
|
49
|
+
specification_version: 3
|
50
|
+
summary: A basic chronological scheduler for Redis.
|
51
|
+
test_files: []
|