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 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: