sparrow 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sparrow.rb +4 -4
- data/lib/sparrow/queue.rb +25 -0
- data/lib/sparrow/queues/disk.rb +9 -5
- data/lib/sparrow/queues/memory.rb +6 -3
- data/lib/sparrow/queues/sqlite.rb +6 -3
- data/lib/sparrow/runner.rb +26 -11
- data/lib/sparrow/server.rb +58 -3
- data/lib/sparrow/utils.rb +47 -2
- metadata +4 -4
data/lib/sparrow.rb
CHANGED
@@ -8,7 +8,7 @@ module Sparrow
|
|
8
8
|
class SparrowError < StandardError #:nodoc:
|
9
9
|
end
|
10
10
|
|
11
|
-
VERSION = '0.
|
11
|
+
VERSION = '0.3'
|
12
12
|
|
13
13
|
@@options = {}
|
14
14
|
|
@@ -32,15 +32,15 @@ module Sparrow
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def base_dir
|
35
|
-
options[:base_dir] || File.join(%w( / var spool sparrow ))
|
35
|
+
options[:base_dir] || File.join(%w( / var spool sparrow base ))
|
36
36
|
end
|
37
37
|
|
38
38
|
def log_path
|
39
39
|
options[:log_path] || File.join(%w( / var run sparrow.log ))
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
options[:
|
42
|
+
def pid_dir
|
43
|
+
options[:pid_dir] || File.join(%w( / var run sparrow pids ))
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
data/lib/sparrow/queue.rb
CHANGED
@@ -35,6 +35,31 @@ module Sparrow
|
|
35
35
|
FileUtils.rm_rf base_dir
|
36
36
|
FileUtils.mkdir_p base_dir
|
37
37
|
end
|
38
|
+
|
39
|
+
def get_stats(queue_name)
|
40
|
+
stats = {
|
41
|
+
:type => Sparrow.options[:type],
|
42
|
+
:total_bytes => (File.size?(Sparrow.base_dir) || 0),
|
43
|
+
:queues => Dir.glob(File.join(Sparrow.base_dir, '*')).collect {|s| File.basename(s) }.join(','),
|
44
|
+
:number_of_queues => queues.keys.length,
|
45
|
+
:debug => Sparrow.options[:debug],
|
46
|
+
:pid => Process.pid,
|
47
|
+
:uptime => Time.now - Sparrow.options[:start_time],
|
48
|
+
:time => Time.now.to_i,
|
49
|
+
:version => Sparrow::VERSION,
|
50
|
+
:rusage_user => Process.times.utime,
|
51
|
+
:rusage_system => Process.times.stime
|
52
|
+
}
|
53
|
+
if queue_name
|
54
|
+
queue = get_queue(queue_name)
|
55
|
+
stats.merge!({
|
56
|
+
:bytes => Dir.glob(File.join(Sparrow.base_dir, queue_name + '**')).inject(0){|a, b| a += (File.size?(b) || 0); a },
|
57
|
+
:total_items => queue.count_push,
|
58
|
+
:curr_items => queue.count
|
59
|
+
})
|
60
|
+
end
|
61
|
+
stats
|
62
|
+
end
|
38
63
|
end
|
39
64
|
|
40
65
|
end
|
data/lib/sparrow/queues/disk.rb
CHANGED
@@ -13,11 +13,13 @@ module Sparrow
|
|
13
13
|
attr_accessor :queue_name
|
14
14
|
attr_accessor :trxr
|
15
15
|
attr_accessor :trxw
|
16
|
-
attr_accessor :
|
16
|
+
attr_accessor :count_pop
|
17
|
+
attr_accessor :count_push
|
17
18
|
|
18
19
|
def initialize(queue_name)
|
19
20
|
self.queue_name = queue_name
|
20
|
-
self.
|
21
|
+
self.count_pop = 0
|
22
|
+
self.count_push = 0
|
21
23
|
open_queue
|
22
24
|
end
|
23
25
|
|
@@ -27,9 +29,9 @@ module Sparrow
|
|
27
29
|
data = sprintf(TRX_PUSH, size, value)
|
28
30
|
trxw.seek(0, IO::SEEK_END)
|
29
31
|
trxw.write data
|
30
|
-
trxw.fsync
|
32
|
+
# trxw.fsync
|
31
33
|
rotate_queue if trxw.pos > max_log_size
|
32
|
-
self.
|
34
|
+
self.count_push += 1
|
33
35
|
value
|
34
36
|
end
|
35
37
|
|
@@ -48,9 +50,10 @@ module Sparrow
|
|
48
50
|
e_pos = trxr.pos
|
49
51
|
trxr.seek(s_pos, IO::SEEK_SET)
|
50
52
|
trxr.write(TRX_POP)
|
51
|
-
trxr.fsync
|
53
|
+
# trxr.fsync
|
52
54
|
trxr.pos = e_pos
|
53
55
|
next unless value
|
56
|
+
self.count_pop += 1
|
54
57
|
return value
|
55
58
|
end
|
56
59
|
|
@@ -69,6 +72,7 @@ module Sparrow
|
|
69
72
|
end
|
70
73
|
|
71
74
|
def count
|
75
|
+
self.count_push - self.count_pop
|
72
76
|
end
|
73
77
|
|
74
78
|
private
|
@@ -5,20 +5,23 @@ module Sparrow
|
|
5
5
|
|
6
6
|
attr_accessor :queue_name
|
7
7
|
attr_accessor :queue_data
|
8
|
-
attr_accessor :
|
8
|
+
attr_accessor :count_pop
|
9
|
+
attr_accessor :count_push
|
9
10
|
|
10
11
|
def initialize(queue_name)
|
11
12
|
self.queue_name = queue_name
|
12
13
|
self.queue_data = []
|
13
|
-
self.
|
14
|
+
self.count_pop = 0
|
15
|
+
self.count_push = 0
|
14
16
|
end
|
15
17
|
|
16
18
|
def pop
|
19
|
+
self.count_pop += 1
|
17
20
|
queue_data.shift
|
18
21
|
end
|
19
22
|
|
20
23
|
def push(value)
|
21
|
-
self.
|
24
|
+
self.count_push += 1
|
22
25
|
queue_data.push(value)
|
23
26
|
end
|
24
27
|
|
@@ -6,11 +6,13 @@ module Sparrow
|
|
6
6
|
|
7
7
|
attr_accessor :queue_name
|
8
8
|
attr_accessor :db
|
9
|
-
attr_accessor :
|
9
|
+
attr_accessor :count_pop
|
10
|
+
attr_accessor :count_push
|
10
11
|
|
11
12
|
def initialize(queue_name)
|
12
13
|
self.queue_name = queue_name
|
13
|
-
self.
|
14
|
+
self.count_pop = 0
|
15
|
+
self.count_push = 0
|
14
16
|
db_exists = File.exists?(db_path)
|
15
17
|
self.db = SQLite3::Database.new( db_path )
|
16
18
|
if !db_exists
|
@@ -26,14 +28,15 @@ module Sparrow
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def push(value)
|
29
|
-
self.count_all += 1
|
30
31
|
db.execute("INSERT INTO queues (data) VALUES (?);", value)
|
32
|
+
self.count_push += 1
|
31
33
|
value
|
32
34
|
end
|
33
35
|
|
34
36
|
def pop
|
35
37
|
id, value = db.get_first_row("SELECT * FROM queues LIMIT 1;")
|
36
38
|
db.execute("DELETE FROM queues WHERE id = ?", id)
|
39
|
+
self.count_pop += 1
|
37
40
|
value
|
38
41
|
end
|
39
42
|
|
data/lib/sparrow/runner.rb
CHANGED
@@ -16,16 +16,27 @@ module Sparrow
|
|
16
16
|
:host => "0.0.0.0",
|
17
17
|
:port => 11212,
|
18
18
|
:debug => false,
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:log_path => log_path,
|
22
|
-
:type => 'disk'
|
19
|
+
:type => 'disk',
|
20
|
+
:start_time => Time.now
|
23
21
|
}
|
24
22
|
|
23
|
+
self.options.merge!({
|
24
|
+
:base_dir => base_dir,
|
25
|
+
:pid_dir => pid_dir,
|
26
|
+
:log_path => log_path
|
27
|
+
})
|
28
|
+
|
25
29
|
parse_options
|
26
30
|
|
31
|
+
FileUtils.mkdir_p(options[:base_dir])
|
32
|
+
FileUtils.mkdir_p(File.dirname(options[:log_path]))
|
33
|
+
|
27
34
|
if options.include?(:kill)
|
28
|
-
kill_pid(options[:
|
35
|
+
kill_pid(options[:port])
|
36
|
+
end
|
37
|
+
|
38
|
+
if options.include?(:kill_all)
|
39
|
+
kill_pid('*')
|
29
40
|
end
|
30
41
|
|
31
42
|
if !options[:daemonize]
|
@@ -89,18 +100,22 @@ module Sparrow
|
|
89
100
|
|
90
101
|
opts.separator ""; opts.separator "Daemonization:"
|
91
102
|
|
92
|
-
opts.on("-P", "--pid FILE", String, "save PID in
|
93
|
-
options[:
|
103
|
+
opts.on("-P", "--pid FILE", String, "save PID in DIR when using -d option.", "(default: #{options[:pid_dir]})") do |v|
|
104
|
+
options[:pid_dir] = File.expand_path(v)
|
94
105
|
end
|
95
106
|
|
96
107
|
opts.on("-d", "--daemon", "Daemonize mode") do |v|
|
97
108
|
options[:daemonize] = v
|
98
109
|
end
|
99
110
|
|
100
|
-
opts.on("-k", "--kill
|
111
|
+
opts.on("-k", "--kill", "Kill daemons on port #{options[:port]}.") do |v|
|
101
112
|
options[:kill] = v
|
102
113
|
end
|
103
114
|
|
115
|
+
opts.on("-j", "--kill-all", String, "Kill specified running daemons - leave blank to kill all.") do |v|
|
116
|
+
options[:kill_all] = v
|
117
|
+
end
|
118
|
+
|
104
119
|
opts.separator ""; opts.separator "Logging:"
|
105
120
|
|
106
121
|
opts.on("-l", "--log [FILE]", String, "Path to print debugging information.") do |v|
|
@@ -129,12 +144,12 @@ module Sparrow
|
|
129
144
|
private
|
130
145
|
|
131
146
|
def store_pid(pid)
|
132
|
-
FileUtils.mkdir_p(
|
133
|
-
File.open(
|
147
|
+
FileUtils.mkdir_p(pid_dir)
|
148
|
+
File.open(File.join(pid_dir, "sparrow.#{options[:port]}.pid"), 'w'){|f| f.write("#{pid}\n") }
|
134
149
|
end
|
135
150
|
|
136
151
|
def kill_pid(k)
|
137
|
-
Dir[
|
152
|
+
Dir[File.join(pid_dir, "sparrow.#{k}.pid")].each do |f|
|
138
153
|
begin
|
139
154
|
puts f
|
140
155
|
pid = IO.read(f).chomp.to_i
|
data/lib/sparrow/server.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'socket'
|
1
2
|
module Sparrow
|
2
3
|
module Server
|
3
4
|
include Sparrow::Miscel
|
@@ -32,6 +33,8 @@ module Sparrow
|
|
32
33
|
|
33
34
|
VERSION = "VERSION"
|
34
35
|
|
36
|
+
STATS = "STATS"
|
37
|
+
|
35
38
|
SET_REGEX = /\ASET\s/i
|
36
39
|
ADD_REGEX = /\AADD\s/i
|
37
40
|
REPLACE_REGEX = /\AREPLACE\s/i
|
@@ -40,6 +43,21 @@ module Sparrow
|
|
40
43
|
QUIT_REGEX = /\AQUIT/i
|
41
44
|
FLUSH_ALL_REGEX = /\AFLUSH_ALL/i
|
42
45
|
VERSION_REGEX = /\AVERSION/i
|
46
|
+
STATS_REGEX = /\ASTATS/i
|
47
|
+
|
48
|
+
mattr_accessor :bytes_read
|
49
|
+
mattr_accessor :bytes_written
|
50
|
+
mattr_accessor :connections_made
|
51
|
+
mattr_accessor :connections_lost
|
52
|
+
mattr_accessor :get_count
|
53
|
+
mattr_accessor :set_count
|
54
|
+
|
55
|
+
self.bytes_read = 0
|
56
|
+
self.bytes_written = 0
|
57
|
+
self.connections_made = 0
|
58
|
+
self.connections_lost = 0
|
59
|
+
self.get_count = 0
|
60
|
+
self.set_count = 0
|
43
61
|
|
44
62
|
def post_init
|
45
63
|
@current_queue = nil
|
@@ -47,11 +65,23 @@ module Sparrow
|
|
47
65
|
@expected_bytes = 0
|
48
66
|
@current_flag = nil
|
49
67
|
@buffer = ''
|
50
|
-
|
68
|
+
self.connections_made += 1
|
69
|
+
logger.debug "New client [#{client_ip}]"
|
70
|
+
end
|
71
|
+
|
72
|
+
def unbind
|
73
|
+
self.connections_lost += 1
|
74
|
+
logger.debug "Lost client"
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_data(data)
|
78
|
+
self.bytes_written += 1
|
79
|
+
super(data)
|
51
80
|
end
|
52
81
|
|
53
82
|
def receive_data(data)
|
54
83
|
logger.debug "Receiving data: #{data}"
|
84
|
+
self.bytes_read += 1
|
55
85
|
@buffer << data
|
56
86
|
@buffer = process_whole_messages(@buffer)
|
57
87
|
end
|
@@ -89,6 +119,8 @@ module Sparrow
|
|
89
119
|
version_command
|
90
120
|
elsif ln =~ FLUSH_ALL_REGEX
|
91
121
|
flush_all_command
|
122
|
+
elsif ln =~ STATS_REGEX
|
123
|
+
stats_command
|
92
124
|
elsif @expecting_body
|
93
125
|
process_body
|
94
126
|
else
|
@@ -102,7 +134,6 @@ module Sparrow
|
|
102
134
|
publish CLIENT_ERROR, e
|
103
135
|
publish ERROR
|
104
136
|
rescue => e
|
105
|
-
debugger
|
106
137
|
logger.error e
|
107
138
|
publish SERVER_ERROR, e
|
108
139
|
end
|
@@ -131,6 +162,7 @@ module Sparrow
|
|
131
162
|
@data << @current_flag
|
132
163
|
logger.debug "Adding message to queue - #{@current_queue}"
|
133
164
|
Sparrow::Queue.add_message(@current_queue, @data)
|
165
|
+
self.set_count += 1
|
134
166
|
@expected_bytes = 0
|
135
167
|
@current_queue = nil
|
136
168
|
@expecting_body = false
|
@@ -145,8 +177,8 @@ module Sparrow
|
|
145
177
|
raise ClientError if args.empty?
|
146
178
|
rsp = []
|
147
179
|
args.each do |queue|
|
180
|
+
logger.debug "Getting message from queue - #{queue}"
|
148
181
|
begin
|
149
|
-
logger.debug "Getting message from queue - #{queue}"
|
150
182
|
msg = Sparrow::Queue.next_message(queue)
|
151
183
|
next unless msg
|
152
184
|
rescue NoMoreMessages
|
@@ -156,6 +188,7 @@ module Sparrow
|
|
156
188
|
msg = msg[0..-2]
|
157
189
|
rsp << [VALUE, queue, flag, msg.length].join(' ')
|
158
190
|
rsp << msg
|
191
|
+
self.get_count += 1
|
159
192
|
end
|
160
193
|
rsp << EOF
|
161
194
|
send_data(rsp.join(CR) + CR)
|
@@ -172,6 +205,24 @@ module Sparrow
|
|
172
205
|
publish NOT_FOUND
|
173
206
|
end
|
174
207
|
end
|
208
|
+
|
209
|
+
def stats_command
|
210
|
+
rsp = []
|
211
|
+
stats_hash = Sparrow::Queue.get_stats(args[1])
|
212
|
+
stats_hash.merge!({
|
213
|
+
:curr_connections => (self.connections_made - self.connections_lost),
|
214
|
+
:total_connections => self.connections_made,
|
215
|
+
:bytes_read => self.bytes_read,
|
216
|
+
:bytes_written => self.bytes_written,
|
217
|
+
:get_count => self.get_count,
|
218
|
+
:set_count => self.set_count
|
219
|
+
})
|
220
|
+
stats_hash.each do |key, value|
|
221
|
+
rsp << [STATS, key, value].join(' ')
|
222
|
+
end
|
223
|
+
rsp << EOF
|
224
|
+
send_data(rsp.join(CR) + CR)
|
225
|
+
end
|
175
226
|
|
176
227
|
# FLUSH_ALL
|
177
228
|
def flush_all_command
|
@@ -196,6 +247,10 @@ module Sparrow
|
|
196
247
|
def args
|
197
248
|
@split_args ||= @data.split(' ')
|
198
249
|
end
|
250
|
+
|
251
|
+
def client_ip
|
252
|
+
Socket.unpack_sockaddr_in(get_peername)[1]
|
253
|
+
end
|
199
254
|
|
200
255
|
end
|
201
256
|
end
|
data/lib/sparrow/utils.rb
CHANGED
@@ -42,6 +42,51 @@ class Class # :nodoc:
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
class Module # :nodoc:
|
46
|
+
def mattr_reader(*syms)
|
47
|
+
syms.each do |sym|
|
48
|
+
next if sym.is_a?(Hash)
|
49
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
50
|
+
unless defined? @@#{sym}
|
51
|
+
@@#{sym} = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.#{sym}
|
55
|
+
@@#{sym}
|
56
|
+
end
|
57
|
+
|
58
|
+
def #{sym}
|
59
|
+
@@#{sym}
|
60
|
+
end
|
61
|
+
EOS
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def mattr_writer(*syms)
|
66
|
+
syms.each do |sym|
|
67
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
68
|
+
unless defined? @@#{sym}
|
69
|
+
@@#{sym} = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.#{sym}=(obj)
|
73
|
+
@@#{sym} = obj
|
74
|
+
end
|
75
|
+
|
76
|
+
def #{sym}=(obj)
|
77
|
+
@@#{sym} = obj
|
78
|
+
end
|
79
|
+
EOS
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def mattr_accessor(*syms)
|
84
|
+
mattr_reader(*syms)
|
85
|
+
mattr_writer(*syms)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
45
90
|
module Sparrow
|
46
91
|
module Miscel
|
47
92
|
def options
|
@@ -60,8 +105,8 @@ module Sparrow
|
|
60
105
|
Sparrow.log_path
|
61
106
|
end
|
62
107
|
|
63
|
-
def
|
64
|
-
Sparrow.
|
108
|
+
def pid_dir
|
109
|
+
Sparrow.pid_dir
|
65
110
|
end
|
66
111
|
|
67
112
|
def logger
|
metadata
CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sparrow
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2008-
|
6
|
+
version: "0.3"
|
7
|
+
date: 2008-02-02 00:00:00 +00:00
|
8
8
|
summary: Simple file based messagine queue using the memcache protocol
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: info@eribium.org
|
12
12
|
homepage: http://code.google.com/p/sparrow
|
13
13
|
rubyforge_project: Sparrow
|
14
|
-
description: The
|
14
|
+
description: "# Sparrow is a really fast lightweight queue written in Ruby that speaks memcached. # That means you can use Sparrow with any memcached client library (Ruby or otherwise). # # Basic tests shows that Sparrow processes messages at a rate of 850-900 per second. # The load Sparrow can cope with increases exponentially as you add to the cluster. # Sparrow also takes advantage of eventmachine, which uses a non-blocking io, offering great performance. # # Sparrow comes with built in support for daemonization and clustering. # Also included are example libraries and clients. For example: # # require 'memcache' # m = MemCache.new('127.0.0.1:11212') # m['queue_name'] = '1' # Publish to queue # m['queue_name'] #=> 1 Pull next msg from queue # m['queue_name'] #=> nil # m.delete('queue_name) # Delete queue # # # or using the included client: # # class MyQueue < MQ3::Queue # def on_message # logger.info \"Received msg with args: #{args.inspect}\" # end # end # # MyQueue.servers = [ # MQ3::Protocols::Memcache.new({:host => '127.0.0.1', :port => 11212, :weight => 1}) # ] # MyQueue.publish('test msg') # MyQueue.run # # Messages are deleted as soon as they're read and the order you add messages to the queue probably won't # be the same order when they're removed. # # Additional memcached commands that are supported are: # flush_all # Deletes all queues # version # quit # The memcached commands 'add', and 'replace' just call 'set'. # # Call sparrow with --help for usage options # # The daemonization won't work on Windows. # # Check out the code: # svn checkout http://sparrow.googlecode.com/svn/trunk/ sparrow # # Sparrow was inspired by Twitter's Starling"
|
15
15
|
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
@@ -74,5 +74,5 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - ">="
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 1.
|
77
|
+
version: 1.5.0
|
78
78
|
version:
|