redu 0.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/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