rack-objectspace 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 35feca0bf17fa1e774df23fb8365426fdfd7de50
4
+ data.tar.gz: 80d9c9b395bb45f2543733c9d51ba75356f81840
5
+ SHA512:
6
+ metadata.gz: 9b6724b84e1b0e0c215d27910ca140632e38b29ffba65a82a0f71c9a5d3c1d2b4e64cd2c038ac630ca410213ce0efa94011f3f6de5dec9e211fd82f7d5a33a97
7
+ data.tar.gz: c90a90bf2615ce3c0644f3f1fe284697e5cbbe3d08d1f5bb0e64c762f628795aef26239fb0ad7a4cba9d6c2c52dc1d673e260e4d188a288f1428f19cbddd4f1f
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.2
5
+ before_install: gem install bundler -v 1.10.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-objectspace.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Iain Beeston
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Iain Beeston
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # rack-objectspace
2
+ [![Build Status](https://travis-ci.org/iainbeeston/rack-objectspace.svg)](https://travis-ci.org/iainbeeston/rack-objectspace)
3
+
4
+ This is my attempt to make a rack middleware that profiles memory usage in rails (or any rack-based app), with the aim of detecting memory leaks. After *every* request it takes a dump of the objectspace and saves it to a data store of your choice\*.
5
+
6
+ \* Any object supporting the `[]=` method can be used as a data-store. I recommend [Moneta](http://github.com/minad/moneta), which has support for Redis and Amazon S3.
7
+
8
+ ## Getting Started
9
+
10
+ After installing the gem, insert rack-objectspace into your middleware stack:
11
+
12
+ ~~~
13
+ config.middleware.insert_after ActionDispatch::Static, Rack::Objectspace, store: Moneta.new(:Redis)
14
+ ~~~
15
+
16
+ I'd recommend installing rack-objectspace as low as you can in your stack. Preferably only requests that hit your own application code should be profiled.
17
+
18
+ ## Warnings
19
+
20
+ *DO NOT USE THIS IN PRODUCTION* (it's *VERY* slow - I'd recommend running it on a close-copy of production instead)
21
+
22
+ Every objectspace dump is unique to a particular ruby process. Make sure your server is only running a single worker when profiling.
23
+
24
+ I've found that with unicorn, requests will usually time-out when using rack-objectspace. Try increasing the `timeout` value in unicorn.rb.
25
+
26
+ ## How to find memory leaks
27
+
28
+ Right now this bit is manual. rack-objectspace will give you the objectspace for a running app. I'd recommend taking the objectspace dumps for several successive requests and looking for objects that aren't garbage collected, as @wagenet did in his [post on the Skylight blog](http://blog.skylight.io/hunting-for-leaks-in-ruby/).
29
+
30
+ ## Credits
31
+
32
+ This project was heavily inspired by blog posts by [@samsaffron](http://samsaffron.com/archive/2015/03/31/debugging-memory-leaks-in-ruby) and [@wagenet](http://blog.skylight.io/hunting-for-leaks-in-ruby/). I take no credit for the technique used here.
33
+
34
+ Also, many thanks to [@newbamboo](https://www.new-bamboo.co.uk) for letting me work on this during office hours.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rack/objectspace"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,69 @@
1
+ require 'objspace'
2
+ require 'yajl'
3
+ require 'securerandom'
4
+
5
+ module Rack
6
+ class Objectspace
7
+ KEY_SEPARATOR = '-'.freeze
8
+
9
+ def initialize(app, store:, async: true, object_space: ObjectSpace)
10
+ @app = app
11
+ @store = store
12
+ @async = async
13
+ @object_space = object_space
14
+ @object_space.trace_object_allocations_start
15
+ end
16
+
17
+ def call(env)
18
+ result = @app.call(env)
19
+
20
+ dumper = Thread.new { dump_allocations(env) }
21
+ dumper.join unless @async
22
+
23
+ result
24
+ end
25
+
26
+ def dump_allocations(env)
27
+ read, write = IO.pipe
28
+
29
+ if pid = fork
30
+ # parent
31
+ read.close
32
+ GC.start
33
+ @object_space.dump_all(output: write)
34
+ write.close
35
+ Process.wait(pid) unless @async
36
+ else
37
+ # child
38
+ write.close
39
+ parse_dump(input: read, key: request_id(request: env, pid: Process.ppid))
40
+ read.close
41
+ end
42
+ end
43
+
44
+ def parse_dump(input:, key:, run: ->(id, obj) { store_object(id, obj) })
45
+ parser = Yajl::Parser.new
46
+ parser.on_parse_complete = lambda do |obj|
47
+ run.call(key, obj)
48
+ end
49
+ parser.parse(input)
50
+ end
51
+
52
+ def store_object(request_id, obj)
53
+ object_id = obj.delete('address')
54
+ if object_id
55
+ obj.each do |attr, value|
56
+ @store[[request_id, object_id, attr].join(KEY_SEPARATOR)] = value
57
+ end
58
+ end
59
+ end
60
+
61
+ def request_id(request:, pid:, time: Time)
62
+ timestamp = time.now.to_i
63
+ request_path = request['PATH_INFO'].tr_s(::File::SEPARATOR, '-').sub!(/^-/, '').downcase
64
+ request_method = request['REQUEST_METHOD'].downcase
65
+ random_salt = SecureRandom.hex(4)
66
+ ['rack-objectspace', pid, timestamp, request_path, request_method, random_salt].join(KEY_SEPARATOR)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class Objectspace
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/objectspace/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-objectspace"
8
+ spec.version = Rack::Objectspace::VERSION
9
+ spec.authors = ["Iain Beeston"]
10
+ spec.email = ["iain.beeston@gmail.com"]
11
+
12
+ spec.summary = %q{Saves objectspace to a key-value store after every request}
13
+ spec.homepage = "http://github.com/iainbeeston/rack-objectspace"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|tmp)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = '>= 2.1'
22
+
23
+ spec.add_dependency "rack"
24
+ spec.add_dependency "yajl-ruby"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.10"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.3"
29
+ spec.add_development_dependency "moneta", "~> 0.8"
30
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-objectspace
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Iain Beeston
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-06-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yajl-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: moneta
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ description:
98
+ email:
99
+ - iain.beeston@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - LICENSE
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - bin/console
113
+ - bin/setup
114
+ - lib/rack/objectspace.rb
115
+ - lib/rack/objectspace/version.rb
116
+ - rack-objectspace.gemspec
117
+ homepage: http://github.com/iainbeeston/rack-objectspace
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '2.1'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.4.5
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Saves objectspace to a key-value store after every request
141
+ test_files: []