timshadel-starling 0.9.8.01.20080924
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +52 -0
- data/LICENSE +20 -0
- data/README.rdoc +106 -0
- data/Rakefile +22 -0
- data/bin/starling +6 -0
- data/bin/starling_top +57 -0
- data/etc/sample-config.yml +9 -0
- data/etc/starling.redhat +63 -0
- data/etc/starling.ubuntu +71 -0
- data/lib/starling/handler.rb +234 -0
- data/lib/starling/persistent_queue.rb +151 -0
- data/lib/starling/queue_collection.rb +141 -0
- data/lib/starling/server.rb +125 -0
- data/lib/starling/server_runner.rb +297 -0
- data/lib/starling.rb +131 -0
- data/spec/starling_server_spec.rb +216 -0
- metadata +100 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
== 0.9.9
|
2
|
+
* remove dependency on SyslogLogger so that starling works on windows.
|
3
|
+
|
4
|
+
== 0.9.8
|
5
|
+
* add fix to enable relative paths <david@motorator.com>
|
6
|
+
* fix tests so they don't run 10 times due to a stupid bug with how the server is forked <seth@mojodna.net>
|
7
|
+
* fix some other tests <romeda@gmail.com>
|
8
|
+
* fix some error messages <romeda@gmail.com>
|
9
|
+
* probably some other things <romeda@gmail.com>
|
10
|
+
|
11
|
+
== 0.9.7.9
|
12
|
+
* properly complain if the spool directory isn't writable <seth@mojodna.net>
|
13
|
+
* assume group and user privileges in a working order <seth@mojodna.net>
|
14
|
+
* support string user / group names in addition to uid/gids <seth@mojodna.net>
|
15
|
+
|
16
|
+
== 0.9.7.7
|
17
|
+
* added init.d scripts for redhat and ubuntu by Mike Perham <mperham@gmail.com>
|
18
|
+
* fixed dependencies for SyslogLogger, eventmachine and memcache-client by Mike Perham <mperham@gmail.com>
|
19
|
+
* added starling_top script to monitor starling server by Mike Perham <mperham@gmail.com>
|
20
|
+
* fixed starling_top to use 22122 as port by Abdul-Rahman Advany <abdulrahman@advany.com>
|
21
|
+
|
22
|
+
== 0.9.7.6 2008-06-24
|
23
|
+
* removed client code (will be added to different project) by Abdul-Rahman Advany <abdulrahman@advany.com>
|
24
|
+
|
25
|
+
== 0.9.7.5 2008-05-04
|
26
|
+
* added worker class, using starling client you can now run them in background by Abdul-Rahman Advany <abdulrahman@advany.com>
|
27
|
+
- handles creation of threadpool
|
28
|
+
- handles fetching of messages and passing these to the threads
|
29
|
+
- handles pushing of processed messages to starling again if needed
|
30
|
+
|
31
|
+
== 0.9.7 2008-05-03
|
32
|
+
* merged branch of AnotherBritt and Glenn Rempe by Abdul-Rahman Advany <abdulrahman@advany.com>
|
33
|
+
* rspeced tests by Abdul-Rahman Advany <abdulrahman@advany.com>
|
34
|
+
|
35
|
+
== 0.9.6 2008-04-30
|
36
|
+
* logging of message lifecycle as :age by AnotherBritt <?>
|
37
|
+
* added some extra logging options by AnotherBritt <?>
|
38
|
+
* added some test for epoll by AnotherBritt <?>
|
39
|
+
|
40
|
+
== 0.9.5.4 2008-04-28
|
41
|
+
|
42
|
+
* Bumped version number by Glenn Rempe <glenn.rempe@gmail.com>
|
43
|
+
* Purged all old RubyForge config. Now GitHub friendly by Glenn Rempe <glenn.rempe@gmail.com>
|
44
|
+
* New gemspec for GitHub gem auto-build-serve by Glenn Rempe <glenn.rempe@gmail.com>
|
45
|
+
|
46
|
+
== 0.9.4 2008-01-31 ==
|
47
|
+
* Evented code added using EventMachine by Chris Wanstrath <chris@ozmm.org>
|
48
|
+
|
49
|
+
== 2007-11-02
|
50
|
+
|
51
|
+
* Initial release
|
52
|
+
|
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,106 @@
|
|
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
|
+
= Installation
|
13
|
+
|
14
|
+
This fork of the Starling source is hosted at GitHub and can be found at:
|
15
|
+
|
16
|
+
http://github.com/starling/starling/tree/master
|
17
|
+
|
18
|
+
The original source was to be found at RubyForge but no longer exists there.
|
19
|
+
|
20
|
+
GitHub serves gems prefixed by a username to differentiate different forks.
|
21
|
+
This project can be installed with:
|
22
|
+
|
23
|
+
# THIS COMMAND ONE TIME ONLY
|
24
|
+
gem sources -a http://gems.github.com/
|
25
|
+
|
26
|
+
# As often as you like
|
27
|
+
sudo gem install starling-starling
|
28
|
+
|
29
|
+
See http://gems.github.com/ if you want more info about GitHub and gems.
|
30
|
+
|
31
|
+
= Quick Start Usage
|
32
|
+
|
33
|
+
# View the Starling help and usage message
|
34
|
+
starling --help
|
35
|
+
|
36
|
+
# In a console window start the Starling server. By default
|
37
|
+
# it runs verbosely in the foreground, listening on 127.0.0.1:22122
|
38
|
+
# and stores its files under /tmp/starling:
|
39
|
+
starling
|
40
|
+
|
41
|
+
# In a new console test the put and get of messages on a queue:
|
42
|
+
|
43
|
+
irb
|
44
|
+
>> require 'starling'
|
45
|
+
=> true
|
46
|
+
>> starling = Starling.new('127.0.0.1:22122')
|
47
|
+
=> MemCache: 1 servers, 1 buckets, ns: nil, ro: false
|
48
|
+
>> starling.set('my_queue', 12345)
|
49
|
+
=> nil
|
50
|
+
>> starling.get('my_queue')
|
51
|
+
=> 12345
|
52
|
+
|
53
|
+
# You can do a simple loop over a queue with something like:
|
54
|
+
>> loop { puts starling.get('my_queue'); sleep 1 }
|
55
|
+
12345
|
56
|
+
nil
|
57
|
+
nil
|
58
|
+
...
|
59
|
+
|
60
|
+
For more information run the following in a new console:
|
61
|
+
|
62
|
+
'gem server'
|
63
|
+
|
64
|
+
This will start a gem server on http://localhost:8808/ which you can view in your
|
65
|
+
browser to see the RDocs for the gem. Or generate rdocs by running the following
|
66
|
+
in a new console:
|
67
|
+
|
68
|
+
'rdoc'
|
69
|
+
|
70
|
+
= Using fiveruns memcache-client
|
71
|
+
|
72
|
+
memcache-client from fiveruns has a couple of fixed added like supporting failover and retry on failure.
|
73
|
+
|
74
|
+
This fork of the memcache-client source is hosted at GitHub and can be found at:
|
75
|
+
|
76
|
+
http://github.com/fiveruns/memcache-client/tree/master
|
77
|
+
|
78
|
+
It can be installed using:
|
79
|
+
|
80
|
+
# THIS COMMAND ONE TIME ONLY
|
81
|
+
gem sources -a http://gems.github.com/
|
82
|
+
|
83
|
+
# As often as you like
|
84
|
+
sudo gem install fiveruns-memcache-client
|
85
|
+
|
86
|
+
= Known Issues
|
87
|
+
|
88
|
+
* Starling is "slow" as far as messaging systems are concerned. In practice,
|
89
|
+
it's fast enough.
|
90
|
+
|
91
|
+
== TODO
|
92
|
+
|
93
|
+
* Implement memcached instead of memcache as a client interface (to make it faster)
|
94
|
+
|
95
|
+
= Authors
|
96
|
+
|
97
|
+
* Blaine Cook <romeda@gmail.com>
|
98
|
+
* Chris Wanstrath <chris@ozmm.org>
|
99
|
+
* AnotherBritt <?>
|
100
|
+
* Glenn Rempe <?>
|
101
|
+
* Abdul-Rahman Advany <abdulrahman@advany.com>
|
102
|
+
|
103
|
+
= Copyright
|
104
|
+
|
105
|
+
Starling - a light-weight server for reliable distributed message passing.
|
106
|
+
Copyright 2007-2008 Blaine Cook <blaine@twitter.com>, Twitter Inc.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
task :install do
|
8
|
+
sh %{gem build starling.gemspec}
|
9
|
+
sh %{sudo gem install starling-*.gem}
|
10
|
+
end
|
11
|
+
|
12
|
+
Spec::Rake::SpecTask.new do |t|
|
13
|
+
t.ruby_opts = ['-rtest/unit']
|
14
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
15
|
+
t.fail_on_error = true
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake::RDocTask.new do |rd|
|
19
|
+
rd.main = "README.rdoc"
|
20
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
21
|
+
rd.rdoc_dir = 'doc'
|
22
|
+
end
|
data/bin/starling
ADDED
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 = 22122
|
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: 22122]") 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
|
data/etc/starling.redhat
ADDED
@@ -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
|
data/etc/starling.ubuntu
ADDED
@@ -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.7
|
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
|
+
|
@@ -0,0 +1,234 @@
|
|
1
|
+
module StarlingServer
|
2
|
+
|
3
|
+
##
|
4
|
+
# This is an internal class that's used by Starling::Server to handle the
|
5
|
+
# MemCache protocol and act as an interface between the Server and the
|
6
|
+
# QueueCollection.
|
7
|
+
|
8
|
+
class Handler < EventMachine::Connection
|
9
|
+
|
10
|
+
DATA_PACK_FMT = "Ia*".freeze
|
11
|
+
|
12
|
+
# ERROR responses
|
13
|
+
ERR_UNKNOWN_COMMAND = "CLIENT_ERROR bad command line format\r\n".freeze
|
14
|
+
|
15
|
+
# GET Responses
|
16
|
+
GET_COMMAND = /\Aget (.{1,250})\s*\r\n/m
|
17
|
+
GET_RESPONSE = "VALUE %s %s %s\r\n%s\r\nEND\r\n".freeze
|
18
|
+
GET_RESPONSE_EMPTY = "END\r\n".freeze
|
19
|
+
|
20
|
+
# SET Responses
|
21
|
+
SET_COMMAND = /\Aset (.{1,250}) ([0-9]+) ([0-9]+) ([0-9]+)\r\n/m
|
22
|
+
SET_RESPONSE_SUCCESS = "STORED\r\n".freeze
|
23
|
+
SET_RESPONSE_FAILURE = "NOT STORED\r\n".freeze
|
24
|
+
SET_CLIENT_DATA_ERROR = "CLIENT_ERROR bad data chunk\r\nERROR\r\n".freeze
|
25
|
+
|
26
|
+
# DELETE Responses
|
27
|
+
DELETE_COMMAND = /\Adelete (.{1,250}) ([0-9]+)\r\n/m
|
28
|
+
DELETE_RESPONSE = "END\r\n".freeze
|
29
|
+
|
30
|
+
# STAT Response
|
31
|
+
STATS_COMMAND = /\Astats\r\n/m
|
32
|
+
STATS_RESPONSE = "STAT pid %d\r
|
33
|
+
STAT uptime %d\r
|
34
|
+
STAT time %d\r
|
35
|
+
STAT version %s\r
|
36
|
+
STAT rusage_user %0.6f\r
|
37
|
+
STAT rusage_system %0.6f\r
|
38
|
+
STAT curr_items %d\r
|
39
|
+
STAT total_items %d\r
|
40
|
+
STAT bytes %d\r
|
41
|
+
STAT curr_connections %d\r
|
42
|
+
STAT total_connections %d\r
|
43
|
+
STAT cmd_get %d\r
|
44
|
+
STAT cmd_set %d\r
|
45
|
+
STAT get_hits %d\r
|
46
|
+
STAT get_misses %d\r
|
47
|
+
STAT bytes_read %d\r
|
48
|
+
STAT bytes_written %d\r
|
49
|
+
STAT limit_maxbytes %d\r
|
50
|
+
%sEND\r\n".freeze
|
51
|
+
QUEUE_STATS_RESPONSE = "STAT queue_%s_items %d\r
|
52
|
+
STAT queue_%s_total_items %d\r
|
53
|
+
STAT queue_%s_logsize %d\r
|
54
|
+
STAT queue_%s_expired_items %d\r
|
55
|
+
STAT queue_%s_age %d\r\n".freeze
|
56
|
+
|
57
|
+
SHUTDOWN_COMMAND = /\Ashutdown\r\n/m
|
58
|
+
|
59
|
+
|
60
|
+
@@next_session_id = 1
|
61
|
+
|
62
|
+
##
|
63
|
+
# Creates a new handler for the MemCache protocol that communicates with a
|
64
|
+
# given client.
|
65
|
+
|
66
|
+
def initialize(options = {})
|
67
|
+
@opts = options
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Process incoming commands from the attached client.
|
72
|
+
|
73
|
+
def post_init
|
74
|
+
@stash = []
|
75
|
+
@data = ""
|
76
|
+
@data_buf = ""
|
77
|
+
@server = @opts[:server]
|
78
|
+
@logger = StarlingServer::Base.logger
|
79
|
+
@expiry_stats = Hash.new(0)
|
80
|
+
@expected_length = nil
|
81
|
+
@server.stats[:total_connections] += 1
|
82
|
+
set_comm_inactivity_timeout @opts[:timeout]
|
83
|
+
@queue_collection = @opts[:queue]
|
84
|
+
|
85
|
+
@session_id = @@next_session_id
|
86
|
+
@@next_session_id += 1
|
87
|
+
|
88
|
+
peer = Socket.unpack_sockaddr_in(get_peername)
|
89
|
+
#@logger.debug "(#{@session_id}) New session from #{peer[1]}:#{peer[0]}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def receive_data(incoming)
|
93
|
+
@server.stats[:bytes_read] += incoming.size
|
94
|
+
@data << incoming
|
95
|
+
|
96
|
+
while data = @data.slice!(/.*?\r\n/m)
|
97
|
+
response = process(data)
|
98
|
+
end
|
99
|
+
|
100
|
+
send_data response if response
|
101
|
+
end
|
102
|
+
|
103
|
+
def process(data)
|
104
|
+
data = @data_buf + data if @data_buf.size > 0
|
105
|
+
# our only non-normal state is consuming an object's data
|
106
|
+
# when @expected_length is present
|
107
|
+
if @expected_length && data.size == @expected_length
|
108
|
+
response = set_data(data)
|
109
|
+
@data_buf = ""
|
110
|
+
return response
|
111
|
+
elsif @expected_length
|
112
|
+
@data_buf = data
|
113
|
+
return
|
114
|
+
end
|
115
|
+
case data
|
116
|
+
when SET_COMMAND
|
117
|
+
@server.stats[:set_requests] += 1
|
118
|
+
set($1, $2, $3, $4.to_i)
|
119
|
+
when GET_COMMAND
|
120
|
+
@server.stats[:get_requests] += 1
|
121
|
+
get($1)
|
122
|
+
when STATS_COMMAND
|
123
|
+
stats
|
124
|
+
when SHUTDOWN_COMMAND
|
125
|
+
# no point in responding, they'll never get it.
|
126
|
+
Runner::shutdown
|
127
|
+
when DELETE_COMMAND
|
128
|
+
delete $1
|
129
|
+
else
|
130
|
+
logger.warn "Unknown command: #{data}."
|
131
|
+
respond ERR_UNKNOWN_COMMAND
|
132
|
+
end
|
133
|
+
rescue => e
|
134
|
+
logger.error "Error handling request: #{e}."
|
135
|
+
logger.debug e.backtrace.join("\n")
|
136
|
+
respond GET_RESPONSE_EMPTY
|
137
|
+
end
|
138
|
+
|
139
|
+
def unbind
|
140
|
+
#@logger.debug "(#{@session_id}) connection ends"
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def delete(queue)
|
146
|
+
@queue_collection.queues(queue).close
|
147
|
+
@queue_collection.queues.delete(queue)
|
148
|
+
respond DELETE_RESPONSE
|
149
|
+
end
|
150
|
+
|
151
|
+
def respond(str, *args)
|
152
|
+
response = sprintf(str, *args)
|
153
|
+
@server.stats[:bytes_written] += response.length
|
154
|
+
response
|
155
|
+
end
|
156
|
+
|
157
|
+
def set(key, flags, expiry, len)
|
158
|
+
@expected_length = len + 2
|
159
|
+
@stash = [ key, flags, expiry ]
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
|
163
|
+
def set_data(incoming)
|
164
|
+
key, flags, expiry = @stash
|
165
|
+
data = incoming.slice(0...@expected_length-2)
|
166
|
+
@stash = []
|
167
|
+
@expected_length = nil
|
168
|
+
|
169
|
+
internal_data = [expiry.to_i, data].pack(DATA_PACK_FMT)
|
170
|
+
if @queue_collection.put(key, internal_data)
|
171
|
+
respond SET_RESPONSE_SUCCESS
|
172
|
+
else
|
173
|
+
respond SET_RESPONSE_FAILURE
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def get(key)
|
178
|
+
now = Time.now.to_i
|
179
|
+
|
180
|
+
while response = @queue_collection.take(key)
|
181
|
+
expiry, data = response.unpack(DATA_PACK_FMT)
|
182
|
+
|
183
|
+
break if expiry == 0 || expiry >= now
|
184
|
+
|
185
|
+
@expiry_stats[key] += 1
|
186
|
+
expiry, data = nil
|
187
|
+
end
|
188
|
+
|
189
|
+
if data
|
190
|
+
respond GET_RESPONSE, key, 0, data.size, data
|
191
|
+
else
|
192
|
+
respond GET_RESPONSE_EMPTY
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def stats
|
197
|
+
respond STATS_RESPONSE,
|
198
|
+
Process.pid, # pid
|
199
|
+
Time.now - @server.stats(:start_time), # uptime
|
200
|
+
Time.now.to_i, # time
|
201
|
+
StarlingServer::VERSION, # version
|
202
|
+
Process.times.utime, # rusage_user
|
203
|
+
Process.times.stime, # rusage_system
|
204
|
+
@queue_collection.stats(:current_size), # curr_items
|
205
|
+
@queue_collection.stats(:total_items), # total_items
|
206
|
+
@queue_collection.stats(:current_bytes), # bytes
|
207
|
+
@server.stats(:connections), # curr_connections
|
208
|
+
@server.stats(:total_connections), # total_connections
|
209
|
+
@server.stats(:get_requests), # get count
|
210
|
+
@server.stats(:set_requests), # set count
|
211
|
+
@queue_collection.stats(:get_hits),
|
212
|
+
@queue_collection.stats(:get_misses),
|
213
|
+
@server.stats(:bytes_read), # total bytes read
|
214
|
+
@server.stats(:bytes_written), # total bytes written
|
215
|
+
0, # limit_maxbytes
|
216
|
+
queue_stats
|
217
|
+
end
|
218
|
+
|
219
|
+
def queue_stats
|
220
|
+
@queue_collection.queues.inject("") do |m,(k,v)|
|
221
|
+
m + sprintf(QUEUE_STATS_RESPONSE,
|
222
|
+
k, v.length,
|
223
|
+
k, v.total_items,
|
224
|
+
k, v.logsize,
|
225
|
+
k, @expiry_stats[k],
|
226
|
+
k, v.current_age)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def logger
|
231
|
+
@logger
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|