redu 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in redu.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ redu (0.0.3)
5
+ algorithms
6
+ progressbar
7
+ redis (>= 2.2.2)
8
+ terminal-table
9
+ thor (>= 0.14.6)
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ algorithms (0.3.0)
15
+ progressbar (0.10.0)
16
+ redis (2.2.2)
17
+ terminal-table (1.4.4)
18
+ thor (0.14.6)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ algorithms
25
+ bundler (>= 1.0.0)
26
+ progressbar
27
+ redis (>= 2.2.2)
28
+ redu!
29
+ terminal-table
30
+ thor (>= 0.14.6)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Curtis Spencer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ == Introduction ==
2
+ The goal of Redu is to be similar to the unix du command for your redis
3
+ installation. Many times you want to figure out what keys are eating up your
4
+ redis memory.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+ include Rake::DSL
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.ruby_opts = ['-rubygems'] if defined? Gem
9
+ t.ruby_opts << '-I.'
10
+ end
11
+
12
+ task :test_redu do |t|
13
+ system("bundle exec bin/redu")
14
+ end
data/bin/redu ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'redu/cli'
3
+ Redu::CLI.start
data/lib/redu.rb ADDED
@@ -0,0 +1 @@
1
+ require 'redu/version'
@@ -0,0 +1,192 @@
1
+ require 'redis'
2
+ require 'benchmark'
3
+ require 'progressbar'
4
+ require 'terminal-table'
5
+ require 'algorithms'
6
+ module Redu
7
+ class RedisKeyValue
8
+ attr_accessor :key,:bytesize,:type
9
+ end
10
+
11
+ class KeyPrefix
12
+ attr_accessor :count, :bytesize
13
+ end
14
+
15
+ class WorstOffenderSet
16
+
17
+ def initialize(max_count)
18
+ @offenders = {}
19
+ @max_count = max_count
20
+ @least_worst_object = nil
21
+
22
+ @offenders = Containers::MinHeap.new
23
+ end
24
+
25
+ def add(redis_debug_object)
26
+ # If it's full and you aren't big enough skip you
27
+ #puts "Least Worst Object: #{@least_worst_object.inspect}"
28
+ if @offenders.size < @max_count
29
+ @offenders.push(redis_debug_object.bytesize,redis_debug_object)
30
+ else
31
+ min = @offenders.min
32
+ if min.bytesize < redis_debug_object.bytesize
33
+ @offenders.pop
34
+ @offenders.push(redis_debug_object.bytesize,redis_debug_object)
35
+ end
36
+ end
37
+ end
38
+
39
+ def max_sort
40
+ output = []
41
+ loop do
42
+ output << @offenders.pop
43
+ break if @offenders.size == 0
44
+ end
45
+ output.reverse
46
+ end
47
+
48
+ def sorted_by_offense
49
+ sorted_offenders = @offenders.sort do |a,b|
50
+ b[1].bytesize <=> a[1].bytesize
51
+ end
52
+ output_offenders = []
53
+ sorted_offenders.each do |pair|
54
+ output_offenders << pair[1]
55
+ end
56
+ return output_offenders
57
+ end
58
+ end
59
+
60
+ class RedisDebugObject
61
+ # "Value at:0x7f93e48464e0 refcount:1 encoding:raw serializedlength:230 lru:710236 lru_seconds_idle:2040"
62
+ attr_accessor :bytesize,:key
63
+
64
+ def self.initialize_from_string(key,string)
65
+ RedisDebugObject.new(key,string)
66
+ end
67
+
68
+ def initialize(key,string)
69
+ @key = key
70
+ if string =~ /serializedlength:(\d+)/
71
+ @bytesize = $1.to_i
72
+ else
73
+ raise "Invalid matching for #{string}"
74
+ end
75
+ end
76
+
77
+ def inspect
78
+ "rdo:(#{@key},#{@bytesize})"
79
+ end
80
+
81
+ def <=>(other)
82
+ self.bytesize <=> other.bytesize
83
+ end
84
+ end
85
+
86
+ class Analyzer
87
+ def initialize(options)
88
+ @redis = Redis.new(options)
89
+ @delimiter = options[:delimiter]
90
+ @prefixes = {}
91
+ @worst_offender_set = WorstOffenderSet.new(options[:worst_offender_count])
92
+ end
93
+
94
+ def start
95
+ keys = []
96
+ STDERR.puts "Loading Keys for Analysis. This may take a few minutes"
97
+ keys = @redis.keys
98
+ pb = ProgressBar.new("Analyzing",keys.size)
99
+ keys.each do |key|
100
+ analyze_key(key)
101
+ pb.inc
102
+ end
103
+ pb.finish
104
+
105
+ output_prefix_table
106
+ output_worst_offenders_table
107
+ end
108
+
109
+ protected
110
+ GIGA_SIZE = 1073741824.0
111
+ MEGA_SIZE = 1048576.0
112
+ KILO_SIZE = 1024.0
113
+
114
+ def human_size(bytesize)
115
+ precision = 3
116
+ # Return the file size with a readable style.
117
+ if bytesize == 1
118
+ "1 Byte"
119
+ elsif bytesize < KILO_SIZE
120
+ "%d Bytes" % bytesize
121
+ elsif bytesize < MEGA_SIZE
122
+ "%.#{precision}f KB" % (bytesize / KILO_SIZE)
123
+ elsif bytesize < GIGA_SIZE
124
+ "%.#{precision}f MB" % (bytesize / MEGA_SIZE)
125
+ else
126
+ "%.#{precision}f GB" % (bytesize / GIGA_SIZE)
127
+ end
128
+ end
129
+
130
+ def output_prefix_table
131
+ rows = []
132
+ sorted_prefixes = @prefixes.sort do |a,b|
133
+ b[1].bytesize <=> a[1].bytesize
134
+ end
135
+
136
+ sorted_prefixes.each do |pair|
137
+ prefix = pair[1]
138
+ rows << [pair[0],prefix.count,human_size(prefix.bytesize)]
139
+ end
140
+
141
+ table = Terminal::Table.new :title => "Prefixes", :headings => ['Prefix', 'Count', 'Size'], :rows => rows
142
+ table.align_column(1,:right)
143
+ table.align_column(2,:right)
144
+ puts table
145
+ end
146
+
147
+ def output_worst_offenders_table
148
+ sorted_offenders = @worst_offender_set.max_sort
149
+ rows = []
150
+ sorted_offenders.each do |offender|
151
+ rows << [offender.key,human_size(offender.bytesize)]
152
+ end
153
+ table = Terminal::Table.new :title => "Worst Offenders", :headings => ['Key', 'Size'], :rows => rows
154
+ table.align_column(1,:right)
155
+ puts table
156
+ end
157
+
158
+ def derive_type
159
+ end
160
+
161
+ def derive_prefix(key)
162
+ first_delimiter = key.index(@delimiter)
163
+ if !first_delimiter.nil?
164
+ prefix = key[0,first_delimiter]
165
+ return prefix
166
+ end
167
+ return nil
168
+ end
169
+
170
+ def analyze_key(key)
171
+ obj_str = @redis.debug("OBJECT", key)
172
+
173
+ rdo = RedisDebugObject.initialize_from_string(key,obj_str)
174
+
175
+ prefix_str = derive_prefix(key)
176
+ if prefix_str
177
+ prefix = @prefixes[prefix_str]
178
+ if !prefix
179
+ prefix = KeyPrefix.new
180
+ prefix.count = 1
181
+ prefix.bytesize = rdo.bytesize
182
+ @prefixes[prefix_str] = prefix
183
+ else
184
+ prefix.count += 1
185
+ prefix.bytesize += rdo.bytesize
186
+ end
187
+ end
188
+
189
+ @worst_offender_set.add(rdo)
190
+ end
191
+ end
192
+ end
data/lib/redu/cli.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'thor'
2
+ require 'redu/analyzer'
3
+
4
+ module Redu
5
+ class CLI < Thor
6
+ default_task("analyze")
7
+
8
+ desc "analyze", "Analyze a redis server on a particular host"
9
+ method_option :host, :type => :string, :aliases => '-h',:default => 'localhost'
10
+ method_option :port, :type => :numeric, :aliases => '-p', :default => 6379
11
+ method_option :delimiter, :type => :string, :aliases => '-d', :default => '|'
12
+ method_option :worst_offender_count, :type => :string, :aliases => '-w', :default => 25
13
+ def analyze
14
+ Redu::Analyzer.new(options).start
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Redu
2
+ VERSION = "0.0.3"
3
+ end
data/redu.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "redu/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "redu"
7
+ s.version = Redu::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Curtis Spencer"]
10
+ s.email = ["curtis@sevenforge.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Redu is way to find where your memory is being spent in Redis}
13
+ s.description = %q{Use Redu to find culprits and bad memory usage in your Redis installation}
14
+
15
+ s.rubyforge_project = "redu"
16
+ #s.add_dependency 'system_timer'
17
+ s.add_dependency 'progressbar'
18
+ s.add_dependency 'algorithms'
19
+ s.add_dependency "redis",">= 2.2.2"
20
+ s.add_dependency "thor",">= 0.14.6"
21
+ s.add_dependency "terminal-table"
22
+
23
+ s.add_development_dependency "bundler", ">= 1.0.0"
24
+
25
+ s.files = `git ls-files`.split("\n")
26
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
+ s.require_paths = ["lib"]
29
+ end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+ require 'bundler/setup'
3
+
4
+ testdir = File.dirname(__FILE__)
5
+ $LOAD_PATH.unshift testdir unless $LOAD_PATH.include?(testdir)
6
+
7
+ libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
8
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redu
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 3
9
+ version: 0.0.3
10
+ platform: ruby
11
+ authors:
12
+ - Curtis Spencer
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-02-05 00:00:00 -08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: progressbar
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: algorithms
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: redis
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 2
56
+ - 2
57
+ - 2
58
+ version: 2.2.2
59
+ type: :runtime
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: thor
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ - 14
72
+ - 6
73
+ version: 0.14.6
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: terminal-table
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ type: :runtime
88
+ version_requirements: *id005
89
+ - !ruby/object:Gem::Dependency
90
+ name: bundler
91
+ prerelease: false
92
+ requirement: &id006 !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 1
99
+ - 0
100
+ - 0
101
+ version: 1.0.0
102
+ type: :development
103
+ version_requirements: *id006
104
+ description: Use Redu to find culprits and bad memory usage in your Redis installation
105
+ email:
106
+ - curtis@sevenforge.com
107
+ executables:
108
+ - redu
109
+ extensions: []
110
+
111
+ extra_rdoc_files: []
112
+
113
+ files:
114
+ - Gemfile
115
+ - Gemfile.lock
116
+ - LICENSE.txt
117
+ - README.md
118
+ - Rakefile
119
+ - bin/redu
120
+ - lib/redu.rb
121
+ - lib/redu/analyzer.rb
122
+ - lib/redu/cli.rb
123
+ - lib/redu/version.rb
124
+ - redu.gemspec
125
+ - test/test_helper.rb
126
+ has_rdoc: true
127
+ homepage: ""
128
+ licenses: []
129
+
130
+ post_install_message:
131
+ rdoc_options: []
132
+
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ requirements: []
152
+
153
+ rubyforge_project: redu
154
+ rubygems_version: 1.3.7
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Redu is way to find where your memory is being spent in Redis
158
+ test_files:
159
+ - test/test_helper.rb