hyphy 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ hyphy
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "sequel"
4
+ gem "sqlite3"
5
+ gem "activesupport", "~> 3.2"
6
+
7
+ group :development do
8
+ gem "bundler"
9
+ gem "database_cleaner"
10
+ gem "jeweler"
11
+ gem "pry-nav"
12
+ gem "rdoc"
13
+ gem "rspec"
14
+ gem "simplecov"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,89 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.14)
5
+ i18n (~> 0.6, >= 0.6.4)
6
+ multi_json (~> 1.0)
7
+ addressable (2.3.5)
8
+ builder (3.2.2)
9
+ coderay (1.0.9)
10
+ database_cleaner (1.0.1)
11
+ diff-lcs (1.2.4)
12
+ faraday (0.8.7)
13
+ multipart-post (~> 1.1)
14
+ git (1.2.5)
15
+ github_api (0.10.1)
16
+ addressable
17
+ faraday (~> 0.8.1)
18
+ hashie (>= 1.2)
19
+ multi_json (~> 1.4)
20
+ nokogiri (~> 1.5.2)
21
+ oauth2
22
+ hashie (2.0.5)
23
+ highline (1.6.19)
24
+ httpauth (0.2.0)
25
+ i18n (0.6.4)
26
+ jeweler (1.8.6)
27
+ builder
28
+ bundler (~> 1.0)
29
+ git (>= 1.2.5)
30
+ github_api (= 0.10.1)
31
+ highline (>= 1.6.15)
32
+ nokogiri (= 1.5.10)
33
+ rake
34
+ rdoc
35
+ json (1.8.0)
36
+ jwt (0.1.8)
37
+ multi_json (>= 1.5)
38
+ method_source (0.8.1)
39
+ multi_json (1.7.7)
40
+ multi_xml (0.5.4)
41
+ multipart-post (1.2.0)
42
+ nokogiri (1.5.10)
43
+ oauth2 (0.9.2)
44
+ faraday (~> 0.8)
45
+ httpauth (~> 0.2)
46
+ jwt (~> 0.1.4)
47
+ multi_json (~> 1.0)
48
+ multi_xml (~> 0.5)
49
+ rack (~> 1.2)
50
+ pry (0.9.12.2)
51
+ coderay (~> 1.0.5)
52
+ method_source (~> 0.8)
53
+ slop (~> 3.4)
54
+ pry-nav (0.2.3)
55
+ pry (~> 0.9.10)
56
+ rack (1.5.2)
57
+ rake (10.1.0)
58
+ rdoc (4.0.1)
59
+ json (~> 1.4)
60
+ rspec (2.14.1)
61
+ rspec-core (~> 2.14.0)
62
+ rspec-expectations (~> 2.14.0)
63
+ rspec-mocks (~> 2.14.0)
64
+ rspec-core (2.14.4)
65
+ rspec-expectations (2.14.0)
66
+ diff-lcs (>= 1.1.3, < 2.0)
67
+ rspec-mocks (2.14.1)
68
+ sequel (4.0.0)
69
+ simplecov (0.7.1)
70
+ multi_json (~> 1.0)
71
+ simplecov-html (~> 0.7.1)
72
+ simplecov-html (0.7.1)
73
+ slop (3.4.5)
74
+ sqlite3 (1.3.7)
75
+
76
+ PLATFORMS
77
+ ruby
78
+
79
+ DEPENDENCIES
80
+ activesupport (~> 3.2)
81
+ bundler
82
+ database_cleaner
83
+ jeweler
84
+ pry-nav
85
+ rdoc
86
+ rspec
87
+ sequel
88
+ simplecov
89
+ sqlite3
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 David Huie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # Hyphy
2
+
3
+ Hyphy is a framework for identifying SQL bottlenecks in tests.
4
+
5
+ ## Contributing to Hyphy
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
+ * Fork the project.
10
+ * Start a feature/bugfix branch.
11
+ * Commit and push until you are happy with your contribution.
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Hyphy has *100% test coverage*; keep it that way.
14
+
15
+ ## Copyright
16
+
17
+ Copyright (c) 2013 David Huie. See LICENSE.txt for
18
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ gem.name = "hyphy"
17
+ gem.homepage = "http://github.com/3dna/hyphy"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{Identify SQL bottlenecks in tests}
20
+ gem.description = %Q{Identify SQL bottlenecks in tests}
21
+ gem.email = "david@nationbuilder.com"
22
+ gem.authors = ["David Huie"]
23
+ end
24
+ Jeweler::RubygemsDotOrgTasks.new
25
+
26
+ require 'rspec/core'
27
+ require 'rspec/core/rake_task'
28
+ RSpec::Core::RakeTask.new(:spec) do |spec|
29
+ spec.pattern = FileList['spec/**/*_spec.rb']
30
+ end
31
+
32
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :default => :spec
38
+
39
+ require 'rdoc/task'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "hyphy #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/hyphy.gemspec ADDED
@@ -0,0 +1,96 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "hyphy"
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["David Huie"]
12
+ s.date = "2013-07-24"
13
+ s.description = "Identify SQL bottlenecks in tests"
14
+ s.email = "david@nationbuilder.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ ".ruby-gemset",
23
+ ".ruby-version",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.md",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "hyphy.gemspec",
31
+ "lib/hyphy.rb",
32
+ "lib/hyphy/database.rb",
33
+ "lib/hyphy/dataset.rb",
34
+ "lib/hyphy/dataset_collection.rb",
35
+ "lib/hyphy/filters/abstract_filter.rb",
36
+ "lib/hyphy/filters/duration_filter.rb",
37
+ "lib/hyphy/models/sql_statement.rb",
38
+ "lib/hyphy/orm_adapters/abstract_orm_adapter.rb",
39
+ "lib/hyphy/orm_adapters/activerecord_adapter.rb",
40
+ "lib/hyphy/sampler.rb",
41
+ "spec/dataset_collection_spec.rb",
42
+ "spec/dataset_spec.rb",
43
+ "spec/filters/abstract_filter_spec.rb",
44
+ "spec/filters/duration_filter_spec.rb",
45
+ "spec/models/sql_statement_spec.rb",
46
+ "spec/orm_adapters/activerecord_orm_adapter_spec.rb",
47
+ "spec/sampler_spec.rb",
48
+ "spec/spec_helper.rb",
49
+ "specification.txt"
50
+ ]
51
+ s.homepage = "http://github.com/3dna/hyphy"
52
+ s.licenses = ["MIT"]
53
+ s.require_paths = ["lib"]
54
+ s.rubygems_version = "1.8.25"
55
+ s.summary = "Identify SQL bottlenecks in tests"
56
+
57
+ if s.respond_to? :specification_version then
58
+ s.specification_version = 3
59
+
60
+ 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
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.2"])
64
+ s.add_development_dependency(%q<bundler>, [">= 0"])
65
+ s.add_development_dependency(%q<database_cleaner>, [">= 0"])
66
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
67
+ s.add_development_dependency(%q<pry-nav>, [">= 0"])
68
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
69
+ s.add_development_dependency(%q<rspec>, [">= 0"])
70
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
71
+ else
72
+ s.add_dependency(%q<sequel>, [">= 0"])
73
+ s.add_dependency(%q<sqlite3>, [">= 0"])
74
+ s.add_dependency(%q<activesupport>, ["~> 3.2"])
75
+ s.add_dependency(%q<bundler>, [">= 0"])
76
+ s.add_dependency(%q<database_cleaner>, [">= 0"])
77
+ s.add_dependency(%q<jeweler>, [">= 0"])
78
+ s.add_dependency(%q<pry-nav>, [">= 0"])
79
+ s.add_dependency(%q<rdoc>, [">= 0"])
80
+ s.add_dependency(%q<rspec>, [">= 0"])
81
+ s.add_dependency(%q<simplecov>, [">= 0"])
82
+ end
83
+ else
84
+ s.add_dependency(%q<sequel>, [">= 0"])
85
+ s.add_dependency(%q<sqlite3>, [">= 0"])
86
+ s.add_dependency(%q<activesupport>, ["~> 3.2"])
87
+ s.add_dependency(%q<bundler>, [">= 0"])
88
+ s.add_dependency(%q<database_cleaner>, [">= 0"])
89
+ s.add_dependency(%q<jeweler>, [">= 0"])
90
+ s.add_dependency(%q<pry-nav>, [">= 0"])
91
+ s.add_dependency(%q<rdoc>, [">= 0"])
92
+ s.add_dependency(%q<rspec>, [">= 0"])
93
+ s.add_dependency(%q<simplecov>, [">= 0"])
94
+ end
95
+ end
96
+
@@ -0,0 +1,3 @@
1
+ require 'sequel'
2
+
3
+ Hyphy::DB = Sequel.sqlite
@@ -0,0 +1,14 @@
1
+ class Hyphy::Dataset
2
+
3
+ attr_reader :data
4
+
5
+ def initialize(data=nil)
6
+ @data = data || Hyphy::SQLStatement.all
7
+ end
8
+
9
+ def apply_filter(filter_class, opts={})
10
+ filter = filter_class.new(@data, opts)
11
+ filter.filter
12
+ end
13
+
14
+ end
@@ -0,0 +1,35 @@
1
+ class Hyphy::DatasetCollection
2
+
3
+ class InvalidKeyException < Exception; end
4
+
5
+ attr_reader :dataset_collection
6
+
7
+ def initialize(dataset, key)
8
+ raise InvalidKeyException unless Hyphy::SQLStatement.method_defined?(key)
9
+
10
+ @key = key
11
+ @dataset = dataset
12
+ @dataset_collection = {}
13
+ end
14
+
15
+ def process_dataset
16
+ collection = Hash.new { |hash, key| hash[key] = [] }
17
+
18
+ @dataset.data.each do |sql_statement|
19
+ collection[sql_statement.send(@key)] << sql_statement
20
+ end
21
+
22
+ collection.each { |key, value| @dataset_collection[key] = Hyphy::Dataset.new(value) }
23
+ end
24
+
25
+ def counts_hash
26
+ key_to_count = {}
27
+
28
+ @dataset_collection.map do |key, dataset|
29
+ key_to_count[key] = dataset.data.count
30
+ end
31
+
32
+ key_to_count
33
+ end
34
+
35
+ end
@@ -0,0 +1,13 @@
1
+ class Hyphy::AbstractFilter
2
+
3
+ attr_reader :dataset
4
+
5
+ def initialize(dataset, opts={})
6
+ @dataset = dataset
7
+ end
8
+
9
+ def filter
10
+ @dataset
11
+ end
12
+
13
+ end
@@ -0,0 +1,20 @@
1
+ class Hyphy::DurationFilter < Hyphy::AbstractFilter
2
+
3
+ attr_reader :duration_min, :duration_max
4
+
5
+ def initialize(dataset, opts)
6
+ @duration_min = opts[:duration_min] || 0.0
7
+ @duration_max = opts[:duration_max] || Float::INFINITY
8
+
9
+ super
10
+ end
11
+
12
+ def filter
13
+ @dataset.select! do |sql_statement|
14
+ (@duration_min <= sql_statement.duration) and (sql_statement.duration <= @duration_max)
15
+ end
16
+
17
+ @dataset.sort_by! { |sql_statement| -sql_statement.duration }
18
+ end
19
+
20
+ end
@@ -0,0 +1,49 @@
1
+ require 'json'
2
+
3
+ Hyphy::DB.create_table(:sql_statements) do
4
+ primary_key :id
5
+
6
+ String :statement, :text => true
7
+ String :trace_json, :text => true
8
+ String :metadata_json, :text => true
9
+ Float :start_time
10
+ Float :end_time
11
+ end
12
+
13
+ class Hyphy::SQLStatement < Sequel::Model
14
+
15
+ DIGIT_MARKER = '<digit>'
16
+
17
+ def duration
18
+ @duration ||= (end_time - start_time)
19
+ end
20
+
21
+ def stripped_statement
22
+ statement.strip
23
+ end
24
+
25
+ def digitless
26
+ without_digits = stripped_statement.gsub(/\d+/, DIGIT_MARKER)
27
+ end
28
+
29
+ def trace
30
+ JSON.parse(trace_json)
31
+ end
32
+
33
+ def metadata
34
+ return {} unless metadata_json
35
+ JSON.parse(metadata_json)
36
+ end
37
+
38
+ def add_metadata(key, value)
39
+ new_metadata = metadata
40
+ new_metadata[key] = value
41
+ self.metadata_json = JSON(new_metadata)
42
+ save
43
+ end
44
+
45
+ def self.truncate_table
46
+ Hyphy::DB[:sql_statements].truncate
47
+ end
48
+
49
+ end
@@ -0,0 +1,7 @@
1
+ require 'json'
2
+
3
+ class Hyphy::AbstractORMAdapter
4
+
5
+ def self.subscribe_to_sql_notifications(callback); end
6
+
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_support/notifications'
2
+
3
+ class Hyphy::ActiveRecordAdapter < Hyphy::AbstractORMAdapter
4
+
5
+ def self.subscribe_to_sql_notifications(callback)
6
+ ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
7
+ sql_statement = args[4][:sql]
8
+ start_time = args[1]
9
+ end_time = args[2]
10
+
11
+ callback.call(sql_statement, start_time, end_time)
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,45 @@
1
+ class Hyphy::Sampler
2
+
3
+ attr_reader :orm_adapter, :metadata_callbacks
4
+
5
+ class UnsupportedORMException < Exception; end
6
+
7
+ def initialize(opts={})
8
+ orm = opts[:orm] || :active_record
9
+
10
+ if orm == :active_record
11
+ @orm_adapter = Hyphy::ActiveRecordAdapter
12
+ else
13
+ raise UnsupportedORMException, 'ORM #{orm} is not supported'
14
+ end
15
+
16
+ @metadata_callbacks = {}
17
+ end
18
+
19
+ def log_sql(statement, start_time, end_time)
20
+ Hyphy::SQLStatement.create(:statement => statement,
21
+ :start_time => start_time,
22
+ :end_time => end_time,
23
+ :trace_json => JSON(caller))
24
+ end
25
+
26
+ def process_metadata(sql_statement)
27
+ @metadata_callbacks.each do |key, value_block|
28
+ sql_statement.add_metadata(key, value_block.call)
29
+ end
30
+ end
31
+
32
+ def sample(statement, start_time, end_time)
33
+ sql_statement = log_sql(statement, start_time, end_time)
34
+ process_metadata(sql_statement)
35
+ end
36
+
37
+ def add_metadata(name, &block)
38
+ @metadata_callbacks[name] = block
39
+ end
40
+
41
+ def begin
42
+ @orm_adapter.subscribe_to_sql_notifications(method(:sample))
43
+ end
44
+
45
+ end
data/lib/hyphy.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Hyphy; end
2
+
3
+ require 'hyphy/database'
4
+ require 'hyphy/models/sql_statement'
5
+ require 'hyphy/orm_adapters/abstract_orm_adapter'
6
+ require 'hyphy/orm_adapters/activerecord_adapter'
7
+ require 'hyphy/sampler'
8
+ require 'hyphy/filters/abstract_filter'
9
+ require 'hyphy/filters/duration_filter'
10
+ require 'hyphy/dataset'
11
+ require 'hyphy/dataset_collection'
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hyphy::DatasetCollection do
4
+
5
+ before(:each) do
6
+ Hyphy::SQLStatement.create :statement => 'select * from table where id = 4'
7
+ Hyphy::SQLStatement.create :statement => 'select * from table where id = 7'
8
+ Hyphy::SQLStatement.create :statement => 'select * from table'
9
+ end
10
+
11
+ let(:dataset) { Hyphy::Dataset.new }
12
+ let(:dataset_collection) { Hyphy::DatasetCollection.new(dataset, :statement) }
13
+
14
+ describe "#process_dataset" do
15
+
16
+ before(:each) { dataset_collection.process_dataset }
17
+
18
+ it "creates a collection with each statement having a unique key" do
19
+ dataset_collection.dataset_collection.keys.uniq.count.should == 3
20
+ dataset_collection.dataset_collection.values.uniq.count.should == 3
21
+ end
22
+
23
+ end
24
+
25
+ describe "#counts_hash" do
26
+
27
+ before(:each) { dataset_collection.process_dataset }
28
+
29
+ it "maps the correct statements to counts" do
30
+ puts dataset_collection.counts_hash.should ==
31
+ { "select * from table where id = 4" => 1,
32
+ "select * from table where id = 7" => 1,
33
+ "select * from table" => 1 }
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hyphy::Dataset do
4
+
5
+ let!(:sql_statement1) { Hyphy::SQLStatement.create(:statement => "select * from table1",
6
+ :start_time => 1.001,
7
+ :end_time => 2.002) }
8
+
9
+ let!(:sql_statement2) { Hyphy::SQLStatement.create(:statement => "select * from table2",
10
+ :start_time => 1.001,
11
+ :end_time => 3.002) }
12
+
13
+ let(:dataset) { Hyphy::Dataset.new }
14
+
15
+ describe "#get_data" do
16
+
17
+ it "stores all sql statements in the data attribute" do
18
+ dataset.data.should == [sql_statement1, sql_statement2]
19
+ end
20
+
21
+ end
22
+
23
+ describe "#apply_filter" do
24
+
25
+ it "filters the in memory dataset" do
26
+ dataset.apply_filter(Hyphy::DurationFilter, :duration_min => 1.5)
27
+ dataset.data.should == [sql_statement2]
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hyphy::AbstractFilter do
4
+
5
+ it 'stores a dataset' do
6
+ dataset = [1, 2]
7
+ filter = Hyphy::AbstractFilter.new(dataset)
8
+
9
+ filter.dataset.should == dataset
10
+ filter.filter.should == dataset
11
+ end
12
+
13
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hyphy::DurationFilter do
4
+
5
+ let!(:sql_statement1) { Hyphy::SQLStatement.create(:statement => 'select count(*) from table1',
6
+ :start_time => 1.0,
7
+ :end_time => 1.1) }
8
+
9
+ let!(:sql_statement2) { Hyphy::SQLStatement.create(:statement => 'select count(*) from table2',
10
+ :start_time => 2.0,
11
+ :end_time => 2.1) }
12
+
13
+ let!(:sql_statement3) { Hyphy::SQLStatement.create(:statement => 'select count(*) from table3',
14
+ :start_time => 2.0,
15
+ :end_time => 3.0) }
16
+
17
+ let!(:sql_statement4) { Hyphy::SQLStatement.create(:statement => 'select count(*) from table4',
18
+ :start_time => 3.0,
19
+ :end_time => 10.0) }
20
+
21
+ let(:dataset) { Hyphy::Dataset.new }
22
+
23
+ describe "#filter" do
24
+
25
+ it 'only returns the sql statements that last longer than one second' do
26
+ dataset.apply_filter(Hyphy::DurationFilter, :duration_min => 1.0)
27
+
28
+ dataset.data.should == [sql_statement4, sql_statement3]
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,81 @@
1
+ require 'json'
2
+
3
+ require "spec_helper"
4
+
5
+ describe Hyphy::SQLStatement do
6
+
7
+ let(:statement) { ' select * from table ' }
8
+ let(:sql_statement) { Hyphy::SQLStatement.new(:statement => statement,
9
+ :start_time => 2,
10
+ :end_time => 3.001,
11
+ :trace_json => JSON(["hello!"]),
12
+ :metadata_json => JSON({ "hello!" => "hi!" })) }
13
+
14
+ describe "#duration" do
15
+
16
+ it "returns the duration of the statement" do
17
+ sql_statement.duration.should == 1.001
18
+ end
19
+
20
+ end
21
+
22
+ describe "#stripped_description" do
23
+
24
+ it 'strips leading and trailing whitespace' do
25
+ sql_statement.stripped_statement.should == 'select * from table'
26
+ end
27
+
28
+ end
29
+
30
+ describe "#digitless" do
31
+
32
+ let(:statement) { 'select * from table where id = 44444' }
33
+ let(:sql_statement) { Hyphy::SQLStatement.new(:statement => statement,
34
+ :start_time => 2,
35
+ :end_time => 3.001) }
36
+
37
+ it 'replaces numbers with a marker' do
38
+ sql_statement.digitless.should == 'select * from table where id = <digit>'
39
+ end
40
+
41
+ end
42
+
43
+ describe "#trace" do
44
+
45
+ it 'should return the decoded json stored in the trace_json attribute' do
46
+ sql_statement.trace.should == ["hello!"]
47
+ end
48
+
49
+ end
50
+
51
+ describe ".truncate_table" do
52
+
53
+ it 'should clear all sql_statements rows' do
54
+ Hyphy::SQLStatement.create
55
+ Hyphy::SQLStatement.all.count.should == 1
56
+
57
+ Hyphy::SQLStatement.truncate_table
58
+ Hyphy::SQLStatement.all.count.should == 0
59
+ end
60
+
61
+ end
62
+
63
+ describe "#metadata" do
64
+
65
+ it 'should return the decoded json from the metadata_json attribute' do
66
+ sql_statement.metadata.should == { "hello!" => "hi!" }
67
+ end
68
+
69
+ end
70
+
71
+ describe "#add_metadata" do
72
+
73
+ it "should store the key, value pair in the metadata" do
74
+ sql_statement.add_metadata("key", "value")
75
+
76
+ sql_statement.metadata["key"].should == "value"
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe Hyphy::ActiveRecordAdapter do
4
+
5
+ let(:sql_statement) { 'select count(*) from victims' }
6
+ let(:start_time) { 1.0001 }
7
+ let(:end_time) { 1.0002 }
8
+
9
+ describe ".subscribe_to_sql_notifications" do
10
+
11
+ it "should catch sql.active_record notifications" do
12
+ ActiveSupport::Notifications.should_receive(:subscribe)
13
+ .and_yield(nil, start_time, end_time, nil, { :sql => sql_statement })
14
+
15
+ callback = lambda { |a, b, c| }
16
+
17
+ Hyphy::ActiveRecordAdapter.subscribe_to_sql_notifications(callback)
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hyphy::Sampler do
4
+
5
+ let(:sampler) { Hyphy::Sampler.new }
6
+ let(:statement) { 'select * from heroes' }
7
+ let(:start_time) { 1.0001 }
8
+ let(:end_time) { 1.0002 }
9
+
10
+ describe "#initialize" do
11
+
12
+ it "throws an exception for unsupported ORMs" do
13
+ expect{ Hyphy::Sampler.new :orm => :datamapper }
14
+ .to raise_error(Hyphy::Sampler::UnsupportedORMException)
15
+ end
16
+
17
+ end
18
+
19
+ describe "#begin" do
20
+
21
+ it "subscribes to SQL notifications" do
22
+ Hyphy::ActiveRecordAdapter.should_receive(:subscribe_to_sql_notifications)
23
+ sampler.begin
24
+ end
25
+
26
+ end
27
+
28
+ describe "#log_sql" do
29
+
30
+ it 'creates a new SQLStatement row' do
31
+ sampler.log_sql(statement,
32
+ start_time,
33
+ end_time)
34
+
35
+ sql_statement = Hyphy::SQLStatement.last
36
+ sql_statement.statement.should == statement
37
+ sql_statement.start_time.should == start_time
38
+ sql_statement.end_time.should == end_time
39
+ sql_statement.trace.class.should == Array
40
+ end
41
+
42
+ end
43
+
44
+ describe "#sample" do
45
+
46
+ it "logs the SQL statement and adds metadata" do
47
+ sampler.should_receive(:log_sql).with(statement, start_time, end_time)
48
+ sampler.should_receive(:process_metadata)
49
+
50
+ sampler.sample(statement, start_time, end_time)
51
+ end
52
+
53
+ end
54
+
55
+ describe "#add_metadata" do
56
+
57
+ it "stores a block that'll be used for adding metadata to a SQLStatement" do
58
+ sampler.add_metadata("test") { "this is just a test!" }
59
+
60
+ sampler.metadata_callbacks["test"].call.should == "this is just a test!"
61
+ end
62
+
63
+ end
64
+
65
+ describe "#process_metadata" do
66
+
67
+ let(:sql_statement) { Hyphy::SQLStatement.create }
68
+
69
+ it "adds metadata from a proc to a SQLStatement" do
70
+ sampler.add_metadata("test") { "this is just a test!" }
71
+ sampler.process_metadata(sql_statement)
72
+
73
+ sql_statement.metadata.should == { "test" => "this is just a test!" }
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,31 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'database_cleaner'
7
+ require 'pry-nav'
8
+ require 'rspec'
9
+ require 'hyphy'
10
+
11
+ # Requires supporting files with custom matchers and macros, etc,
12
+ # in ./support/ and its subdirectories.
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+
17
+ config.before(:suite) do
18
+ DatabaseCleaner.strategy = :transaction
19
+ end
20
+
21
+ config.before(:each) do
22
+ DatabaseCleaner.start
23
+ end
24
+
25
+ config.after(:each) do
26
+ DatabaseCleaner.clean
27
+ end
28
+
29
+ end
30
+
31
+ SimpleCov.start
data/specification.txt ADDED
@@ -0,0 +1,21 @@
1
+ Swaggie -- A Rails performance testing framework
2
+
3
+ Requirements:
4
+ - Samples all SQL calls
5
+ - Logs length of time
6
+ - Can classify "similar" SQL statements, and nest classifications
7
+ - Provide good interface so analysis can be made
8
+
9
+ Implementation:
10
+ - Use SQLite3
11
+ - Use sequel
12
+
13
+ Todo:
14
+ - Create reports
15
+ - Save metadata from each filter
16
+ - Create way to clear database
17
+
18
+ Filters
19
+ - Nation
20
+ - Query time
21
+ - Query base
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyphy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Huie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sequel
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sqlite3
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: database_cleaner
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: jeweler
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry-nav
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rdoc
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rspec
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: simplecov
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ description: Identify SQL bottlenecks in tests
175
+ email: david@nationbuilder.com
176
+ executables: []
177
+ extensions: []
178
+ extra_rdoc_files:
179
+ - LICENSE.txt
180
+ - README.md
181
+ files:
182
+ - .document
183
+ - .rspec
184
+ - .ruby-gemset
185
+ - .ruby-version
186
+ - Gemfile
187
+ - Gemfile.lock
188
+ - LICENSE.txt
189
+ - README.md
190
+ - Rakefile
191
+ - VERSION
192
+ - hyphy.gemspec
193
+ - lib/hyphy.rb
194
+ - lib/hyphy/database.rb
195
+ - lib/hyphy/dataset.rb
196
+ - lib/hyphy/dataset_collection.rb
197
+ - lib/hyphy/filters/abstract_filter.rb
198
+ - lib/hyphy/filters/duration_filter.rb
199
+ - lib/hyphy/models/sql_statement.rb
200
+ - lib/hyphy/orm_adapters/abstract_orm_adapter.rb
201
+ - lib/hyphy/orm_adapters/activerecord_adapter.rb
202
+ - lib/hyphy/sampler.rb
203
+ - spec/dataset_collection_spec.rb
204
+ - spec/dataset_spec.rb
205
+ - spec/filters/abstract_filter_spec.rb
206
+ - spec/filters/duration_filter_spec.rb
207
+ - spec/models/sql_statement_spec.rb
208
+ - spec/orm_adapters/activerecord_orm_adapter_spec.rb
209
+ - spec/sampler_spec.rb
210
+ - spec/spec_helper.rb
211
+ - specification.txt
212
+ homepage: http://github.com/3dna/hyphy
213
+ licenses:
214
+ - MIT
215
+ post_install_message:
216
+ rdoc_options: []
217
+ require_paths:
218
+ - lib
219
+ required_ruby_version: !ruby/object:Gem::Requirement
220
+ none: false
221
+ requirements:
222
+ - - ! '>='
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ segments:
226
+ - 0
227
+ hash: -1377831420524407634
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
229
+ none: false
230
+ requirements:
231
+ - - ! '>='
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ requirements: []
235
+ rubyforge_project:
236
+ rubygems_version: 1.8.25
237
+ signing_key:
238
+ specification_version: 3
239
+ summary: Identify SQL bottlenecks in tests
240
+ test_files: []