memcached_stats 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb7d3b8523d38561f42308aca4abd8fda160fd71
4
+ data.tar.gz: f6bcbd334b14fee0a16c22a51d1b5056a65fd66f
5
+ SHA512:
6
+ metadata.gz: d5ba210fc118497116689a2ee235f09d899a1903b8bc2c4e158c34908a892cce60a35d0d8bcab482fdecee0853aa919618eed0d6cf33c6014647d0a6e1c2bd1b
7
+ data.tar.gz: 2793035a28ceeab51f4c0a6400ab826d21208c18785f8edffa6496713b8d9701cee876f5ea2981c577092a704947192a016d6ffe911ebfcb456553bd5f105982
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 William Schneider
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.textile ADDED
@@ -0,0 +1,216 @@
1
+ h1. Memcached Stats
2
+
3
+ <i>*Note:* This gem is probably not yet ready for production environments. Use at your own discretion.</i>
4
+
5
+ The goal of this gem is to provide overall memcached statistics (uptime, size, etc/) as well as an advanced look at cache slabs, providing slab item statistics as well as slab keys and values arranged hierarchically. As of the current release, this gem does not use the memcached binary protocol, therefore memcached versions prior to 1.4 (as well as 1.4+) should work fine with this gem. Please open an issue through github specifying your version of memcached if you find this is not the case.
6
+
7
+
8
+ The following usage guidelines are subject to change...at least until 0.1.x release (which may or may not occur).
9
+
10
+ h2. Usage
11
+
12
+ h3. Basics
13
+
14
+ <pre>
15
+ <code>
16
+ $ gem install memcached_stats
17
+ </code>
18
+ </pre>
19
+
20
+ (You may need to install the gem with @sudo@ prepended)
21
+
22
+ The steps below assume usage from irb, however this gem can just as easily be used within a Rails project.
23
+
24
+ First, require the @memcached_stats@ gem.
25
+
26
+ <pre>
27
+ <code>
28
+ require 'memcached_stats'
29
+ </code>
30
+ </pre>
31
+
32
+ memcached_stats assumes a default host of @localhost@ and a port of @11211@. Values other than the default can be specified when creating a new @MemcachedStats@ object:
33
+
34
+ <pre>
35
+ <code>
36
+ stats = MemcachedStats.new(your_host, your_port)
37
+ </code>
38
+ </pre>
39
+
40
+ After you create the new @MemcachedStats@ object, you can fetch the basic stats from memcached:
41
+
42
+ <pre>
43
+ <code>
44
+ stats.fetch_stats
45
+ </code>
46
+ </pre>
47
+
48
+ h3. Summary Statistics
49
+
50
+ Once you do this, you have access to the following default memcached statistics through the following instance accessor methods (via attributes courtesy of @method_missing@) which are described in greater detail "on the memcached github page":https://github.com/memcached/memcached/blob/master/doc/protocol.txt under the "General-purpose statistics" heading. The definitions below are copied directly from this document:
51
+
52
+ *NOTE:* These memcached statistics methods are somewhat memcached version specific. They are defined in the text protocol of your particular memcached version, which can be viewed by visiting the github page linked above and switching to the tag which corresponds to your memcached version. Alternatively, via irb following the steps above, run @stats.summary.keys@ to view the keys which @MemcachedStats@ was able to attain.
53
+
54
+ @pid@ - Process id of this server process
55
+ @uptime@ - Number of secs since the server started
56
+ @time@ - current UNIX time according to the server
57
+ @version@ - Version string of this server
58
+ @pointer_size@ - Default size of pointers on the host OS (generally 32 or 64)
59
+ @rusage_user@ - Accumulated user time for this process (seconds:microseconds)
60
+ @rusage_system@ - Accumulated system time for this process (seconds:microseconds)
61
+ @curr_items@ - Current number of items stored
62
+ @total_items@ - Total number of items stored since the server started
63
+ @bytes@ - Current number of bytes used to store items
64
+ @curr_connections@ - Number of open connections
65
+ @total_connections@ - Total number of connections opened since the server started running
66
+ @connection_structures@ - Number of connection structures allocated by the server
67
+ @cmd_get@ - Cumulative number of retrieval requests
68
+ @cmd_set@ - Cumulative number of storage requests
69
+ @cmd_flush@ - Cumulative number of flush requests
70
+ @get_hits@ - Number of keys that have been requested and found present
71
+ @get_misses@ - Number of items that have been requested and not found
72
+ @delete_misses@ - Number of deletions requests for missing keys
73
+ @delete_hits@ - Number of deletion requests resulting in an item being removed
74
+ @incr_misses@ - Number of increment requests against missing keys
75
+ @incr_hits@ - Number of successful increment requests
76
+ @decr_misses@ - Number of decrement requests against missing keys
77
+ @decr_hits@ - Number of successful decrement requests
78
+ @cas_misses@ - Number of CAS reqs against missing keys
79
+ @cas_hits@ - Number of successful CAS reqs
80
+ @cas_badval@ - Number of CAS reqs for which a key was found, but the CAS value did not match
81
+ @auth_cmds@ - Number of authentication commands handled, success or failure
82
+ @auth_errors@ - Number of failed authentications
83
+ @evictions@ - Number of valid items removed from cache to free memory for new items
84
+ @reclaimed@ - Number of times an entry was stored using memory from an expired entry
85
+ @bytes_read@ - Total number of bytes read by this server from network
86
+ @bytes_written@ - Total number of bytes sent by this server to network
87
+ @limit_maxbytes@ - Number of bytes this server is allowed to use for storage
88
+ @accepting_conns@ -
89
+ @listen_disabled_num@ -
90
+ @threads@ - Number of worker threads requested
91
+ @conn_yields@ - Number of times any connection yielded to another due to hitting the memcached @-R@ limit
92
+
93
+
94
+ The following are defined as well, providing a few more useful metrics:
95
+
96
+ @hit_ratio@ - Number of keys requested and found present over the total number of retrieval requests
97
+ @miss_ratio@ - Number of keys requested and not found over the total number of retrieval requests
98
+ @request_rate@ - Number of key retrieval requests over the memcached instance uptime (aka 'requests per second')
99
+ @hit_rate@ - Number of keys requested and found present over the memcached instance uptime (aka 'cache hits per second')
100
+ @miss_rate@ - Number of keys requested and not found over the memcached instance uptime (aka 'cache misses per second')
101
+
102
+
103
+
104
+ h3. Slab Item Statistics
105
+
106
+ The @fetch_stats@ instance method not only provides top level memcached statistics, but also gathers statistics for memcached slabs. Each slab is accessible via the @slabs@ accessor: a hash of hashes, the first of which is keyed by the slab_id, then the resultant hash is keyed by the statistic of interest. So for example:
107
+
108
+ <pre>
109
+ <code>
110
+ stats.slabs # => {"1"=>{"number"=>"1234", "age"=>"7000", "evicted"=>"0", "evicted_nonzero"=>"0", "evicted_time"=>"0", "outofmemory"=>"0", "tailrepairs"=>"0", "reclaimed"=>"0"}, "2"=>{"number"=>"122", "age"=>"161000", "evicted"=>"0", "evicted_nonzero"=>"0", "evicted_time"=>"0", "outofmemory"=>"0", "tailrepairs"=>"0", "reclaimed"=>"1"}...}
111
+
112
+ stats.slabs["1"] # => {"number"=>"1234", "age"=>"7000", "evicted"=>"0", "evicted_nonzero"=>"0", "evicted_time"=>"0", "outofmemory"=>"0", "tailrepairs"=>"0", "reclaimed"=>"0"}
113
+
114
+ stats.slabs["1"].number # => "1234"
115
+ </code>
116
+ </pre>
117
+
118
+
119
+ "on the memcached github page":https://github.com/memcached/memcached/blob/master/doc/protocol.txt under the "Item statistics" heading, the slabs statistics represented in the slabs hash above are explained as follows:
120
+
121
+
122
+ @number@ - Number of items presently stored in this class (expired items are not automatically excluded)
123
+ @age@ - Age of the oldest item in the LRU
124
+ @evicted@ - Number of times an item had to be evicted from the LRU before it expired
125
+ @evicted_nonzero@ - Number of times an item which had an explicit expire time set had to be evicted from the LRU before it expired
126
+ @evicted_time@ - Seconds since the last access for the most recent item evicted from this class (use this to judge how recently active your evicted data is)
127
+ @outofmemory@ - Number of times the underlying slab class was unable to store a new item (this means you are running memcached with @-M@ or an eviction failed)
128
+ @tailrepairs@ - Number of times we self-healed a slab with a refcount leak
129
+ @reclaimed@ - Number of times an entry was stored using memory from an expired entry
130
+
131
+
132
+ h3. Slab Keys
133
+
134
+
135
+ One of the nice features of this gem is the ability to fetch all slab keys in a hierarchical hash. This is best explained with an example:
136
+
137
+ Suppose your stored the following keys and corresponding values in memcached
138
+
139
+ <pre>
140
+ "User:bananastalktome:EmailCount" = 1
141
+ "User:bananastalktome:Alerts" = 5
142
+ "User:anotheruser:EmailCount" = 0
143
+ "Post:Article:12:title" = "This article is cool"
144
+ </pre>
145
+
146
+ Using this gem, call @fetch_all_slab_keys@ on the @MemcachedStats@ object (@stats@) created above. The following hash will result, as an attribute called @slab_keys@:
147
+
148
+ <pre>
149
+ {
150
+ {
151
+ "User" =>
152
+ {
153
+ "bananastalktome" =>
154
+ {
155
+ "EmailCount" =>
156
+ {
157
+ }
158
+ , "Alerts" =>
159
+ {
160
+
161
+ }
162
+ }
163
+ , "anotheruser" =>
164
+ {
165
+ "EmailCount" =>
166
+ {
167
+ }
168
+ }
169
+ }
170
+ , "Post" =>
171
+ {
172
+ "Article" =>
173
+ {
174
+ "12" =>
175
+ {
176
+ "title" =>
177
+ {
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ </pre>
184
+
185
+ Arranging the cache keys in this manner allows quick traversal down a hash to determine if a given key exists and, if it does, in which slab the key exists. For the lowest level of each tree in the hash, there is a @prod@ defined called @value@, which can be invoked to retrieve the value for that particular cache key. For example:
186
+
187
+ <pre>
188
+ <code>
189
+ stats.slab_keys["User"]["bananastalktome"]["EmailCount"].value # => 1
190
+ </code>
191
+ </pre>
192
+
193
+
194
+ h2. Experimental Branch
195
+
196
+ In the experimental branch of this project, I am messing with the ability to gather stats from multiple memcached instances configured in a cluster (as done in many production Rails projects) <i>in an intuitive way</i>. If you would like to help out with this, please
197
+
198
+
199
+ h2. Bugs and Feedback
200
+
201
+ If you notice any bugs, would like any features added, or would just like to write code or tests ( <i>hint hint :)</i> ), please do not hesitate to do so! Either follow the *Note on Patches/Pull Requests* guidelines below, submit a issue through github, or email me directly at bananastalktome@gmail.com.
202
+
203
+
204
+ h2. Note on Patches/Pull Requests
205
+
206
+ * Fork the project.
207
+ * Make your feature addition or bug fix.
208
+ * Add tests for it. This is important so I don't break it in a
209
+ future version unintentionally. (I tend to be lax on tests, so if you want to make some for the code that I already have, you get bonus points!)
210
+ * Commit, do not mess with Rakefile, VERSION, history, or other files not directly relevant to your contribution.
211
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
212
+ * Send me a pull request. Bonus points for fun request messages.
213
+
214
+ h2. Copyright
215
+
216
+ Copyright (c) 2010 William Schneider. See LICENSE for details.
@@ -0,0 +1,150 @@
1
+ class MemcachedStats
2
+ attr_reader :host, :port, :summary, :slabs, :slab_keys
3
+
4
+ ## Altered code from watsonian/memcache_do gem
5
+ def self.service_connection(command, host, port, &block)
6
+ begin
7
+ data = ''
8
+ sock = TCPSocket.new(host, port)
9
+ sock.print("#{command}\r\n")
10
+ sock.flush
11
+ stats = sock.gets
12
+ while true
13
+ data += stats
14
+ break if ['END', 'OK', 'ERROR'].include? stats.strip
15
+ stats = sock.gets
16
+ end
17
+ sock.close
18
+ data
19
+ rescue => e
20
+ ## ToDo: REVISIT LATER, MAYBE RAISE SOMETHING
21
+ #puts e
22
+ end
23
+ end
24
+
25
+ def initialize(host = "localhost", port = 11211)
26
+ @host = host
27
+ @port = port.to_i
28
+ @slab_keys = { }
29
+ self
30
+ end
31
+
32
+ def fetch_stats
33
+ get_summary
34
+ get_slabs
35
+ self
36
+ end
37
+
38
+ #def inspect
39
+ #[@host, @port, @summary]
40
+ #end
41
+
42
+ # # Commented out since I feel (at least at the moment) that it is out of the scope of what
43
+ # # this gem should do.
44
+ # def flush
45
+ # MemcachedStats.service_connection("flush_all", @host, @port)
46
+ # end
47
+
48
+ def fetch_all_slab_keys
49
+ get_slabs unless @slabs
50
+ @slabs.each do |slab|
51
+ fetch_single_slab_keys(slab)
52
+ end
53
+ true
54
+ end
55
+
56
+
57
+ def fetch_single_slab_keys(slab)
58
+ slab_id = slab.is_a?(Fixnum) ? slab : (slab.is_a?(Array) ? slab.first : nil)
59
+
60
+ MemcachedStats.service_connection("stats cachedump #{slab_id} 0", @host, @port).sub("END\r\n","").each_line do |line|
61
+ #tmp = line.sub("ITEM ", "").split(" ").first.split(":")
62
+
63
+ ## ToDo: Colon might not be delineator
64
+
65
+ tmp = line.match(/ITEM\ (.*?)\ \[(\d*)\ b\;\ (\d*)\ s\]/).to_a[1..-1] # 0:key, 1:size, 2:expires
66
+ #ITEM User:3293:EmailCount [4 b; 1289245903 s]
67
+ # [size; expires_timestamp]
68
+
69
+ record_slab_data(tmp, slab_id)
70
+ end
71
+ true
72
+ end
73
+
74
+
75
+ protected
76
+
77
+ def get_summary
78
+ @summary = Hash[*MemcachedStats.service_connection("stats", @host, @port).sub("END\r\n","").each_line.inject([]){|arr, line| arr.push(line.sub("STAT ", "").split(' '))}.flatten]
79
+ @summary["hit_ratio"] = @summary["get_hits"].to_f / @summary["cmd_get"].to_f
80
+ @summary["miss_ratio"] = @summary["get_misses"].to_f / @summary["cmd_get"].to_f
81
+ @summary["request_rate"] = @summary["cmd_get"].to_f / @summary["uptime"].to_i
82
+ @summary["hit_rate"] = @summary["get_hits"].to_f / @summary["uptime"].to_i
83
+ @summary["miss_rate"] = @summary["get_misses"].to_f / @summary["uptime"].to_i
84
+ @summary.freeze
85
+ end
86
+
87
+ def get_slabs
88
+ @slabs = Hash.new{|h,k| h[k] = {} unless h.frozen? }
89
+ MemcachedStats.service_connection("stats items", @host, @port).sub("END\r\n","").each_line do |line|
90
+ l = (line.sub("STAT items:", "").split(' '))
91
+ k,v = l.first.split(":")
92
+ slabs[k][v] = l.last
93
+ end
94
+ @slabs.freeze
95
+ end
96
+
97
+ private
98
+
99
+ def record_slab_data(data, slab_id)
100
+ cache_key = data[0] #data.join(":")
101
+ key = cache_key.split(":")
102
+ first = key.shift
103
+ @slab_keys[first] = { } unless @slab_keys[first]
104
+ pointer = @slab_keys[first]
105
+ while elem = key.shift
106
+ pointer[elem] = { } unless pointer[elem]
107
+ pointer = pointer[elem]
108
+ end
109
+
110
+ host = @host
111
+ port = @port
112
+
113
+ pointer.define_singleton_method(:value) do
114
+ Proc.new do
115
+ #a = MemcachedStats.service_connection("get #{cache_key}", host, port).split
116
+ ## a => ["VALUE", "Billy:TestKey", "1", "29", "\x04\bI\"\bHey\x06:", "encoding\"", "US-ASCII", "END"]
117
+ #value = a[4..-4].join(" ")
118
+ #value += "\r#{a[-3]}\r#{a[-2]}"
119
+
120
+ socket = TCPSocket.new(host, port)
121
+ socket.write("get #{cache_key}\r\n")
122
+ socket.gets
123
+ value = socket.read(data[1].to_i)
124
+ socket.close
125
+
126
+ begin
127
+ Marshal.load value
128
+ rescue TypeError # Meaning marshalling failed
129
+ value
130
+ end
131
+ end
132
+ end
133
+
134
+ ## ToDo: Add a delete key method??
135
+
136
+ #pointer["slab"] = slab_id # Not really useful, I guess
137
+ #pointer["size"] = data[1] # The size (in bytes)
138
+ #pointer["expires"] = data[2] # Unix timestamp of expiration
139
+ end
140
+
141
+
142
+ def method_missing(method, *arguments, &block)
143
+ if @summary && @summary.key?(method.to_s)
144
+ @summary[method.to_s]
145
+ else
146
+ super
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,6 @@
1
+ require 'socket'
2
+
3
+
4
+ Dir["#{File.dirname(__FILE__) + '/memcached_stats'}/**/*"].each do |filter|
5
+ require "#{filter}"
6
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'memcached_stats'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestMemcachedStats < Test::Unit::TestCase
4
+ should "" do
5
+
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memcached_stats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - William Schneider
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2010-12-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thoughtbot-shoulda
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Gather Memcached Stats as well as Slabs
28
+ email: bananastalktome@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.textile
34
+ files:
35
+ - LICENSE
36
+ - README.textile
37
+ - lib/memcached_stats.rb
38
+ - lib/memcached_stats/base.rb
39
+ - test/helper.rb
40
+ - test/test_memcached_stats.rb
41
+ homepage: https://github.com/bananastalktome/memcached_stats
42
+ licenses: []
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options:
46
+ - "--charset=UTF-8"
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.5.1
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Gather Memcached Stats as well as Slabs
65
+ test_files:
66
+ - test/test_memcached_stats.rb
67
+ - test/helper.rb