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 +19 -0
- data/README.markdown +69 -0
- data/lib/amnesia.rb +55 -0
- data/lib/amnesia/host.rb +29 -0
- data/lib/amnesia/public/css/application.css +38 -0
- data/lib/amnesia/views/host.haml +24 -0
- data/lib/amnesia/views/index.haml +34 -0
- data/lib/amnesia/views/layout.haml +19 -0
- data/lib/core_ext/array.rb +5 -0
- metadata +138 -0
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.
|
data/README.markdown
ADDED
|
@@ -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
|
+

|
|
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.
|
data/lib/amnesia.rb
ADDED
|
@@ -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
|
data/lib/amnesia/host.rb
ADDED
|
@@ -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.
|
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
|
+
|