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 CHANGED
@@ -8,7 +8,7 @@ module Sparrow
8
8
  class SparrowError < StandardError #:nodoc:
9
9
  end
10
10
 
11
- VERSION = '0.2'
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 pid_path
43
- options[:pid_path] || File.join(%w( / var run sparrow.#{options[:port]}.pid ))
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
@@ -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 :count_all
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.count_all = 0
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.count_all += 1
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 :count_all
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.count_all = 0
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.count_all += 1
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 :count_all
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.count_all = 0
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
 
@@ -16,16 +16,27 @@ module Sparrow
16
16
  :host => "0.0.0.0",
17
17
  :port => 11212,
18
18
  :debug => false,
19
- :base_dir => base_dir,
20
- :pid_path => pid_path,
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[:kill] || '*')
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 FILE when using -d option.", "(default: #{options[:pid_path]})") do |v|
93
- options[:pid_path] = File.expand_path(v)
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 PORT", String, "Kill specified running daemons - leave blank to kill all.") do |v|
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(File.dirname(pid_path))
133
- File.open(pid_path, 'w'){|f| f.write("#{pid}\n")}
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[options[:pid_path]||File.join(File.dirname(pid_dir), "sparrow.#{k}.pid")].each do |f|
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
@@ -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
- logger.debug "New client"
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 pid_path
64
- Sparrow.log_path
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.2"
7
- date: 2008-01-26 00:00:00 +00:00
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 author was too lazy to write a description
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.4.0
77
+ version: 1.5.0
78
78
  version: