Debugging Ruby With MongoDB
Debugging Ruby With MongoDB
Debugging Ruby With MongoDB
with MongoDB
Aman Gupta
@tmm1
Ruby developers
know...
Ruby
is
fatboyke (flickr)
Ruby loves eating RAM
37prime (flickr)
ruby allocates
memory from
the OS
memory is
broken up
into slots
each slot
holds one
ruby object
when you need an object, it’s
pulled off the freelist
john_lam (flickr)
Conservative
lifeisaprayer (flickr)
Stop
the
World
benimoto (flickr)
Mark
and
Sweep
michaelgoodin (flickr)
kiksbalayon (flickr)
Garbage
Collector
• conservative: the VM
hands out raw pointers to
ruby objects
mckaysavage (flickr)
longer GC
=
less time to run
your ruby code
kgrocki (flickr)
fewer objects
=
better
performance
januskohl (flickr)
improve performance
1. remove unnecessary object allocations
object allocations are not free
improve performance
1. remove unnecessary object allocations
object allocations are not free
1571529 /tmp/ruby.heap
version 1: analyze data
$ wc -l /tmp/ruby.heap
1571529 /tmp/ruby.heap
236840 memcached/memcached.rb:316
version 1: analyze data
$ wc -l /tmp/ruby.heap
1571529 /tmp/ruby.heap
236840 memcached/memcached.rb:316
10948 ARRAY
20355 OBJECT
30744 DATA
64952 HASH
123290 STRING
version 1
• it works!
• but...
• must patch and rebuild ruby binary
• no information about references between
objects
• limited analysis via shell scripting
version 2 goals
• better data format
• simple: one line of text per object
• expressive: include all details about
object contents and references
• easy to use: easy to generate from C
code & easy to consume from various
scripting languages
equanimity (flickr)
version 2 is memprof
• no patches to ruby necessary
• gem install memprof
• require ‘memprof’
• Memprof.dump_all(“/tmp/app.json”)
• C extension for MRI ruby VM
http://github.com/ice799/memprof
• uses libyajl to dump out all ruby objects
as json
Memprof.dump{
strings }
"hello" + "world"
{
"_id": "0x19c610", memory address of object
"file": "file.rb", file and line where string
"line": 2,
was created
"type": "string",
"class": "0x1ba7f0", address of the class
"class_name": "String", “String”
"length": 4,
"data": [
1, integers and symbols are
":b", stored in the array itself
"0x19c750", floats and strings are
"0x19c598" separate ruby objects
]
}
hashes
Memprof.dump{
{
:a => 1,
"b" => 2.2
{ }
"_id": "0x19c598", }
"type": "hash",
"class": "0x1af170",
"class_name": "Hash",
$ mongoimport
-d memprof
-c rails
--file /tmp/app.json
$ mongo memprof
thaths (flickr)
how many objects?
> db.rails.count()
809816
brettlider (flickr)
what types of objects?
> db.rails.distinct(‘type’)
[‘array’,
‘bignum’,
‘class’,
‘float’,
‘hash’,
‘module’,
‘node’,
‘object’,
‘regexp’,
‘string’,
...]
mongodb: distinct
• distinct(‘type’)
list of types of objects
• distinct(‘file’)
list of source files
• distinct(‘class_name’)
list of instance class names
• optionally filter first
• distinct(‘name’, {type:“class”})
names of all defined classes
improve performance
with indexes
> db.rails.ensureIndex({‘type’:1})
> db.rails.ensureIndex(
{‘file’:1},
{background:true}
)
mongodb: ensureIndex
• add an index on a field (if it doesn’t exist yet)
• improve performance of queries against
common fields: type, class_name, super, file
• can index embedded field names
• ensureIndex(‘methods.add’)
• find({‘methods.add’:{$exists:true}})
find classes that define the method add
how many objs per type?
darrenhester (flickr)
how many objs per type?
> db.rails.group({
initial: {count:0},
key: {type:true}, group on type
cond: {},
reduce: function(obj, out) {
increment count
out.count++
for each obj
}
}).sort(function(a,b) {
return a.count - b.count sort results
})
how many objs per type?
[
...,
{type: ‘array’, count: 7621},
{type: ‘string’, count: 69139},
{type: ‘node’, count: 365285}
]
lots of nodes
• bykey:
file & line
• {file:1, line:1}
• bycond:
type in a specific file
• {file: “app.rb”},
key: {file:1, line:1}
• bycond:
length of strings in a specific file
• {file:“app.rb”,type:‘string’},
key: {length:1}
what subclasses String?
davestfu (flickr)
what subclasses String?
> db.rails.find(
{super_name:"String"},
{name:1} select only name field
)
{name: "ActiveSupport::SafeBuffer"}
{name: "ActiveSupport::StringInquirer"}
{name: "SQLite3::Blob"}
{name: "ActiveModel::Name"}
{name: "Arel::Attribute::Expressions"}
{name: "ActiveSupport::JSON::Variable"}
mongodb: find
• find({type:‘string’})
all strings
• find({type:{$ne:‘string’}})
everything except strings
• find({type:‘string’}, {data:1})
only select string’s data field
the largest objects?
http://body.builder.hu/imagebank/pictures/1088273777.jpg
the largest objects?
> db.rails.find(
{type:
{$in:['string','array','hash']}
},
{type:1,length:1}
).sort({length:-1}).limit(3)
zoutedrop (flickr)
when were objs created?
• useful to look at objects over time
• each obj has a timestamp of when it was
created
• find minimum time, call it
start_time
• create buckets for every
minute of execution since
start
• place objects into buckets
when were objs created?
> db.rails.mapReduce(function(){
var secs = this.time - start_time;
var mins_since_start = secs % 60;
emit(mins_since_start, 1);
}, function(key, vals){
for(var i=0,sum=0; i<vals.length;
sum += vals[i++]);
return sum;
}, {
scope: { start_time: db.rails.find
().sort({time:1}).limit(1)[0].time }
} start_time = min(time)
)
{result:"tmp.mr_1272615772_3"}
mongodb: mapReduce
• arguments
• map: function that emits one or more
key/value pairs given each object this
• reduce: function to return aggregate
result, given key and list of values
• scope: global variables to set for funcs
• results
• stored in a temporary collection
(tmp.mr_1272615772_3)
when were objs created?
> db.tmp.mr_1272615772_3.count()
12
script was running for 12 minutes
> db.tmp.mr_1272615772_3.find().sort
({value:-1}).limit(1)
{_id: 8, value: 41231}
41k objects created 8 minutes after start
references to this object?
jeffsmallwood (flickr)
references to this object?
ary = [“a”,”b”,”c”]
ary references “a”
“b” referenced by ary
# in environment.rb
require `gem which memprof/signal`.strip
plugging a leak
in rails3
send the app some
requests so it leaks
$ ab -c 1 -n 30
http://localhost:3000/
holding references
to all controllers
• In development mode, Rails reloads all your
application code on every request
• ActionView::Partials::PartialRenderer is caching
partials used by each controller as an optimization
• But.. it ends up holding a reference to every single
reloaded version of those controllers
• In development mode, Rails reloads all your
application code on every request
• ActionView::Partials::PartialRenderer is caching
partials used by each controller as an optimization
• But.. it ends up holding a reference to every single
reloaded version of those controllers
Questions?
Aman Gupta
@tmm1