fiveruns-starling 0.9.7.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,27 @@
1
+ == 0.9.7.5 2008-05-04
2
+ * added worker class, using starling client you can now run them in background
3
+ - handles creation of threadpool
4
+ - handles fetching of messages and passing these to the threads
5
+ - handles pushing of processed messages to starling again if needed
6
+
7
+ == 0.9.7 2008-05-03
8
+ * merged branch of AnotherBritt and Glenn Rempe by Abdul-Rahman Advany <abdulrahman@advany.com>
9
+
10
+ == 0.9.6 2008-04-30
11
+ * logging of message lifecycle as :age by AnotherBritt <?>
12
+ * added some extra logging options by AnotherBritt <?>
13
+ * added some test for epoll by AnotherBritt <?>
14
+
15
+ == 0.9.5.4 2008-04-28
16
+
17
+ * Bumped version number by Glenn Rempe <glenn.rempe@gmail.com>
18
+ * Purged all old RubyForge config. Now GitHub friendly by Glenn Rempe <glenn.rempe@gmail.com>
19
+ * New gemspec for GitHub gem auto-build-serve by Glenn Rempe <glenn.rempe@gmail.com>
20
+
21
+ == 0.9.4 2008-01-31 ==
22
+ * Evented code added using EventMachine by Chris Wanstrath <chris@ozmm.org>
23
+
24
+ == 2007-11-02
25
+
26
+ * Initial release
27
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 FIXME full name
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,93 @@
1
+ = Name
2
+
3
+ Starling - a light weight server for reliable distributed message passing.
4
+
5
+ = Description
6
+
7
+ Starling is a powerful but simple messaging server that enables reliable
8
+ distributed queuing with an absolutely minimal overhead. It speaks the
9
+ MemCache protocol for maximum cross-platform compatibility. Any language
10
+ that speaks MemCache can take advantage of Starling's queue facilities.
11
+
12
+
13
+ = Installation
14
+
15
+ This fork of the Starling source is hosted at GitHub and can be found at:
16
+
17
+ http://github.com/advany/starling/tree/master
18
+
19
+ The original source was to be found at RubyForge but no longer exists there.
20
+
21
+ GitHub serves gems prefixed by a username to differentiate different forks.
22
+ This project can be installed with:
23
+
24
+ # THIS COMMAND ONE TIME ONLY
25
+ gem sources -a http://gems.github.com/
26
+
27
+ # As often as you like
28
+ sudo gem install advany-starling
29
+
30
+ See http://gems.github.com/ if you want more info about GitHub and gems.
31
+
32
+ = Quick Start Usage
33
+
34
+ # View the Starling help and usage message
35
+ starling --help
36
+
37
+ # In a console window start the Starling server. By default
38
+ # it runs verbosely in the foreground, listening on 127.0.0.1:22122
39
+ # and stores its files under /tmp/starling:
40
+ starling
41
+
42
+ # In a new console test the put and get of messages on a queue:
43
+
44
+ irb
45
+ >> require 'starling'
46
+ => true
47
+ >> starling = Starling.new('127.0.0.1:22122')
48
+ => MemCache: 1 servers, 1 buckets, ns: nil, ro: false
49
+ >> starling.set('my_queue', 12345)
50
+ => nil
51
+ >> starling.get('my_queue')
52
+ => 12345
53
+
54
+ # You can do a simple loop over a queue with something like:
55
+ >> loop { puts starling.get('my_queue'); sleep 1 }
56
+ 12345
57
+ nil
58
+ nil
59
+ ...
60
+
61
+ For more information run the following in a new console:
62
+
63
+ 'gem server'
64
+
65
+ This will start a gem server on http://localhost:8808/ which you can view in your
66
+ browser to see the RDocs for the gem. Or generate rdocs by running the following
67
+ in a new console:
68
+
69
+ 'rdoc'
70
+
71
+ = Known Issues
72
+
73
+ * Starling is "slow" as far as messaging systems are concerned. In practice,
74
+ it's fast enough.
75
+
76
+ == TODO
77
+
78
+ * Implement memcached in stead of memcache as a client interface
79
+ * Improve client structure (with workers - simplify and add defaults)
80
+ * Fix running starling_client as deamonized process (can't seem to fork inside a fork)
81
+
82
+ = Authors
83
+
84
+ * Blaine Cook <romeda@gmail.com>
85
+ * Chris Wanstrath <chris@ozmm.org>
86
+ * AnotherBritt <?>
87
+ * Glenn Rempe <?>
88
+ * Abdul-Rahman Advany <abdulrahman@advany.com>
89
+
90
+ = Copyright
91
+
92
+ Starling - a light-weight server for reliable distributed message passing.
93
+ Copyright 2007-2008 Blaine Cook <blaine@twitter.com>, Twitter Inc.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ task :install do
6
+ sh %{gem build starling.gemspec}
7
+ sh %{sudo gem install starling-*.gem}
8
+ end
9
+
10
+ Spec::Rake::SpecTask.new do |t|
11
+ t.ruby_opts = ['-rtest/unit']
12
+ t.spec_files = FileList['test/test_*.rb']
13
+ t.fail_on_error = true
14
+ end
15
+
16
+ Rake::RDocTask.new do |rd|
17
+ rd.main = "README.rdoc"
18
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
19
+ rd.rdoc_dir = 'doc'
20
+ # rd.options = spec.rdoc_options
21
+ end
data/bin/starling ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'starling/server_runner'
4
+ StarlingServer::Runner.run
5
+
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
4
+
5
+ require 'starling/client_runner'
6
+ StarlingClient::Runner.run
data/bin/starling_top ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'socket'
6
+
7
+ @options = OpenStruct.new
8
+ @options.hostname = 'localhost'
9
+ @options.port = 11211
10
+
11
+ op = OptionParser.new do |opts|
12
+ opts.banner = "STARLING TOP\nUsage: startop [options]"
13
+ opts.separator "General Options:"
14
+ opts.on("-h HOSTNAME", "--hostname=HOSTNAME", "Hostname [default: localhost]") do |h|
15
+ @options.hostname = h
16
+ end
17
+ opts.on("-p PORT", "--port=PORT", Integer, "Port [default: 11211]") do |p|
18
+ @options.port = p
19
+ end
20
+ opts.on_tail("--help", "Show this message") do
21
+ puts opts
22
+ exit
23
+ end
24
+ end
25
+ op.parse!
26
+
27
+
28
+ def stats_data
29
+ data = ''
30
+ sock = TCPSocket.new(@options.hostname, @options.port)
31
+ sock.print("stats\r\n")
32
+ sock.flush
33
+ # memcached does not close the socket once it is done writing
34
+ # the stats data. We need to read line by line until we detect
35
+ # the END line and then stop/close on our side.
36
+ stats = sock.gets
37
+ while true
38
+ data += stats
39
+ break if stats.strip == 'END'
40
+ stats = sock.gets
41
+ end
42
+ sock.close
43
+ data
44
+ end
45
+
46
+ def parse(stats_data)
47
+ stats = []
48
+ stats_data.each_line do |line|
49
+ stats << "#{$1}: #{$2}" if line =~ /STAT (\w+) (\S+)/
50
+ end
51
+ stats.sort
52
+ end
53
+
54
+ stats = parse(stats_data)
55
+ stats.each do |stat|
56
+ puts stat
57
+ end
@@ -0,0 +1,63 @@
1
+ #!/bin/bash
2
+ #
3
+ # starling This shell script takes care of starting and stopping
4
+ # the starling server
5
+ # chkconfig: 345 98 98
6
+ # description: The starling queue server
7
+
8
+ #determine where the 'pidof' executable is located
9
+ if [ -e /bin/pidof ]; then
10
+ PIDOF="/bin/pidof"
11
+ elif [ -e /sbin/pidof ]; then
12
+ PIDOF="/sbin/pidof"
13
+ elif [ -e /usr/local/bin/pidof ]; then
14
+ PIDOF="/usr/local/bin/pidof"
15
+ elif [ -e /bin/pgrep ]; then
16
+ PIDOF="/bin/pgrep"
17
+ elif [ -e /usr/bin/pgrep ]; then
18
+ PIDOF="/usr/bin/pgrep"
19
+ elif [ -e /usr/local/bin/pgrep ]; then
20
+ PIDOF="/usr/local/bin/pgrep"
21
+ else
22
+ echo "Could not find pidof or pgrep"
23
+ fi
24
+
25
+ PROGDIR="/usr/bin"
26
+ PROGNAME="starling"
27
+ OPTIONS="-u nobody -g nobody -L /var/log/starling.log -q /var/spool/starling"
28
+
29
+ start() {
30
+ pid=`$PIDOF $PROGNAME`
31
+ if [ "$pid" != "" ]; then
32
+ echo "$PROGDIR$PROGNAME already running: $pid"
33
+ else
34
+ echo "Starting $PROGDIR$PROGNAME"
35
+ cd $PROGDIR
36
+ nohup $PROGDIR$PROGNAME $OPTIONS &
37
+ fi
38
+ }
39
+
40
+ stop() {
41
+ pid=`$PIDOF $PROGNAME`
42
+ if [ "$pid" != "" ]; then
43
+ echo "Stopping $PROGDIR$PROGNAME: $pid"
44
+ kill $pid
45
+ else
46
+ echo "$PROGDIR$PROGNAME not running"
47
+ fi
48
+ }
49
+
50
+ case "$1" in
51
+ start)
52
+ start
53
+ ;;
54
+ stop)
55
+ stop
56
+ ;;
57
+ restart)
58
+ stop
59
+ sleep 3
60
+ start
61
+ ;;
62
+
63
+ esac
@@ -0,0 +1,71 @@
1
+ #! /bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: starling
4
+ # Required-Start: $local_fs $remote_fs
5
+ # Required-Stop: $local_fs $remote_fs
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: S 0 1 6
8
+ # Short-Description: Starling queue server
9
+ # Description: The Starling distributed, transactional queue server
10
+ ### END INIT INFO
11
+ # Author: Twitter
12
+ # Version: 0.9.7.5
13
+
14
+ set -e
15
+
16
+ DUSER=starling
17
+ DGROUP=starling
18
+ LOGFILE=/var/log/starling/starling.log
19
+ SPOOLDIR=/var/spool/starling
20
+ PORT=22122
21
+ LISTEN=0.0.0.0
22
+ PIDFILE=/var/run/starling/starling.pid
23
+
24
+ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
25
+ NAME=starling
26
+ DESC="Starling"
27
+ INSTALL_DIR=/usr/local/bin
28
+ DAEMON=$INSTALL_DIR/$NAME
29
+ SCRIPTNAME=/etc/init.d/$NAME
30
+ OPTS="-h $LISTEN -p $PORT -d -q $SPOOLDIR -P $PIDFILE -L $LOGFILE"
31
+
32
+ . /lib/lsb/init-functions
33
+
34
+
35
+ # Gracefully exit if the package has been removed.
36
+ test -x $DAEMON || exit 0
37
+
38
+ d_start() {
39
+ log_begin_msg "Starting Starling Server..."
40
+ start-stop-daemon -c $DUSER:$DGROUP --start --quiet --pidfile $PIDFILE --exec $DAEMON \
41
+ -- $OPTS || log_end_msg 1
42
+ log_end_msg 0
43
+ }
44
+
45
+ d_stop() {
46
+ log_begin_msg "Stopping Starling Server..."
47
+ start-stop-daemon -c $DUSER:$DGROUP --stop --quiet --pidfile $PIDFILE \
48
+ || log_end_msg 1
49
+ log_end_msg 0
50
+ }
51
+
52
+ case "$1" in
53
+ start)
54
+ d_start
55
+ ;;
56
+ stop)
57
+ d_stop
58
+ ;;
59
+ restart|force-reload|reload)
60
+ d_stop
61
+ sleep 2
62
+ d_start
63
+ ;;
64
+ *)
65
+ echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
66
+ exit 3
67
+ ;;
68
+ esac
69
+
70
+ exit 0
71
+
data/lib/starling.rb ADDED
@@ -0,0 +1,104 @@
1
+ require 'memcache'
2
+
3
+ class Starling < MemCache
4
+
5
+ WAIT_TIME = 0.25
6
+
7
+ ##
8
+ # fetch an item from a queue.
9
+
10
+ def get(*args)
11
+ loop do
12
+ response = super(*args)
13
+ return response unless response.nil?
14
+ sleep WAIT_TIME
15
+ end
16
+ end
17
+
18
+ ##
19
+ # insert +value+ into +queue+.
20
+ #
21
+ # +expiry+ is expressed as a UNIX timestamp
22
+ #
23
+ # If +raw+ is true, +value+ will not be Marshalled. If +raw+ = :yaml, +value+
24
+ # will be serialized with YAML, instead.
25
+
26
+ def set(queue, value, expiry = 0, raw = false)
27
+ retries = 0
28
+ begin
29
+ if raw == :yaml
30
+ value = YAML.dump(value)
31
+ raw = true
32
+ end
33
+
34
+ super(queue, value, expiry, raw)
35
+ rescue MemCache::MemCacheError => e
36
+ retries += 1
37
+ sleep WAIT_TIME
38
+ retry unless retries > 3
39
+ raise e
40
+ end
41
+ end
42
+
43
+ ##
44
+ # returns the number of items in +queue+. If +queue+ is +:all+, a hash of all
45
+ # queue sizes will be returned.
46
+
47
+ def sizeof(queue, statistics = nil)
48
+ statistics ||= stats
49
+
50
+ if queue == :all
51
+ queue_sizes = {}
52
+ available_queues(statistics).each do |queue|
53
+ queue_sizes[queue] = sizeof(queue, statistics)
54
+ end
55
+ return queue_sizes
56
+ end
57
+
58
+ statistics.inject(0) { |m,(k,v)| m + v["queue_#{queue}_items"].to_i }
59
+ end
60
+
61
+ ##
62
+ # returns a list of available (currently allocated) queues.
63
+
64
+ def available_queues(statistics = nil)
65
+ statistics ||= stats
66
+
67
+ statistics.map { |k,v|
68
+ v.keys
69
+ }.flatten.uniq.grep(/^queue_(.*)_items/).map { |v|
70
+ v.gsub(/^queue_/, '').gsub(/_items$/, '')
71
+ }.reject { |v|
72
+ v =~ /_total$/ || v =~ /_expired$/
73
+ }
74
+ end
75
+
76
+ ##
77
+ # iterator to flush +queue+. Each element will be passed to the provided
78
+ # +block+
79
+
80
+ def flush(queue)
81
+ sizeof(queue).times do
82
+ v = get(queue)
83
+ yield v if block_given?
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def get_server_for_key(key)
90
+ raise ArgumentError, "illegal character in key #{key.inspect}" if key =~ /\s/
91
+ raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
92
+ raise MemCacheError, "No servers available" if @servers.empty?
93
+
94
+ bukkits = @buckets.dup
95
+ bukkits.nitems.times do |try|
96
+ n = rand(bukkits.nitems)
97
+ server = bukkits[n]
98
+ return server if server.alive?
99
+ bukkits.delete_at(n)
100
+ end
101
+
102
+ raise MemCacheError, "No servers available (all dead)"
103
+ end
104
+ end