redis-stat 0.1.4 → 0.2.0

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/README.md CHANGED
@@ -11,7 +11,7 @@ gem install redis-stat
11
11
  ## Usage
12
12
 
13
13
  ```
14
- usage: redis-stat [HOST[:PORT]] [INTERVAL [COUNT]]
14
+ usage: redis-stat [HOST[:PORT] ...] [INTERVAL [COUNT]]
15
15
 
16
16
  --csv=OUTPUT_CSV_FILE_PATH Save the result in CSV format
17
17
  -v, --verbose Show more info
@@ -26,11 +26,13 @@ redis-stat
26
26
 
27
27
  redis-stat 1
28
28
 
29
- redis-stat localhost:6380
29
+ redis-stat 1 10
30
30
 
31
31
  redis-stat localhost:6380 1 10
32
32
 
33
- redis-stat localhost:6380 1 10 --csv=/tmp/output.csv --verbose
33
+ redis-stat localhost localhost:6380 localhost:6381 5
34
+
35
+ redis-stat localhost localhost:6380 1 10 --csv=/tmp/output.csv --verbose
34
36
  ```
35
37
 
36
38
  ## Screenshot
data/lib/redis-stat.rb CHANGED
@@ -5,6 +5,7 @@ require 'redis'
5
5
  require 'tabularize'
6
6
  require 'ansi'
7
7
  require 'csv'
8
+ require 'parallelize'
8
9
 
9
10
  class RedisStat
10
11
  DEFAULT_TERM_WIDTH = 180
@@ -12,7 +13,11 @@ class RedisStat
12
13
 
13
14
  def initialize options = {}
14
15
  @options = RedisStat::Option::DEFAULT.merge options
15
- @redis = Redis.new(Hash[ @options.select { |k, _| [:host, :port].include? k } ])
16
+ @hosts = @options[:hosts]
17
+ @redises = @hosts.map { |e|
18
+ host, port = e.split(':')
19
+ Redis.new(Hash[ {:host => host, :port => port}.select { |k, v| v } ])
20
+ }
16
21
  @max_count = @options[:count]
17
22
  @count = 0
18
23
  @colors = @options[:colors] || COLORS
@@ -25,12 +30,30 @@ class RedisStat
25
30
  csv = File.open(@options[:csv], 'w') if @options[:csv]
26
31
  update_term_size!
27
32
 
28
- prev_info = nil
33
+
29
34
  begin
35
+ # Warm-up
36
+ @redises.each { |r| r.info }
37
+
30
38
  @started_at = Time.now
39
+ prev_info = nil
31
40
  loop do
32
- info = @redis.info.insensitive
41
+ info = {}.insensitive
42
+ class << info
43
+ def sumf label
44
+ (self[label] || []).map(&:to_f).inject(:+)
45
+ end
46
+ end
47
+
33
48
  info[:at] = Time.now.to_f
49
+ @redises.pmap(@redises.length) { |redis|
50
+ redis.info.insensitive
51
+ }.each do |rinfo|
52
+ rinfo.each do |k, v|
53
+ info[k] ||= []
54
+ info[k] << v
55
+ end
56
+ end
34
57
 
35
58
  output info, prev_info, csv
36
59
 
@@ -98,9 +121,9 @@ private
98
121
 
99
122
  movement = nil
100
123
  if @count == 0
101
- movement = 0
102
124
  output_static_info info
103
125
 
126
+ movement = 0
104
127
  if file
