hyphy 0.0.0 → 0.0.1
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/Gemfile +1 -3
- data/Gemfile.lock +15 -10
- data/README.md +83 -1
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/hyphy.gemspec +15 -22
- data/lib/hyphy/filters/abstract_filter.rb +5 -5
- data/lib/hyphy/filters/benchmark_filter.rb +30 -0
- data/lib/hyphy/filters/duration_filter.rb +14 -5
- data/lib/hyphy/filters/limit_filter.rb +13 -0
- data/lib/hyphy/filters/sql_filter.rb +21 -0
- data/lib/hyphy/orm_adapters/abstract_orm_adapter.rb +4 -2
- data/lib/hyphy/orm_adapters/activerecord_adapter.rb +20 -2
- data/lib/hyphy/sampler.rb +29 -8
- data/lib/hyphy/sql_statement.rb +52 -0
- data/lib/hyphy.rb +5 -4
- data/spec/filters/abstract_filter_spec.rb +6 -6
- data/spec/filters/benchmark_filter_spec.rb +38 -0
- data/spec/filters/duration_filter_spec.rb +28 -16
- data/spec/filters/limit_filter_spec.rb +21 -0
- data/spec/filters/sql_filter_spec.rb +31 -0
- data/spec/models/sql_statement_spec.rb +33 -11
- data/spec/orm_adapters/activerecord_orm_adapter_spec.rb +34 -2
- data/spec/sampler_spec.rb +55 -4
- data/spec/spec_helper.rb +0 -4
- metadata +23 -51
- data/.document +0 -5
- data/lib/hyphy/database.rb +0 -3
- data/lib/hyphy/dataset.rb +0 -14
- data/lib/hyphy/dataset_collection.rb +0 -35
- data/lib/hyphy/models/sql_statement.rb +0 -49
- data/spec/dataset_collection_spec.rb +0 -38
- data/spec/dataset_spec.rb +0 -32
- data/specification.txt +0 -21
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
activemodel (3.2.14)
|
5
|
+
activesupport (= 3.2.14)
|
6
|
+
builder (~> 3.0.0)
|
7
|
+
activerecord (3.2.14)
|
8
|
+
activemodel (= 3.2.14)
|
9
|
+
activesupport (= 3.2.14)
|
10
|
+
arel (~> 3.0.2)
|
11
|
+
tzinfo (~> 0.3.29)
|
4
12
|
activesupport (3.2.14)
|
5
13
|
i18n (~> 0.6, >= 0.6.4)
|
6
14
|
multi_json (~> 1.0)
|
7
15
|
addressable (2.3.5)
|
8
|
-
|
16
|
+
arel (3.0.2)
|
17
|
+
builder (3.0.4)
|
9
18
|
coderay (1.0.9)
|
10
|
-
database_cleaner (1.0.1)
|
11
19
|
diff-lcs (1.2.4)
|
12
|
-
faraday (0.8.
|
13
|
-
multipart-post (~> 1.
|
20
|
+
faraday (0.8.8)
|
21
|
+
multipart-post (~> 1.2.0)
|
14
22
|
git (1.2.5)
|
15
23
|
github_api (0.10.1)
|
16
24
|
addressable
|
@@ -65,25 +73,22 @@ GEM
|
|
65
73
|
rspec-expectations (2.14.0)
|
66
74
|
diff-lcs (>= 1.1.3, < 2.0)
|
67
75
|
rspec-mocks (2.14.1)
|
68
|
-
sequel (4.0.0)
|
69
76
|
simplecov (0.7.1)
|
70
77
|
multi_json (~> 1.0)
|
71
78
|
simplecov-html (~> 0.7.1)
|
72
79
|
simplecov-html (0.7.1)
|
73
|
-
slop (3.4.
|
74
|
-
|
80
|
+
slop (3.4.6)
|
81
|
+
tzinfo (0.3.37)
|
75
82
|
|
76
83
|
PLATFORMS
|
77
84
|
ruby
|
78
85
|
|
79
86
|
DEPENDENCIES
|
87
|
+
activerecord (~> 3.2)
|
80
88
|
activesupport (~> 3.2)
|
81
89
|
bundler
|
82
|
-
database_cleaner
|
83
90
|
jeweler
|
84
91
|
pry-nav
|
85
92
|
rdoc
|
86
93
|
rspec
|
87
|
-
sequel
|
88
94
|
simplecov
|
89
|
-
sqlite3
|
data/README.md
CHANGED
@@ -1,6 +1,88 @@
|
|
1
1
|
# Hyphy
|
2
2
|
|
3
|
-
Hyphy is a
|
3
|
+
Hyphy is a toolkit for identifying SQL bottlenecks in Ruby applications. Given
|
4
|
+
an adapter for an ORM and a Ruby block, Hyphy collects all executed queries in
|
5
|
+
a dataset. Afterward, it can filter the dataset and even benchmark the queries.
|
6
|
+
At [NationBuilder](http://nationbuilder.com/), we use this gem to conduct
|
7
|
+
performance regression tests.
|
8
|
+
|
9
|
+
## Supported ORMs
|
10
|
+
|
11
|
+
Hyphy only comes with out of the box support for ActiveRecord. Adding a new ORM
|
12
|
+
should be easy, however.
|
13
|
+
|
14
|
+
## Creating datasets
|
15
|
+
|
16
|
+
You can create a new dataset by initializing a `Sampler` object.
|
17
|
+
```ruby
|
18
|
+
require 'hyphy'
|
19
|
+
sampler = Hyphy::Sampler.new(:orm => :active_record)
|
20
|
+
```
|
21
|
+
|
22
|
+
## Sampling queries
|
23
|
+
|
24
|
+
Once we've created our `Sampler` object, it is very easy to start collecting data.
|
25
|
+
Simply enclose the application code that you need profiled in a block and pass it
|
26
|
+
to the `profile` method. Example:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
sampler.profile do
|
30
|
+
# Application code goes here
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
## Filtering queries
|
35
|
+
|
36
|
+
Hyphy comes with a few filters that fit common use cases. Here is how they can
|
37
|
+
be used:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Only keep SQL 'select' queries
|
41
|
+
sampler.apply_filter(Hyphy::Filters::SQLFilter, :type => :select)
|
42
|
+
|
43
|
+
# Only keep queries that had a running time r such that .01 < r < .05
|
44
|
+
sampler.apply_filter(Hyphy::Filters::DurationFilter,
|
45
|
+
:duration_min => 0.01,
|
46
|
+
:duration_max => 0.05)
|
47
|
+
|
48
|
+
# Keep the top 10 results
|
49
|
+
sampler.apply_filter(Hyphy::Filters::LimitFilter,
|
50
|
+
:limit => 10)
|
51
|
+
|
52
|
+
# Benchmark each query with 10 runs (this saves the benchmark information)
|
53
|
+
sampler.apply_filter(Hyphy::Filters::BenchmarkFilter, :runs => 10)
|
54
|
+
|
55
|
+
# Using the benchmark information, keep queries that fit the
|
56
|
+
# duration requirements.
|
57
|
+
sampler.apply_filter(Hyphy::Filters::DurationFilter,
|
58
|
+
:benchmark => true,
|
59
|
+
:duration_min => 0.01,
|
60
|
+
:duration_max => 0.05))
|
61
|
+
```
|
62
|
+
|
63
|
+
## Accessing data
|
64
|
+
|
65
|
+
To form reports using the data that remains after filtering, we
|
66
|
+
use the `SQLStatement` objects in `sampler.dataset`.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
sql_statement = sampler.dataset.first
|
70
|
+
|
71
|
+
sql_statement.statement
|
72
|
+
"Select * from users"
|
73
|
+
|
74
|
+
sql_statement.duration
|
75
|
+
0.03
|
76
|
+
|
77
|
+
sql_statement.application_trace
|
78
|
+
["/src/application/app/models/model.rb:24:in `make_query'"]
|
79
|
+
|
80
|
+
sql_statement.benchmark_runs
|
81
|
+
10
|
82
|
+
|
83
|
+
sql_statement.benchmark_time
|
84
|
+
0.0324
|
85
|
+
```
|
4
86
|
|
5
87
|
## Contributing to Hyphy
|
6
88
|
|
data/Rakefile
CHANGED
@@ -16,8 +16,10 @@ Jeweler::Tasks.new do |gem|
|
|
16
16
|
gem.name = "hyphy"
|
17
17
|
gem.homepage = "http://github.com/3dna/hyphy"
|
18
18
|
gem.license = "MIT"
|
19
|
-
gem.summary = %Q{
|
20
|
-
gem.description = %Q{
|
19
|
+
gem.summary = %Q{A SQL bottleneck toolkit}
|
20
|
+
gem.description = %Q{Hyphy is a toolkit for identifying SQL bottlenecks in Ruby applications. Given
|
21
|
+
an adapter for an ORM and a Ruby block, Hyphy collects all executed queries in
|
22
|
+
a dataset. Afterward, it can filter the dataset and even benchmark the queries.}
|
21
23
|
gem.email = "david@nationbuilder.com"
|
22
24
|
gem.authors = ["David Huie"]
|
23
25
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
data/hyphy.gemspec
CHANGED
@@ -5,19 +5,18 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "hyphy"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Huie"]
|
12
|
-
s.date = "2013-07-
|
13
|
-
s.description = "
|
12
|
+
s.date = "2013-07-30"
|
13
|
+
s.description = "Hyphy is a toolkit for identifying SQL bottlenecks in Ruby applications. Given\nan adapter for an ORM and a Ruby block, Hyphy collects all executed queries in\na dataset. Afterward, it can filter the dataset and even benchmark the queries."
|
14
14
|
s.email = "david@nationbuilder.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE.txt",
|
17
17
|
"README.md"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
".document",
|
21
20
|
".rspec",
|
22
21
|
".ruby-gemset",
|
23
22
|
".ruby-version",
|
@@ -29,51 +28,47 @@ Gem::Specification.new do |s|
|
|
29
28
|
"VERSION",
|
30
29
|
"hyphy.gemspec",
|
31
30
|
"lib/hyphy.rb",
|
32
|
-
"lib/hyphy/database.rb",
|
33
|
-
"lib/hyphy/dataset.rb",
|
34
|
-
"lib/hyphy/dataset_collection.rb",
|
35
31
|
"lib/hyphy/filters/abstract_filter.rb",
|
32
|
+
"lib/hyphy/filters/benchmark_filter.rb",
|
36
33
|
"lib/hyphy/filters/duration_filter.rb",
|
37
|
-
"lib/hyphy/
|
34
|
+
"lib/hyphy/filters/limit_filter.rb",
|
35
|
+
"lib/hyphy/filters/sql_filter.rb",
|
38
36
|
"lib/hyphy/orm_adapters/abstract_orm_adapter.rb",
|
39
37
|
"lib/hyphy/orm_adapters/activerecord_adapter.rb",
|
40
38
|
"lib/hyphy/sampler.rb",
|
41
|
-
"
|
42
|
-
"spec/dataset_spec.rb",
|
39
|
+
"lib/hyphy/sql_statement.rb",
|
43
40
|
"spec/filters/abstract_filter_spec.rb",
|
41
|
+
"spec/filters/benchmark_filter_spec.rb",
|
44
42
|
"spec/filters/duration_filter_spec.rb",
|
43
|
+
"spec/filters/limit_filter_spec.rb",
|
44
|
+
"spec/filters/sql_filter_spec.rb",
|
45
45
|
"spec/models/sql_statement_spec.rb",
|
46
46
|
"spec/orm_adapters/activerecord_orm_adapter_spec.rb",
|
47
47
|
"spec/sampler_spec.rb",
|
48
|
-
"spec/spec_helper.rb"
|
49
|
-
"specification.txt"
|
48
|
+
"spec/spec_helper.rb"
|
50
49
|
]
|
51
50
|
s.homepage = "http://github.com/3dna/hyphy"
|
52
51
|
s.licenses = ["MIT"]
|
53
52
|
s.require_paths = ["lib"]
|
54
53
|
s.rubygems_version = "1.8.25"
|
55
|
-
s.summary = "
|
54
|
+
s.summary = "A SQL bottleneck toolkit"
|
56
55
|
|
57
56
|
if s.respond_to? :specification_version then
|
58
57
|
s.specification_version = 3
|
59
58
|
|
60
59
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
61
|
-
s.add_runtime_dependency(%q<sequel>, [">= 0"])
|
62
|
-
s.add_runtime_dependency(%q<sqlite3>, [">= 0"])
|
63
60
|
s.add_runtime_dependency(%q<activesupport>, ["~> 3.2"])
|
61
|
+
s.add_runtime_dependency(%q<activerecord>, ["~> 3.2"])
|
64
62
|
s.add_development_dependency(%q<bundler>, [">= 0"])
|
65
|
-
s.add_development_dependency(%q<database_cleaner>, [">= 0"])
|
66
63
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
67
64
|
s.add_development_dependency(%q<pry-nav>, [">= 0"])
|
68
65
|
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
69
66
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
70
67
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
71
68
|
else
|
72
|
-
s.add_dependency(%q<sequel>, [">= 0"])
|
73
|
-
s.add_dependency(%q<sqlite3>, [">= 0"])
|
74
69
|
s.add_dependency(%q<activesupport>, ["~> 3.2"])
|
70
|
+
s.add_dependency(%q<activerecord>, ["~> 3.2"])
|
75
71
|
s.add_dependency(%q<bundler>, [">= 0"])
|
76
|
-
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
77
72
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
78
73
|
s.add_dependency(%q<pry-nav>, [">= 0"])
|
79
74
|
s.add_dependency(%q<rdoc>, [">= 0"])
|
@@ -81,11 +76,9 @@ Gem::Specification.new do |s|
|
|
81
76
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
82
77
|
end
|
83
78
|
else
|
84
|
-
s.add_dependency(%q<sequel>, [">= 0"])
|
85
|
-
s.add_dependency(%q<sqlite3>, [">= 0"])
|
86
79
|
s.add_dependency(%q<activesupport>, ["~> 3.2"])
|
80
|
+
s.add_dependency(%q<activerecord>, ["~> 3.2"])
|
87
81
|
s.add_dependency(%q<bundler>, [">= 0"])
|
88
|
-
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
89
82
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
90
83
|
s.add_dependency(%q<pry-nav>, [">= 0"])
|
91
84
|
s.add_dependency(%q<rdoc>, [">= 0"])
|
@@ -1,13 +1,13 @@
|
|
1
|
-
class Hyphy::AbstractFilter
|
1
|
+
class Hyphy::Filters::AbstractFilter
|
2
2
|
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :data
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
5
|
+
def initialize(data, opts={})
|
6
|
+
@data = data
|
7
7
|
end
|
8
8
|
|
9
9
|
def filter
|
10
|
-
@
|
10
|
+
@data
|
11
11
|
end
|
12
12
|
|
13
13
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Hyphy::Filters::BenchmarkFilter < Hyphy::Filters::AbstractFilter
|
2
|
+
|
3
|
+
def initialize(data, opts)
|
4
|
+
@runs = opts[:runs] || 1
|
5
|
+
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def filter
|
10
|
+
@data.each do |sql_statement|
|
11
|
+
timing = self.class.benchmark(sql_statement, @runs)
|
12
|
+
|
13
|
+
sql_statement.benchmark_runs = @runs
|
14
|
+
sql_statement.benchmark_time = timing
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.benchmark(sql_statement, runs)
|
19
|
+
times = []
|
20
|
+
|
21
|
+
(1..runs).each do
|
22
|
+
times << sql_statement.orm_adapter.time_statement(sql_statement)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Calculate the average
|
26
|
+
average = times.reduce(:+) / times.count
|
27
|
+
average.to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -1,20 +1,29 @@
|
|
1
|
-
class Hyphy::DurationFilter < Hyphy::AbstractFilter
|
1
|
+
class Hyphy::Filters::DurationFilter < Hyphy::Filters::AbstractFilter
|
2
2
|
|
3
3
|
attr_reader :duration_min, :duration_max
|
4
4
|
|
5
|
-
def initialize(
|
5
|
+
def initialize(data, opts)
|
6
6
|
@duration_min = opts[:duration_min] || 0.0
|
7
7
|
@duration_max = opts[:duration_max] || Float::INFINITY
|
8
|
+
@benchmark = opts[:benchmark] || false
|
9
|
+
|
10
|
+
if @benchmark
|
11
|
+
@duration = lambda { |sql_statement| sql_statement.benchmark_time }
|
12
|
+
else
|
13
|
+
@duration = lambda { |sql_statement| sql_statement.duration }
|
14
|
+
end
|
8
15
|
|
9
16
|
super
|
10
17
|
end
|
11
18
|
|
12
19
|
def filter
|
13
|
-
@
|
14
|
-
|
20
|
+
@data.select! do |sql_statement|
|
21
|
+
next unless @duration.call(sql_statement)
|
22
|
+
|
23
|
+
(@duration_min <= @duration.call(sql_statement)) and (@duration.call(sql_statement) <= @duration_max)
|
15
24
|
end
|
16
25
|
|
17
|
-
@
|
26
|
+
@data.sort_by! { |sql_statement| -sql_statement.duration }
|
18
27
|
end
|
19
28
|
|
20
29
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Hyphy::Filters::SQLFilter < Hyphy::Filters::AbstractFilter
|
2
|
+
|
3
|
+
class IncorrectSQLTypeException < Exception; end
|
4
|
+
|
5
|
+
def initialize(data, opts={})
|
6
|
+
@type = opts[:type] || :select
|
7
|
+
|
8
|
+
unless [:select, :insert].include?(@type)
|
9
|
+
raise IncorrectSQLTypeException, "incorrect type: #{@type}"
|
10
|
+
end
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def filter
|
16
|
+
@data.select! do |sql_statement|
|
17
|
+
sql_statement.send("#{@type}?".to_sym)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -1,15 +1,33 @@
|
|
1
|
+
require 'active_record'
|
1
2
|
require 'active_support/notifications'
|
3
|
+
require 'benchmark'
|
2
4
|
|
3
5
|
class Hyphy::ActiveRecordAdapter < Hyphy::AbstractORMAdapter
|
4
6
|
|
5
7
|
def self.subscribe_to_sql_notifications(callback)
|
6
8
|
ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
|
7
|
-
|
9
|
+
sql = args[4][:sql]
|
10
|
+
binds = args[4][:binds]
|
8
11
|
start_time = args[1]
|
9
12
|
end_time = args[2]
|
10
13
|
|
11
|
-
callback.call(
|
14
|
+
sql_statement = callback.call(sql, start_time, end_time)
|
15
|
+
sql_statement.binds = binds
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
19
|
+
def self.unsubscribe_to_sql_notifications(subscriber)
|
20
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.time_statement(sql_statement)
|
24
|
+
ActiveRecord::Base.connection.clear_query_cache
|
25
|
+
|
26
|
+
binds = sql_statement.binds
|
27
|
+
Benchmark.realtime { ActiveRecord::Base.connection.send(:exec_query,
|
28
|
+
sql_statement.statement,
|
29
|
+
'SQL',
|
30
|
+
binds) }
|
31
|
+
end
|
32
|
+
|
15
33
|
end
|
data/lib/hyphy/sampler.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class Hyphy::Sampler
|
2
2
|
|
3
|
+
attr_accessor :dataset
|
3
4
|
attr_reader :orm_adapter, :metadata_callbacks
|
4
5
|
|
5
6
|
class UnsupportedORMException < Exception; end
|
@@ -13,25 +14,30 @@ class Hyphy::Sampler
|
|
13
14
|
raise UnsupportedORMException, 'ORM #{orm} is not supported'
|
14
15
|
end
|
15
16
|
|
17
|
+
@dataset = []
|
16
18
|
@metadata_callbacks = {}
|
17
19
|
end
|
18
20
|
|
19
|
-
def log_sql(statement, start_time, end_time)
|
20
|
-
Hyphy::SQLStatement.
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def log_sql(statement, start_time, end_time, orm_adapter)
|
22
|
+
sql_statement = Hyphy::SQLStatement.new(:statement => statement,
|
23
|
+
:start_time => start_time,
|
24
|
+
:end_time => end_time,
|
25
|
+
:orm_adapter => orm_adapter,
|
26
|
+
:trace => caller)
|
27
|
+
@dataset << sql_statement
|
28
|
+
sql_statement
|
24
29
|
end
|
25
30
|
|
26
31
|
def process_metadata(sql_statement)
|
27
32
|
@metadata_callbacks.each do |key, value_block|
|
28
|
-
sql_statement.
|
33
|
+
sql_statement.metadata[key] = value_block.call
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
37
|
def sample(statement, start_time, end_time)
|
33
|
-
sql_statement = log_sql(statement, start_time, end_time)
|
38
|
+
sql_statement = log_sql(statement, start_time, end_time, @orm_adapter)
|
34
39
|
process_metadata(sql_statement)
|
40
|
+
sql_statement
|
35
41
|
end
|
36
42
|
|
37
43
|
def add_metadata(name, &block)
|
@@ -39,7 +45,22 @@ class Hyphy::Sampler
|
|
39
45
|
end
|
40
46
|
|
41
47
|
def begin
|
42
|
-
@orm_adapter.subscribe_to_sql_notifications(method(:sample))
|
48
|
+
@subscriber = @orm_adapter.subscribe_to_sql_notifications(method(:sample))
|
49
|
+
end
|
50
|
+
|
51
|
+
def stop
|
52
|
+
@orm_adapter.unsubscribe_to_sql_notifications(@subscriber)
|
53
|
+
end
|
54
|
+
|
55
|
+
def profile
|
56
|
+
self.begin
|
57
|
+
yield
|
58
|
+
self.stop
|
59
|
+
end
|
60
|
+
|
61
|
+
def apply_filter(filter_class, opts={})
|
62
|
+
filter = filter_class.new(@dataset, opts)
|
63
|
+
filter.filter
|
43
64
|
end
|
44
65
|
|
45
66
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Hyphy::SQLStatement
|
2
|
+
|
3
|
+
DIGIT_MARKER = '<digit>'
|
4
|
+
|
5
|
+
attr_accessor(:statement,
|
6
|
+
:trace,
|
7
|
+
:metadata,
|
8
|
+
:binds,
|
9
|
+
:orm_adapter,
|
10
|
+
:start_time,
|
11
|
+
:end_time,
|
12
|
+
:benchmark_runs,
|
13
|
+
:benchmark_time)
|
14
|
+
|
15
|
+
def initialize(opts={})
|
16
|
+
@statement = opts[:statement]
|
17
|
+
@start_time = opts[:start_time]
|
18
|
+
@end_time = opts[:end_time]
|
19
|
+
@orm_adapter = opts[:orm_adapter]
|
20
|
+
@trace = opts[:trace] || []
|
21
|
+
@binds = opts[:binds] || []
|
22
|
+
@metadata = opts[:metadata] || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def duration
|
26
|
+
@duration ||= (end_time - start_time)
|
27
|
+
end
|
28
|
+
|
29
|
+
def stripped_statement
|
30
|
+
@stripped_statement ||= statement.strip
|
31
|
+
end
|
32
|
+
|
33
|
+
def select?
|
34
|
+
@select ||= stripped_statement.upcase.match(/^SELECT/)
|
35
|
+
end
|
36
|
+
|
37
|
+
def insert?
|
38
|
+
@insert ||= stripped_statement.upcase.match(/^INSERT/)
|
39
|
+
end
|
40
|
+
|
41
|
+
def digitless
|
42
|
+
@digitless ||= stripped_statement.gsub(/\d+/, DIGIT_MARKER)
|
43
|
+
end
|
44
|
+
|
45
|
+
def application_trace
|
46
|
+
return @application_trace if @application_trace
|
47
|
+
|
48
|
+
regex = Regexp.new("^#{Dir.pwd}")
|
49
|
+
@application_trace ||= trace.select { |line| regex.match(line) }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/hyphy.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Hyphy; end
|
2
|
+
module Hyphy::Filters; end
|
2
3
|
|
3
|
-
require 'hyphy/
|
4
|
-
require 'hyphy/models/sql_statement'
|
4
|
+
require 'hyphy/sql_statement'
|
5
5
|
require 'hyphy/orm_adapters/abstract_orm_adapter'
|
6
6
|
require 'hyphy/orm_adapters/activerecord_adapter'
|
7
7
|
require 'hyphy/sampler'
|
8
8
|
require 'hyphy/filters/abstract_filter'
|
9
9
|
require 'hyphy/filters/duration_filter'
|
10
|
-
require 'hyphy/
|
11
|
-
require 'hyphy/
|
10
|
+
require 'hyphy/filters/benchmark_filter'
|
11
|
+
require 'hyphy/filters/limit_filter'
|
12
|
+
require 'hyphy/filters/sql_filter'
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Hyphy::AbstractFilter do
|
3
|
+
describe Hyphy::Filters::AbstractFilter do
|
4
4
|
|
5
|
-
it 'stores
|
6
|
-
|
7
|
-
filter = Hyphy::AbstractFilter.new(
|
5
|
+
it 'stores data' do
|
6
|
+
data = [1, 2]
|
7
|
+
filter = Hyphy::Filters::AbstractFilter.new(data)
|
8
8
|
|
9
|
-
filter.
|
10
|
-
filter.filter.should ==
|
9
|
+
filter.data.should == data
|
10
|
+
filter.filter.should == data
|
11
11
|
end
|
12
12
|
|
13
13
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hyphy::Filters::BenchmarkFilter do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@sql_statement = Hyphy::SQLStatement.new(:statement => 'select * from table',
|
7
|
+
:orm_adapter => Hyphy::AbstractORMAdapter)
|
8
|
+
@sql_statement_times = [1.0, 2.0, 3.0]
|
9
|
+
@runs = @sql_statement_times.count
|
10
|
+
|
11
|
+
Hyphy::AbstractORMAdapter.stub(:time_statement).and_return(*@sql_statement_times)
|
12
|
+
|
13
|
+
@data = [@sql_statement]
|
14
|
+
@filter = Hyphy::Filters::BenchmarkFilter.new(@data, :runs => @runs)
|
15
|
+
@average_time = @sql_statement_times.reduce(:+) / @runs
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#filter" do
|
19
|
+
|
20
|
+
it "modifies the data to include benchmark data" do
|
21
|
+
@filter.filter
|
22
|
+
|
23
|
+
@data[0].benchmark_runs.should == @runs
|
24
|
+
@data[0].benchmark_time.should == @average_time
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe ".benchmark" do
|
30
|
+
|
31
|
+
it "it returns an average of the different run times of a statement" do
|
32
|
+
average_time = Hyphy::Filters::BenchmarkFilter.benchmark(@sql_statement, @runs)
|
33
|
+
average_time.should == @average_time
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|