redis-scheduler 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/COPYING +14 -0
  2. data/README +38 -0
  3. data/lib/redis-scheduler.rb +90 -0
  4. 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: []