105
128
  file.puts CSV.generate_line(info_output.map { |pair|
106
129
  LABELS[pair.first] || pair.first
@@ -147,30 +170,28 @@ private
147
170
  end
148
171
 
149
172
  def output_static_info info
150
- data =
151
- {
152
- :redis_stat_version => RedisStat::VERSION,
153
- :redis_host => @options[:host],
154
- :redis_port => @options[:port],
155
- :csv => @options[:csv],
156
- }.merge(
157
- Hash[
158
- [
159
- :redis_version,
160
- :process_id,
161
- :uptime_in_seconds,
162
- :uptime_in_days,
163
- :gcc_version,
164
- :role,
165
- :connected_slaves, # FIXME: not so static
166
- :aof_enabled,
167
- :vm_enabled
168
- ].map { |k| [k, info[k]] }
169
- ]
170
- ).reject { |k, v| v.nil? }.to_a
171
- @os.puts Tabularize.it(data, :align => :left).map { |pair|
172
- ansi(:bold) { pair.first } + ' : ' + pair.last
173
- }
173
+ tab = Tabularize.new(
174
+ :unicode => false, :align => :right,
175
+ :hborder => ansi(:black, :bold) { '-' },
176
+ :vborder => ansi(:black, :bold) { '|' },
177
+ :iborder => ansi(:black, :bold) { '+' }
178
+ )
179
+ tab << [nil] + @hosts.map { |h| ansi(:bold, :green) { h } }
180
+ tab.separator!
181
+ [
182
+ :redis_version,
183
+ :process_id,
184
+ :uptime_in_seconds,
185
+ :uptime_in_days,
186
+ :gcc_version,
187
+ :role,
188
+ :connected_slaves,
189
+ :aof_enabled,
190
+ :vm_enabled
191
+ ].each do |key|
192
+ tab << [ansi(:bold) { key }] + info[key]
193
+ end
194
+ @os.puts tab
174
195
  end
175
196
 
176
197
  def init_table info_output
@@ -179,6 +200,7 @@ private
179
200
  :hborder => ansi(:black, :bold) { '-' },
180
201
  :iborder => ansi(:black, :bold) { '+' },
181
202
  :vborder => ' ',
203
+ :ellipsis => ansi(:bold) { '>' },
182
204
  :pad_left => 0,
183
205
  :pad_right => 0,
184
206
  :screen_width => @term_width
@@ -200,8 +222,8 @@ private
200
222
  dur = prev_info && (info[:at] - prev_info[:at])
201
223
 
202
224
  get_diff = lambda do |label|
203
- if dur
204
- (info[label].to_f - prev_info[label].to_f) / dur
225
+ if dur && dur > 0
226
+ (info.sumf(label) - prev_info.sumf(label)) / dur
205
227
  else
206
228
  nil
207
229
  end
@@ -214,8 +236,8 @@ private
214
236
  val = get_diff.call(key)
215
237
  [humanize_number(val), val]
216
238
  when :keys
217
- val = Hash[ info.select { |k, v| k =~ /^db[0-9]+$/ } ].values.inject(0) { |sum, v|
218
- sum + Hash[ v.split(',').map { |e| e.split '=' } ]['keys'].to_i
239
+ val = Hash[ info.select { |k, v| k =~ /^db[0-9]+$/ } ].values.inject(0) { |sum, vs|
240
+ sum + vs.map { |v| Hash[ v.split(',').map { |e| e.split '=' } ]['keys'].to_i }.inject(:+)
219
241
  }
220
242
  [humanize_number(val), val]
221
243
  when :evicted_keys_per_second, :expired_keys_per_second, :keyspace_hits_per_second,
@@ -223,11 +245,13 @@ private
223
245
  val = get_diff.call(key.to_s.gsub(/_per_second$/, '').to_sym)
224
246
  [humanize_number(val), val]
225
247
  when :total_commands_processed, :evicted_keys, :expired_keys, :keyspace_hits, :keyspace_misses
226
- [humanize_number(info[key].to_i), info[key]]
248
+ val = info.sumf(key)
249
+ [humanize_number(val.to_i), val]
227
250
  when :used_memory, :used_memory_rss, :aof_current_size, :aof_base_size
228
- [humanize_number(info[key].to_i, 1024, 'B'), info[key]]
251
+ val = info.sumf(key)
252
+ [humanize_number(val.to_i, 1024, 'B'), val]
229
253
  else
230
- info[key]
254
+ format_number info.sumf(key)
231
255
  end
232
256
  end
233
257
 
@@ -3,8 +3,7 @@ require 'optparse'
3
3
  class RedisStat
4
4
  module Option
5
5
  DEFAULT = {
6
- :host => '127.0.0.1',
7
- :port => 6379,
6
+ :hosts => ['127.0.0.1:6379'],
8
7
  :interval => 2,
9
8
  :count => nil,
10
9
  :csv => nil
@@ -15,7 +14,7 @@ module Option
15
14
 
16
15
  options = DEFAULT.dup
17
16
  opts = ::OptionParser.new { |opts|
18
- opts.banner = "usage: redis-stat [HOST[:PORT]] [INTERVAL [COUNT]]"
17
+ opts.banner = "usage: redis-stat [HOST[:PORT] ...] [INTERVAL [COUNT]]"
19
18
  opts.separator ''
20
19
 
21
20
  opts.on('--csv=OUTPUT_CSV_FILE_PATH', 'Save the result in CSV format') do |v|
@@ -41,33 +40,13 @@ module Option
41
40
  opts.parse! argv
42
41
 
43
42
  is_number = lambda { |str| str =~ /^([0-9]\.?[0-9]*)$|^([1-9][0-9]*)$/ }
44
- set_options = lambda { |host_port, interval, count|
45
- if host_port
46
- host, port = host_port.split(':')
47
- options[:host] = host
48
- options[:port] = port.to_i if port
49
- end
50
43
 
51
- options[:interval] = interval.to_f if interval
52
- options[:count] = count.to_i if count
53
- }
44
+ numbers, hosts = argv.partition { |e| is_number.call e }
45
+ interval, count = numbers.map(&:to_f)
54
46
 
55
- case argv.length
56
- when 1
57
- if is_number.call argv.first
58
- set_options.call nil, argv.first, nil
59
- else
60
- set_options.call argv.first, nil, nil
61
- end
62
- when 2
63
- if is_number.call argv.first
64
- set_options.call nil, argv.first, argv.last
65
- else
66
- set_options.call argv.first, argv.last, nil
67
- end
68
- when 3
69
- set_options.call *argv
70
- end
47
+ options[:interval] = interval if interval
48
+ options[:count] = count if count
49
+ options[:hosts] = hosts unless hosts.empty?
71
50
 
72
51
  validate options
73
52
 
@@ -92,9 +71,19 @@ module Option
92
71
  raise ArgumentError.new("Invalid count: #{count}")
93
72
  end
94
73
 
95
- port = options[:port]
96
- unless port.is_a?(Fixnum) && port > 0 && port < 65536
97
- raise ArgumentError.new("Invalid port: #{port}")
74
+ hosts = options[:hosts]
75
+ if hosts.empty?
76
+ raise ArgumentError.new("Redis host not given")
77
+ end
78
+
79
+ hosts.each do |host|
80
+ host, port = host.split(':')
81
+ if port
82
+ port = port.to_i
83
+ unless port > 0 && port < 65536
84
+ raise ArgumentError.new("Invalid port: #{port}")
85
+ end
86
+ end
98
87
  end
99
88
  end
100
89
  end
@@ -1,3 +1,3 @@
1
1
  class RedisStat
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
data/redis-stat.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |gem|
19
19
  gem.add_runtime_dependency "redis", '~> 3.0.1'
20
20
  gem.add_runtime_dependency "tabularize", '~> 0.2.4'
21
21
  gem.add_runtime_dependency "insensitive_hash", '~> 0.2.4'
22
+ gem.add_runtime_dependency "parallelize", '~> 0.4.0'
22
23
 
23
24
  gem.add_development_dependency 'test-unit'
24
25
  end
@@ -43,8 +43,7 @@ class TestRedisStat < Test::Unit::TestCase
43
43
 
44
44
  options = RedisStat::Option.parse(%w[localhost:1000 20])
45
45
  assert_equal({
46
- :host => 'localhost',
47
- :port => 1000,
46
+ :hosts => ['localhost:1000'],
48
47
  :interval => 20,
49
48
  :count => nil,
50
49
  :csv => nil
@@ -52,8 +51,7 @@ class TestRedisStat < Test::Unit::TestCase
52
51
 
53
52
  options = RedisStat::Option.parse(%w[localhost:1000 20 30])
54
53
  assert_equal({
55
- :host => 'localhost',
56
- :port => 1000,
54
+ :hosts => ['localhost:1000'],
57
55
  :interval => 20,
58
56
  :count => 30,
59
57
  :csv => nil
@@ -61,8 +59,7 @@ class TestRedisStat < Test::Unit::TestCase
61
59
 
62
60
  options = RedisStat::Option.parse(%w[20])
63
61
  assert_equal({
64
- :host => '127.0.0.1',
65
- :port => 6379,
62
+ :hosts => ['127.0.0.1:6379'],
66
63
  :interval => 20,
67
64
  :count => nil,
68
65
  :csv => nil
@@ -70,8 +67,7 @@ class TestRedisStat < Test::Unit::TestCase
70
67
 
71
68
  options = RedisStat::Option.parse(%w[20 30])
72
69
  assert_equal({
73
- :host => '127.0.0.1',
74
- :port => 6379,
70
+ :hosts => ['127.0.0.1:6379'],
75
71
  :interval => 20,
76
72
  :count => 30,
77
73
  :csv => nil
@@ -79,8 +75,7 @@ class TestRedisStat < Test::Unit::TestCase
79
75
 
80
76
  options = RedisStat::Option.parse(%w[localhost:8888 10 --csv=/tmp/a.csv])
81
77
  assert_equal({
82
- :port => 8888,
83
- :host => 'localhost',
78
+ :hosts => ['localhost:8888'],
84
79
  :interval => 10,
85
80
  :count => nil,
86
81
  :csv => '/tmp/a.csv',
@@ -101,7 +96,7 @@ class TestRedisStat < Test::Unit::TestCase
101
96
  def test_start
102
97
  csv = '/tmp/redis-stat.csv'
103
98
  cnt = 50
104
- rs = RedisStat.new :interval => 0.01, :count => cnt, :verbose => true, :csv => csv
99
+ rs = RedisStat.new :hosts => %w[localhost] * 5, :interval => 0.1, :count => cnt, :verbose => true, :csv => csv
105
100
  rs.start $stdout
106
101
 
107
102
  assert_equal cnt + 1, File.read(csv).lines.to_a.length
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-stat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -75,6 +75,22 @@ dependencies:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
77
  version: 0.2.4
78
+ - !ruby/object:Gem::Dependency
79
+ name: parallelize
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.4.0
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.4.0
78
94
  - !ruby/object:Gem::Dependency
79
95
  name: test-unit
80
96
  requirement: !ruby/object:Gem::Requirement