bullet 2.0.1 → 2.1.0
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/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rvmrc +2 -0
- data/.rvmrc.example +2 -0
- data/.watchr +24 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +105 -0
- data/Hacking.textile +69 -0
- data/README.textile +37 -55
- data/README_for_rails2.textile +35 -52
- data/Rakefile +45 -0
- data/bullet.gemspec +24 -0
- data/lib/bullet.rb +5 -5
- data/lib/bullet/action_controller2.rb +5 -5
- data/lib/bullet/active_record2.rb +2 -1
- data/lib/bullet/active_record3.rb +2 -1
- data/lib/bullet/active_record31.rb +96 -0
- data/lib/bullet/detector/association.rb +18 -9
- data/lib/bullet/detector/base.rb +0 -7
- data/lib/bullet/detector/unused_eager_association.rb +7 -5
- data/lib/bullet/notification/base.rb +6 -2
- data/lib/bullet/rack.rb +1 -1
- data/lib/bullet/registry/association.rb +1 -2
- data/lib/bullet/registry/base.rb +6 -8
- data/lib/bullet/version.rb +1 -1
- data/perf/benchmark.rb +106 -0
- data/rails/init.rb +1 -0
- data/spec/bullet/association_for_chris_spec.rb +96 -0
- data/spec/bullet/association_for_peschkaj_spec.rb +86 -0
- data/spec/bullet/association_spec.rb +889 -0
- data/spec/bullet/counter_spec.rb +136 -0
- data/spec/spec_helper.rb +79 -0
- data/tasks/bullet_tasks.rake +9 -0
- metadata +59 -62
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require "rake"
|
6
|
+
require "rdoc/task"
|
7
|
+
require "rspec"
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
|
10
|
+
require "bullet/version"
|
11
|
+
|
12
|
+
task :build do
|
13
|
+
system "gem build bullet.gemspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
task :install => :build do
|
17
|
+
system "sudo gem install bullet-#{Bullet::VERSION}.gem"
|
18
|
+
end
|
19
|
+
|
20
|
+
task :release => :build do
|
21
|
+
puts "Tagging #{Bullet::VERSION}..."
|
22
|
+
system "git tag -a #{Bullet::VERSION} -m 'Tagging #{Bullet::VERSION}'"
|
23
|
+
puts "Pushing to Github..."
|
24
|
+
system "git push --tags"
|
25
|
+
puts "Pushing to rubygems.org..."
|
26
|
+
system "gem push bullet-#{Bullet::VERSION}.gem"
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
30
|
+
spec.pattern = "spec/**/*_spec.rb"
|
31
|
+
end
|
32
|
+
|
33
|
+
RSpec::Core::RakeTask.new('spec:progress') do |spec|
|
34
|
+
spec.rspec_opts = %w(--format progress)
|
35
|
+
spec.pattern = "spec/**/*_spec.rb"
|
36
|
+
end
|
37
|
+
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
rdoc.rdoc_dir = "rdoc"
|
40
|
+
rdoc.title = "bullet #{Bullet::VERSION}"
|
41
|
+
rdoc.rdoc_files.include("README*")
|
42
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :spec
|
data/bullet.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
require "bullet/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "bullet"
|
8
|
+
s.version = Bullet::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Richard Huang"]
|
11
|
+
s.email = ["flyerhzm@gmail.com"]
|
12
|
+
s.homepage = "http://github.com/flyerhzm/bullet"
|
13
|
+
s.summary = "A rails plugin to kill N+1 queries and unused eager loading."
|
14
|
+
s.description = "A rails plugin to kill N+1 queries and unused eager loading."
|
15
|
+
|
16
|
+
s.required_rubygems_version = ">= 1.3.6"
|
17
|
+
|
18
|
+
s.add_dependency "uniform_notifier", "~> 1.0.0"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
24
|
+
|
data/lib/bullet.rb
CHANGED
@@ -2,10 +2,10 @@ require 'set'
|
|
2
2
|
require 'uniform_notifier'
|
3
3
|
|
4
4
|
module Bullet
|
5
|
-
|
6
|
-
|
7
|
-
if Rails.version =~ /^3.0/
|
5
|
+
if Rails.version =~ /^3\.0/
|
8
6
|
autoload :ActiveRecord, 'bullet/active_record3'
|
7
|
+
elsif Rails.version =~ /^3\.1/
|
8
|
+
autoload :ActiveRecord, 'bullet/active_record31'
|
9
9
|
else
|
10
10
|
autoload :ActiveRecord, 'bullet/active_record2'
|
11
11
|
autoload :ActionController, 'bullet/action_controller2'
|
@@ -18,7 +18,6 @@ module Bullet
|
|
18
18
|
autoload :NotificationCollector, 'bullet/notification_collector'
|
19
19
|
|
20
20
|
if defined? Rails::Railtie
|
21
|
-
# compatible with rails 3.0.0.beta4
|
22
21
|
class BulletRailtie < Rails::Railtie
|
23
22
|
initializer "bullet.configure_rails_initialization" do |app|
|
24
23
|
app.middleware.use Bullet::Rack
|
@@ -88,8 +87,9 @@ module Bullet
|
|
88
87
|
responses.join( "\n" )
|
89
88
|
end
|
90
89
|
|
91
|
-
def perform_out_of_channel_notifications
|
90
|
+
def perform_out_of_channel_notifications(env = {})
|
92
91
|
for_each_active_notifier_with_notification do |notification|
|
92
|
+
notification.url = [env['HTTP_HOST'], env['REQUEST_URI']].compact.join
|
93
93
|
notification.notify_out_of_channel
|
94
94
|
end
|
95
95
|
end
|
@@ -3,7 +3,7 @@ module Bullet
|
|
3
3
|
def self.enable
|
4
4
|
require 'action_controller'
|
5
5
|
case Rails.version
|
6
|
-
when /^2.3/
|
6
|
+
when /^2.3/
|
7
7
|
::ActionController::Dispatcher.middleware.use Bullet::Rack
|
8
8
|
::ActionController::Dispatcher.class_eval do
|
9
9
|
class <<self
|
@@ -22,20 +22,20 @@ module Bullet
|
|
22
22
|
Bullet.clear
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
::ActionController::Base.class_eval do
|
27
27
|
alias_method :origin_process, :process
|
28
28
|
def process(request, response, method = :perform_action, *arguments)
|
29
29
|
Bullet.start_request
|
30
30
|
response = origin_process(request, response, method = :perform_action, *arguments)
|
31
|
-
|
31
|
+
|
32
32
|
if Bullet.notification?
|
33
33
|
if response.headers["type"] and response.headers["type"].include? 'text/html' and response.body =~ %r{<html.*</html>}m
|
34
34
|
response.body <<= Bullet.gather_inline_notifications
|
35
35
|
response.headers["Content-Length"] = response.body.length.to_s
|
36
36
|
end
|
37
|
-
|
38
|
-
Bullet.
|
37
|
+
|
38
|
+
Bullet.perform_out_of_channel_notifications
|
39
39
|
end
|
40
40
|
Bullet.end_request
|
41
41
|
response
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Bullet
|
2
|
+
LOAD_TARGET_RGX = /load_target/
|
2
3
|
module ActiveRecord
|
3
4
|
def self.enable
|
4
5
|
require 'active_record'
|
@@ -81,7 +82,7 @@ module Bullet
|
|
81
82
|
def load_target
|
82
83
|
# avoid stack level too deep
|
83
84
|
result = origin_load_target
|
84
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.
|
85
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.find {|c| c =~ LOAD_TARGET_RGX }
|
85
86
|
Bullet::Detector::Association.add_possible_objects(result)
|
86
87
|
result
|
87
88
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Bullet
|
2
|
+
LOAD_TARGET_RGX = /load_target/
|
2
3
|
module ActiveRecord
|
3
4
|
def self.enable
|
4
5
|
require 'active_record'
|
@@ -75,7 +76,7 @@ module Bullet
|
|
75
76
|
def load_target
|
76
77
|
# avoid stack level too deep
|
77
78
|
result = origin_load_target
|
78
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.
|
79
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.find {|c| c =~ LOAD_TARGET_RGX }
|
79
80
|
Bullet::Detector::Association.add_possible_objects(result)
|
80
81
|
result
|
81
82
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Bullet
|
2
|
+
LOAD_TARGET_RGX = /load_target/
|
3
|
+
module ActiveRecord
|
4
|
+
def self.enable
|
5
|
+
require 'active_record'
|
6
|
+
::ActiveRecord::Relation.class_eval do
|
7
|
+
alias_method :origin_to_a, :to_a
|
8
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
9
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
10
|
+
def to_a
|
11
|
+
records = origin_to_a
|
12
|
+
if records.size > 1
|
13
|
+
Bullet::Detector::Association.add_possible_objects(records)
|
14
|
+
Bullet::Detector::Counter.add_possible_objects(records)
|
15
|
+
elsif records.size == 1
|
16
|
+
Bullet::Detector::Association.add_impossible_object(records.first)
|
17
|
+
Bullet::Detector::Counter.add_impossible_object(records.first)
|
18
|
+
end
|
19
|
+
records
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
::ActiveRecord::Associations::Preloader.class_eval do
|
24
|
+
# include query for one to many associations.
|
25
|
+
# keep this eager loadings.
|
26
|
+
alias_method :origin_initialize, :initialize
|
27
|
+
def initialize(records, associations, preload_options = {})
|
28
|
+
origin_initialize(records, associations, preload_options)
|
29
|
+
records = [records].flatten.compact.uniq
|
30
|
+
return if records.empty?
|
31
|
+
records.each do |record|
|
32
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
33
|
+
end
|
34
|
+
Bullet::Detector::Association.add_eager_loadings(records, associations)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
::ActiveRecord::FinderMethods.class_eval do
|
39
|
+
# add includes in scope
|
40
|
+
alias_method :origin_find_with_associations, :find_with_associations
|
41
|
+
def find_with_associations
|
42
|
+
records = origin_find_with_associations
|
43
|
+
associations = (@eager_load_values + @includes_values).uniq
|
44
|
+
records.each do |record|
|
45
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
46
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
47
|
+
end
|
48
|
+
Bullet::Detector::Association.add_eager_loadings(records, associations)
|
49
|
+
records
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
54
|
+
alias_method :origin_construct_association, :construct_association
|
55
|
+
# call join associations
|
56
|
+
def construct_association(record, join, row)
|
57
|
+
associations = join.reflection.name
|
58
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
59
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
60
|
+
origin_construct_association(record, join, row)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
65
|
+
# call one to many associations
|
66
|
+
alias_method :origin_load_target, :load_target
|
67
|
+
def load_target
|
68
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
69
|
+
origin_load_target
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
::ActiveRecord::Associations::Association.class_eval do
|
74
|
+
# call has_one and belong_to association
|
75
|
+
alias_method :origin_load_target, :load_target
|
76
|
+
def load_target
|
77
|
+
# avoid stack level too deep
|
78
|
+
result = origin_load_target
|
79
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.find {|c| c =~ LOAD_TARGET_RGX }
|
80
|
+
Bullet::Detector::Association.add_possible_objects(result)
|
81
|
+
result
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
86
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
87
|
+
|
88
|
+
def has_cached_counter?
|
89
|
+
result = origin_has_cached_counter?
|
90
|
+
Bullet::Detector::Counter.add_counter_cache(@owner, @reflection.name) unless result
|
91
|
+
result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -39,25 +39,35 @@ module Bullet
|
|
39
39
|
def add_eager_loadings(objects, associations)
|
40
40
|
objects = Array(objects)
|
41
41
|
|
42
|
+
to_add = nil
|
43
|
+
to_merge, to_delete = [], []
|
42
44
|
eager_loadings.each do |k, v|
|
43
45
|
key_objects_overlap = k & objects
|
44
46
|
|
45
47
|
next if key_objects_overlap.empty?
|
46
48
|
|
47
49
|
if key_objects_overlap == k
|
48
|
-
|
50
|
+
to_add = [k, associations]
|
49
51
|
break
|
50
52
|
|
51
53
|
else
|
52
|
-
|
54
|
+
to_merge << [key_objects_overlap, ( eager_loadings[k].dup << associations )]
|
53
55
|
|
54
56
|
keys_without_objects = k - objects
|
55
|
-
|
56
|
-
|
57
|
-
eager_loadings.delete(k)
|
57
|
+
to_merge << [keys_without_objects, eager_loadings[k]]
|
58
|
+
to_delete << k
|
58
59
|
objects = objects - k
|
59
60
|
end
|
60
61
|
end
|
62
|
+
if to_add
|
63
|
+
eager_loadings.add *to_add
|
64
|
+
end
|
65
|
+
to_merge.each do |k,val|
|
66
|
+
eager_loadings.merge k, val
|
67
|
+
end
|
68
|
+
to_delete.each do |k|
|
69
|
+
eager_loadings.delete k
|
70
|
+
end
|
61
71
|
|
62
72
|
eager_loadings.add objects, associations unless objects.empty?
|
63
73
|
end
|
@@ -73,15 +83,14 @@ module Bullet
|
|
73
83
|
|
74
84
|
# check if object => associations already exists in object_associations.
|
75
85
|
def association?(object, associations)
|
76
|
-
|
77
|
-
|
78
|
-
|
86
|
+
value = object_associations[object]
|
87
|
+
if value
|
79
88
|
value.each do |v|
|
80
89
|
result = v.is_a?(Hash) ? v.has_key?(associations) : v == associations
|
81
90
|
return true if result
|
82
91
|
end
|
83
|
-
|
84
92
|
end
|
93
|
+
|
85
94
|
return false
|
86
95
|
end
|
87
96
|
|
data/lib/bullet/detector/base.rb
CHANGED
@@ -23,11 +23,13 @@ module Bullet
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.call_object_association( object, association )
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
all = Set.new
|
27
|
+
eager_loadings.similarly_associated( object, association ).each do |related_object|
|
28
|
+
coa = call_object_associations[related_object]
|
29
|
+
next if coa.nil?
|
30
|
+
all.merge coa
|
31
|
+
end
|
32
|
+
all.to_a
|
31
33
|
end
|
32
34
|
|
33
35
|
def self.diff_object_association( object, association )
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Bullet
|
2
2
|
module Notification
|
3
3
|
class Base
|
4
|
-
attr_accessor :notifier
|
4
|
+
attr_accessor :notifier, :url
|
5
5
|
attr_reader :base_class, :associations, :path
|
6
6
|
|
7
7
|
def initialize( base_class, associations, path = nil )
|
@@ -15,6 +15,10 @@ module Bullet
|
|
15
15
|
|
16
16
|
def body
|
17
17
|
end
|
18
|
+
|
19
|
+
def whoami
|
20
|
+
"user: " << `whoami`.chomp
|
21
|
+
end
|
18
22
|
|
19
23
|
def body_with_caller
|
20
24
|
body
|
@@ -25,7 +29,7 @@ module Bullet
|
|
25
29
|
end
|
26
30
|
|
27
31
|
def full_notice
|
28
|
-
|
32
|
+
[whoami, url, title, body_with_caller].compact.join("\n")
|
29
33
|
end
|
30
34
|
|
31
35
|
def notify_inline
|
data/lib/bullet/rack.rb
CHANGED
@@ -16,7 +16,7 @@ module Bullet
|
|
16
16
|
response_body = response.body << Bullet.gather_inline_notifications
|
17
17
|
headers['Content-Length'] = response_body.length.to_s
|
18
18
|
end
|
19
|
-
Bullet.perform_out_of_channel_notifications
|
19
|
+
Bullet.perform_out_of_channel_notifications(env)
|
20
20
|
end
|
21
21
|
response_body ||= response.body
|
22
22
|
Bullet.end_request
|
@@ -3,13 +3,12 @@ module Bullet
|
|
3
3
|
class Association < Base
|
4
4
|
def merge( base, associations )
|
5
5
|
@registry.merge!( { base => associations } )
|
6
|
-
unique( @registry[base] )
|
7
6
|
end
|
8
7
|
|
9
8
|
def similarly_associated( base, associations )
|
10
9
|
@registry.select do |key, value|
|
11
10
|
key.include?( base ) and value == associations
|
12
|
-
end.collect( &:first ).flatten
|
11
|
+
end.collect( &:first ).flatten
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
data/lib/bullet/registry/base.rb
CHANGED
@@ -24,16 +24,14 @@ module Bullet
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def add( key, value )
|
27
|
-
@registry[key] ||=
|
28
|
-
|
29
|
-
|
27
|
+
@registry[key] ||= Set.new
|
28
|
+
if value.is_a? Array
|
29
|
+
@registry[key] += value
|
30
|
+
else
|
31
|
+
@registry[key] << value
|
32
|
+
end
|
30
33
|
end
|
31
34
|
|
32
|
-
private
|
33
|
-
def unique( array )
|
34
|
-
array.flatten!
|
35
|
-
array.uniq!
|
36
|
-
end
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
data/lib/bullet/version.rb
CHANGED
data/perf/benchmark.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
$: << 'lib'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'rails'
|
4
|
+
require 'active_record'
|
5
|
+
require 'activerecord-import'
|
6
|
+
require 'bullet'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'perftools'
|
10
|
+
rescue LoadError
|
11
|
+
puts "Could not load perftools.rb, profiling won't be possible"
|
12
|
+
end
|
13
|
+
|
14
|
+
class Post < ActiveRecord::Base
|
15
|
+
belongs_to :user
|
16
|
+
has_many :comments
|
17
|
+
end
|
18
|
+
|
19
|
+
class Comment < ActiveRecord::Base
|
20
|
+
belongs_to :user
|
21
|
+
belongs_to :post
|
22
|
+
end
|
23
|
+
|
24
|
+
class User < ActiveRecord::Base
|
25
|
+
has_many :posts
|
26
|
+
has_many :comments
|
27
|
+
end
|
28
|
+
|
29
|
+
# create database bullet_benchmark;
|
30
|
+
ActiveRecord::Base.establish_connection(:adapter => 'mysql', :database => 'bullet_benchmark', :server => '/tmp/mysql.socket', :username => 'root')
|
31
|
+
|
32
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
33
|
+
ActiveRecord::Base.connection.drop_table(table)
|
34
|
+
end
|
35
|
+
|
36
|
+
ActiveRecord::Schema.define(:version => 1) do
|
37
|
+
create_table :posts do |t|
|
38
|
+
t.column :title, :string
|
39
|
+
t.column :body, :string
|
40
|
+
t.column :user_id, :integer
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :comments do |t|
|
44
|
+
t.column :body, :string
|
45
|
+
t.column :post_id, :integer
|
46
|
+
t.column :user_id, :integer
|
47
|
+
end
|
48
|
+
|
49
|
+
create_table :users do |t|
|
50
|
+
t.column :name, :string
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
users_size = 100
|
55
|
+
posts_size = 1000
|
56
|
+
comments_size = 10000
|
57
|
+
users = []
|
58
|
+
users_size.times do |i|
|
59
|
+
users << User.new(:name => "user#{i}")
|
60
|
+
end
|
61
|
+
User.import users
|
62
|
+
users = User.all
|
63
|
+
|
64
|
+
posts = []
|
65
|
+
posts_size.times do |i|
|
66
|
+
posts << Post.new(:title => "Title #{i}", :body => "Body #{i}", :user => users[i%100])
|
67
|
+
end
|
68
|
+
Post.import posts
|
69
|
+
posts = Post.all
|
70
|
+
|
71
|
+
comments = []
|
72
|
+
comments_size.times do |i|
|
73
|
+
comments << Comment.new(:body => "Comment #{i}", :post => posts[i%1000], :user => users[i%100])
|
74
|
+
end
|
75
|
+
Comment.import comments
|
76
|
+
|
77
|
+
puts "Start benchmarking..."
|
78
|
+
|
79
|
+
|
80
|
+
Bullet.enable = true
|
81
|
+
|
82
|
+
PerfTools::CpuProfiler.start(ARGV[0]|| "benchmark_profile") if defined? PerfTools
|
83
|
+
|
84
|
+
Benchmark.bm(70) do |bm|
|
85
|
+
bm.report("Querying & Iterating #{posts_size} Posts with #{comments_size} Comments and #{users_size} Users") do
|
86
|
+
Bullet.start_request
|
87
|
+
Post.select("SQL_NO_CACHE *").includes(:user, :comments => :user).each do |p|
|
88
|
+
p.title
|
89
|
+
p.user.name
|
90
|
+
p.comments.each do |c|
|
91
|
+
c.body
|
92
|
+
c.user.name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
Bullet.end_request
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
PerfTools::CpuProfiler.stop if defined? PerfTools
|
100
|
+
puts "End benchmarking..."
|
101
|
+
|
102
|
+
|
103
|
+
# 2.0.1
|
104
|
+
# user system total real
|
105
|
+
# Querying & Iterating 100 Posts with 10000 Comments and 100 Users 2.290000 0.050000 2.340000 ( 2.366174)
|
106
|
+
|