hyphy 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|