sparrow 0.2 → 0.3
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/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:
|