bullet 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|