amnesia 1.0.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/LICENCE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009— Ben Schwarz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ # Amnesia
2
+
3
+ Amnesia is what you get when you lose your memory.
4
+
5
+ Hopefully with Amnesia you'll know exactly whats happening with memory when it comes to memcached.
6
+
7
+ ![Amnesia screen shot](http://farm5.static.flickr.com/4125/5030135910_698fdb4514_z_d.jpg "Amnesia")
8
+
9
+ ## Why?
10
+
11
+ Its always nice to have some statistics to see how everything is performing within your stack. Memcached seems to be a mystery box that people don't really pay alot of attention to.
12
+
13
+ Amnesia tells you how your application is performing, when it misses, when it is running sweet, when you're about to run out of memcached and (perhaps) fall down in a screaming heap.
14
+
15
+ ## What does it tell you?
16
+
17
+ All stats are since each memcached instance was restarted
18
+
19
+ Available as a cumluative result of all your memcached instances, or single instances alone:
20
+
21
+ * Cache hits and misses
22
+ * Reads and writes
23
+ * Remaining memory
24
+
25
+
26
+ Available for single instances only:
27
+
28
+ * Amount of items stored in cache
29
+ * Connections (current and total)
30
+ * Accesses (Read / Write)
31
+ * Accuracy (Hits / Misses)
32
+ * Memory (Used / Total)
33
+
34
+ ## Installation / Getting started
35
+
36
+ gem install amnesia
37
+
38
+ ### How to run it alongside your application
39
+
40
+ "config.ru":
41
+
42
+ require 'amnesia'
43
+ use Amnesia::Application, :hosts => ["localhost:11211"]
44
+ run Sinatra::Application
45
+
46
+ ### Then, cruise on over to `your-host.tld/amnesia`
47
+
48
+ ## Potential issues
49
+
50
+ * Hosts are listed as "Inactive" or "Not Responding"
51
+
52
+ Amnesia uses memcached-client to connect to memcached on the standard memcached port (11211), be sure to enter your
53
+ full hostname with the port if you are using a non standard port. (localhost:11211 will work)
54
+
55
+ Within my slices, I punched a hole through `iptables`
56
+
57
+ sudo iptables -A INPUT -i eth0 -s HOST_THAT_REQUIRES_ACCESS -p tcp --destination-port 11211 -j ACCEPT
58
+
59
+ You won't need to do this unless you've explicitly blocked ports to your server. (When in doubt, block nearly everything)
60
+
61
+ Let me know if you come accross any issues using Github messaging.
62
+
63
+ ## Something missing?
64
+
65
+ Amnesia used to be a full blown application that required a datamapper sqlite database, yml file for configuration and a bit of pain to get deployed. I decided these were all false constraints and wrapped it up as a middleware instead. Now—You can drop it alongside your rails/sinatra/rack application and see what the hell is going on with Memcached.
66
+
67
+ ## Licence
68
+
69
+ MIT, See `LICENCE` file.
@@ -0,0 +1,55 @@
1
+ require 'sinatra'
2
+ require 'memcache'
3
+ require 'gchart'
4
+ require 'haml'
5
+
6
+ $:<< File.dirname(__FILE__)
7
+
8
+ require 'amnesia/host'
9
+ require 'core_ext/array'
10
+
11
+ module Amnesia
12
+ class << self
13
+ attr_accessor :config
14
+ end
15
+
16
+ class Application < Sinatra::Base
17
+ set :public, File.join(File.dirname(__FILE__), 'amnesia', 'public')
18
+ set :views, File.join(File.dirname(__FILE__), 'amnesia', 'views')
19
+
20
+ def initialize(app, configuration = {})
21
+ Amnesia.config = configuration
22
+ super(app)
23
+ end
24
+
25
+ helpers do
26
+ def graph_url(data = [])
27
+ GChart.pie(:data => data, :size => '115x115').to_url
28
+ end
29
+
30
+ def number_to_human_size(size, precision=1)
31
+ size = Kernel.Float(size)
32
+ case
33
+ when size.to_i == 1; "1 Byte"
34
+ when size < 1.kilobyte; "%d Bytes" % size
35
+ when size < 1.megabyte; "%.#{precision}f KB" % (size / 1.0.kilobyte)
36
+ when size < 1.gigabyte; "%.#{precision}f MB" % (size / 1.0.megabyte)
37
+ when size < 1.terabyte; "%.#{precision}f GB" % (size / 1.0.gigabyte)
38
+ else "%.#{precision}f TB" % (size / 1.0.terabyte)
39
+ end.sub(/([0-9])\.?0+ /, '\1 ' )
40
+ rescue
41
+ nil
42
+ end
43
+ end
44
+
45
+ get '/amnesia' do
46
+ @hosts = Amnesia.config[:hosts].map{|host| Host.new(host)}
47
+ haml :index
48
+ end
49
+
50
+ get '/amnesia/:host' do
51
+ @host = Host.new(params[:host])
52
+ haml :host
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,29 @@
1
+ class Host
2
+ attr_reader :address
3
+
4
+ def initialize(address)
5
+ @address = address
6
+ end
7
+
8
+ def alive?
9
+ return true if connection.stats
10
+ rescue MemCache::MemCacheError
11
+ return false
12
+ end
13
+
14
+ def method_missing(method, *args)
15
+ return stats[method.to_s] if stats.has_key? method.to_s
16
+ end
17
+
18
+ def stats
19
+ connection.stats[connection.stats.keys.first]
20
+ rescue MemCache::MemCacheError
21
+ return {}
22
+ end
23
+
24
+ private
25
+
26
+ def connection
27
+ @connection ||= MemCache.new(@address)
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ /* HTML5 */
2
+ header, footer, article, section, aside, nav, menu, figure { display: block; }
3
+
4
+ /* Resets */
5
+ body, h1, h2, h3, h4, h5, h6, p, a, ul, ol, dl, dt, dd, table, caption, th, td, fieldset, legend, blockquote { font-weight: normal; margin: 0; padding: 0; color: inherit; list-style-type: none; }
6
+ a img, form, legend { border: 0; }
7
+
8
+ /* Layout */
9
+ body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 0 auto; width: 450px; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; }
10
+
11
+ h1, h2, h3 { color: #1f1f1f; }
12
+ img { display: block; }
13
+ a { color: inherit; }
14
+
15
+ header { margin: 1em 0; }
16
+ header h1 { font-size: 3.5em; }
17
+ header h1 a { color: #1f1f1f; text-decoration: none; }
18
+ header h1 a:hover:after { content: "—Huh?";}
19
+ header p { font-size: 0.8em; color: #454545; }
20
+
21
+ section#main h2 { font-size: 1em; float: right; }
22
+ section#main p.sub { color: #666; font-size: 0.9em; font-style: italic; }
23
+
24
+ section#main .stats { clear: left; padding: 1.75em 0; }
25
+ section#main :last-child { border: none; }
26
+ section#main .graph img { float: left; margin-top: -1.5em; margin-left: -1em; }
27
+ section#main .stats h3 { font-size: 1.1em; margin-left: 110px; line-height: 1.5em; }
28
+ section#main .stats p { line-height: 1.4em; font-size: 0.75em; color: #454545; margin-left: 110px; }
29
+
30
+ section#main .stats h3 span.graph-indicator { color: #ff9900; }
31
+
32
+ nav#hosts { clear: left; margin: 2em 0; }
33
+ nav#hosts li { margin: 0.5em 0; }
34
+ nav#hosts li.inactive { color: #999; }
35
+
36
+ footer { margin-top: 2em; border-top: 0.1em dotted #eee; width: 50%; margin: 2em auto; padding: 0.5em 0; }
37
+ footer p { font-size: 0.6em; color: #ccc; text-align: center; }
38
+ footer p a:hover { color: #999; }
@@ -0,0 +1,24 @@
1
+ %h2= @host.address
2
+ %p.sub #{@host.curr_items} item/s in cache, with #{@host.curr_connections} active connections
3
+
4
+ %section.stats.graph
5
+ %img{:src => graph_url([@host.bytes, @host.limit_maxbytes]) }
6
+ %h3
7
+ %span.graph-indicator Used Memory (#{number_to_human_size(@host.bytes)})
8
+ \/ Free Memory (#{number_to_human_size(@host.limit_maxbytes)})
9
+ %p The cumulative amount of free memory and total memory across all active hosts.
10
+
11
+
12
+ %section.stats.graph
13
+ %img{:src => graph_url([@host.get_hits, @host.get_misses]) }
14
+ %h3
15
+ %span.graph-indicator Hit (#{@host.get_hits})
16
+ \/ Miss (#{@host.get_misses})
17
+ %p The amount of returned caches vs misses, misses usually require your application servers to work harder.
18
+
19
+ %section.stats.graph
20
+ %img{:src => graph_url([@host.cmd_get, @host.cmd_set]) }
21
+ %h3
22
+ %span.graph-indicator Read (#{@host.cmd_get})
23
+ \/ Write (#{@host.cmd_set})
24
+ %p More writes than reads can often mean that you’re caching too early, or that you’ve not been monitoring for very long.
@@ -0,0 +1,34 @@
1
+ - if @hosts.any?{|host| host.alive? }
2
+ %section#overall
3
+ %section.stats.graph
4
+ %img{:src => graph_url([@hosts.collect{|h| h.get_hits if h.alive? }.compact.sum, @hosts.collect{|h| h.get_misses if h.alive? }.compact.sum])}
5
+ %h3
6
+ %span.graph-indicator Hit
7
+ \/ Miss
8
+ %p The amount of returned caches vs misses, misses usually require your application servers to work harder.
9
+ %section.stats.graph
10
+ %img{:src => graph_url([@hosts.collect{|h| h.cmd_get if h.alive? }.compact.sum, @hosts.collect{|h| h.cmd_set if h.alive? }.compact.sum])}
11
+ %h3
12
+ %span.graph-indicator Read
13
+ \/ Write
14
+ %p More writes than reads can often mean that you’re caching too early, or that you’ve not been monitoring for very long.
15
+
16
+ %section.stats.graph
17
+ %img{:src => graph_url([@hosts.collect{|h| h.bytes if h.alive? }.compact.sum, @hosts.collect{|h| h.limit_maxbytes if h.alive? }.compact.sum])}
18
+ %h3
19
+ %span.graph-indicator Used Memory
20
+ \/ Free Memory
21
+ %p The cumulative amount of free memory and total memory across all active hosts.
22
+
23
+ %nav#hosts
24
+ %p.sub Active Hosts
25
+ %ul
26
+ - for host in @hosts
27
+ - if host.alive?
28
+ %li
29
+ %a{:href => "/amnesia/" + host.address }= host.address
30
+ - else
31
+ %li.inactive
32
+ = host.address
33
+ (Inactive)
34
+
@@ -0,0 +1,19 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{:charset => "utf-8"}
5
+ %title Amnesia
6
+ %link{:rel => "stylesheet", :href => "/css/application.css"}
7
+ %body
8
+ %header
9
+ %h1
10
+ %a{:href => '/amnesia'} Amnesia
11
+ %p Statistics for Memcached. Wait—What?
12
+ %section#main
13
+ = yield
14
+ %footer
15
+ %p
16
+ Another
17
+ %a{:href => "http://germanforblack.com"} Ben Schwarz
18
+ joint. Get the
19
+ %a{:href => "http://github.com/benschwarz/amnesia"} Source.
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def sum
3
+ inject( nil ) { |sum,x| sum ? sum+x : x }
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amnesia
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ben Schwarz
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-28 00:00:00 +10:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: sinatra
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 1
32
+ - 0
33
+ version: "1.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: memcache-client
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 1
47
+ - 5
48
+ - 0
49
+ version: 1.5.0
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: gchart
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - "="
59
+ - !ruby/object:Gem::Version
60
+ hash: 23
61
+ segments:
62
+ - 1
63
+ - 0
64
+ - 0
65
+ version: 1.0.0
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: haml
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 7
77
+ segments:
78
+ - 3
79
+ - 0
80
+ - 0
81
+ version: 3.0.0
82
+ type: :runtime
83
+ version_requirements: *id004
84
+ description: With Amnesia you'll know exactly whats happening with memory when it comes to memcached.
85
+ email:
86
+ - ben.schwarz@gmail.com
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - lib/amnesia/host.rb
95
+ - lib/amnesia/public/css/application.css
96
+ - lib/amnesia/views/host.haml
97
+ - lib/amnesia/views/index.haml
98
+ - lib/amnesia/views/layout.haml
99
+ - lib/amnesia.rb
100
+ - lib/core_ext/array.rb
101
+ - LICENCE
102
+ - README.markdown
103
+ has_rdoc: true
104
+ homepage: http://github.com/benschwarz/amnesia
105
+ licenses: []
106
+
107
+ post_install_message:
108
+ rdoc_options: []
109
+
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ hash: 3
127
+ segments:
128
+ - 0
129
+ version: "0"
130
+ requirements: []
131
+
132
+ rubyforge_project:
133
+ rubygems_version: 1.3.7
134
+ signing_key:
135
+ specification_version: 3
136
+ summary: Amnesia is what you get when you lose your memory
137
+ test_files: []
138
+