Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically Removed Whitespace #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions README.textile
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ h2. What it does
<code>
Processing HotelsController#show (for 127.0.0.1 at 2009-03-18 19:29:38) [GET]
Parameters: {"action"=>"show", "id"=>"8699-radisson-hotel-waterfront-cape-town", "controller"=>"hotels"}
Hotel Load Scrooged (0.3ms) SELECT `hotels`.id FROM `hotels` WHERE (`hotels`.`id` = 8699)
Hotel Load Scrooged (0.3ms) SELECT `hotels`.id FROM `hotels` WHERE (`hotels`.`id` = 8699)
Rendering template within layouts/application
Rendering hotels/show
SQL (0.2ms) SELECT `hotels`.location_id,`hotels`.hotel_name,`hotels`.location,`hotels`.from_price,`hotels`.star_rating,`hotels`.apt,`hotels`.latitude,`hotels`.longitude,`hotels`.distance,`hotels`.narrative,`hotels`.telephone,`hotels`.important_notes,`hotels`.nearest_tube,`hotels`.nearest_rail,`hotels`.created_at,`hotels`.updated_at,`hotels`.id FROM `hotels` WHERE `hotels`.id IN ('8699')
Image Load Scrooged (0.2ms) SELECT `images`.id FROM `images` WHERE (`images`.hotel_id = 8699) LIMIT 1
SQL (0.2ms) SELECT `images`.hotel_id,`images`.title,`images`.url,`images`.width,`images`.height,`images`.thumbnail_url,`images`.thumbnail_width,`images`.thumbnail_height,`images`.has_thumbnail,`images`.created_at,`images`.updated_at,`images`.id FROM `images` WHERE `images`.id IN ('488')
Rendered shared/_header (0.0ms)
Rendered shared/_navigation (0.2ms)
Image Load Scrooged (0.2ms) SELECT `images`.id FROM `images` WHERE (`images`.hotel_id = 8699)
Image Load Scrooged (0.2ms) SELECT `images`.id FROM `images` WHERE (`images`.hotel_id = 8699)
SQL (0.2ms) SELECT `images`.hotel_id,`images`.title,`images`.url,`images`.width,`images`.height,`images`.thumbnail_url,`images`.thumbnail_width,`images`.thumbnail_height,`images`.has_thumbnail,`images`.created_at,`images`.updated_at,`images`.id FROM `images` WHERE `images`.id IN ('488')
Address Columns (306.2ms) SHOW FIELDS FROM `addresses`
Address Load Scrooged (3.6ms) SELECT `addresses`.id FROM `addresses` WHERE (`addresses`.hotel_id = 8699) LIMIT 1
Expand All @@ -40,20 +40,20 @@ h2. What it does

Processing HotelsController#show (for 127.0.0.1 at 2009-03-18 19:29:40) [GET]
Parameters: {"action"=>"show", "id"=>"8699-radisson-hotel-waterfront-cape-town", "controller"=>"hotels"}
Hotel Load Scrooged (0.3ms) SELECT `hotels`.narrative,`hotels`.from_price,`hotels`.star_rating,`hotels`.hotel_name,`hotels`.id FROM `hotels` WHERE (`hotels`.`id` = 8699)
Address Load Scrooged (0.2ms) SELECT `addresses`.id FROM `addresses` WHERE (`addresses`.hotel_id = 8699)
Hotel Load Scrooged (0.3ms) SELECT `hotels`.narrative,`hotels`.from_price,`hotels`.star_rating,`hotels`.hotel_name,`hotels`.id FROM `hotels` WHERE (`hotels`.`id` = 8699)
Address Load Scrooged (0.2ms) SELECT `addresses`.id FROM `addresses` WHERE (`addresses`.hotel_id = 8699)
Rendering template within layouts/application
Rendering hotels/show
Image Load Scrooged (0.3ms) SELECT `images`.url,`images`.id,`images`.height,`images`.width FROM `images` WHERE (`images`.hotel_id = 8699) LIMIT 1
Rendered shared/_header (0.1ms)
Rendered shared/_navigation (0.2ms)
Image Load Scrooged (0.3ms) SELECT `images`.thumbnail_width,`images`.id,`images`.thumbnail_height,`images`.thumbnail_url FROM `images` WHERE (`images`.hotel_id = 8699)
Image Load Scrooged (0.3ms) SELECT `images`.thumbnail_width,`images`.id,`images`.thumbnail_height,`images`.thumbnail_url FROM `images` WHERE (`images`.hotel_id = 8699)
Rendered hotels/_show_sidebar (1.0ms)
Rendered shared/_footer (0.1ms)
Completed in 8ms (View: 5, DB: 1) | 200 OK [http://localhost/hotels/8699-radisson-hotel-waterfront-cape-town]

</code>
</pre>
</pre>

h2. Suggested Use

Expand All @@ -75,7 +75,7 @@ h4. As a Gem

h2. Stability

The whole Rails 2.3.2 ActiveRecord test suite passes with scrooge, except for 13 failures related to callsite augmentation (note the SQL reload snippets below). Thoughts on handling or circumventing this much appreciated.
The whole Rails 2.3.2 ActiveRecord test suite passes with scrooge, except for 13 failures related to callsite augmentation (note the SQL reload snippets below). Thoughts on handling or circumventing this much appreciated.

<pre>
<code>
Expand All @@ -87,10 +87,10 @@ test_finding_with_includes_on_belongs_to_association_with_same_include_includes_
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.3.1/lib/active_support/testing/setup_and_teardown.rb:57:in `run']:
5 instead of 3 queries were executed.
Queries:
SELECT `posts`.id,`posts`.type FROM `posts` WHERE (`posts`.`id` = 1)
SELECT `posts`.author_id,`posts`.title,`posts`.body,`posts`.comments_count,`posts`.taggings_count FROM `posts` WHERE (`posts`.`id` = 1)
SELECT `authors`.name,`authors`.id FROM `authors` WHERE (`authors`.`id` = 1)
SELECT `authors`.author_address_id,`authors`.author_address_extra_id FROM `authors` WHERE (`authors`.`id` = 1)
SELECT `posts`.id,`posts`.type FROM `posts` WHERE (`posts`.`id` = 1)
SELECT `posts`.author_id,`posts`.title,`posts`.body,`posts`.comments_count,`posts`.taggings_count FROM `posts` WHERE (`posts`.`id` = 1)
SELECT `authors`.name,`authors`.id FROM `authors` WHERE (`authors`.`id` = 1)
SELECT `authors`.author_address_id,`authors`.author_address_extra_id FROM `authors` WHERE (`authors`.`id` = 1)
SELECT `author_addresses`.id FROM `author_addresses` WHERE (`author_addresses`.`id` = 1) .
<3> expected but was
<5>.
Expand Down Expand Up @@ -149,43 +149,43 @@ Ruby allows introspection of the call tree through
<pre>
<code>
Kernel#caller
</code>
</pre>
</code>
</pre>

Scrooge analyzes the last 10 calltree elements that triggered
Scrooge analyzes the last 10 calltree elements that triggered

<pre>
<code>
ActiveRecord::Base.find_by_sql
</code>
</code>
</pre>

Lets refer to that as a callsite, or signature.

Thus given SQL such as
Thus given SQL such as

<pre>
<code>
"SELECT * FROM `images` WHERE (`images`.hotel_id = 11697) LIMIT 1"
</code>
</code>
</pre>

Called from our application helper

<pre>
<code>
["/Users/lourens/projects/superbreak_app/vendor/plugins/scrooge/rails/../lib/scrooge.rb:27:in `find_by_sql'", "/Users/lourens/projects/superbreak_app/vendor/rails/activerecord/lib/active_record/base.rb:1557:in `find_every'", "/Users/lourens/projects/superbreak_app/vendor/rails/activerecord/lib/active_record/base.rb:1514:in `find_initial'", "/Users/lourens/projects/superbreak_app/vendor/rails/activerecord/lib/active_record/base.rb:613:in `find'", "/Users/lourens/projects/superbreak_app/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:60:in `find'", "/Users/lourens/projects/superbreak_app/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:67:in `first'", "/Users/lourens/projects/superbreak_app/app/helpers/application_helper.rb:60:in `hotel_image'", "/Users/lourens/projects/superbreak_app/app/views/hotels/_hotel.html.erb:4:in `_run_erb_app47views47hotels47_hotel46html46erb_locals_hotel_hotel_counter_object'", "/Users/lourens/projects/superbreak_app/vendor/rails/actionpack/lib/action_view/renderable.rb:36:in `send'", "/Users/lourens/projects/superbreak_app/vendor/rails/actionpack/lib/action_view/renderable.rb:36:in `render'", "/Users/lourens/projects/superbreak_app/vendor/rails/actionpack/lib/action_view/renderable_partial.rb:20:in `render'"]
</code>
</code>
</pre>

We can generate a unique callsite identifier with the following calculation :
<pre>
<code>
(The above calltree << "SELECT * FROM `images` ).hash " # cut off conditions etc.
</code>
</code>
</pre>

Callsites are tracked on a per model ( table name ) basis.
Callsites are tracked on a per model ( table name ) basis.

h4. Scope

Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ task :default => [:test_with_active_record, :test_scrooge]
task :test => :default

Rake::TestTask.new( :test_with_active_record ) { |t|
t.libs << AR_TEST_SUITE << Scrooge::Test.connection()
t.libs << AR_TEST_SUITE << Scrooge::Test.connection()
t.test_files = Scrooge::Test.active_record_test_files()
t.ruby_opts = ["-r #{File.join( File.dirname(__FILE__), 'test', 'setup' )}"]
t.verbose = true
Expand Down
2 changes: 1 addition & 1 deletion VERSION.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
---
:major: 3
:minor: 0
:patch: 0
54 changes: 27 additions & 27 deletions lib/callsite.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
module Scrooge
class Callsite
# Represents a Callsite and is a container for any columns and

# Represents a Callsite and is a container for any columns and
# associations referenced at the callsite.
#

Mtx = Mutex.new # mutex should perhaps be per-instance at the expense of a little memory

attr_accessor :klass,
:signature,
:columns,
:associations

def initialize( klass, signature )
@klass = klass
@signature = signature
end

# Flag a column as seen
#
def column!( column )
columns && Mtx.synchronize { @columns << column }
end

# Flag an association as seen
# association should be an AssociationReflection object
#
Expand All @@ -31,33 +31,33 @@ def association!(association, record_id)
associations.register(association, record_id)
end
end

def inspect
"<##{@klass.name} :select => '#{@klass.scrooge_select_sql( columns )}', :include => [#{associations_for_inspect}]>"
end

# Lazy init default columns
#
def default_columns
@default_columns || Mtx.synchronize { @default_columns = setup_columns }
end

# Lazy init columns
#
def columns
@columns || default_columns && Mtx.synchronize { @columns = @default_columns.dup }
@columns || default_columns && Mtx.synchronize { @columns = @default_columns.dup }
end

# Lazy init associations
#
def associations
@associations || Mtx.synchronize { @associations = setup_associations }
end

def has_associations?
@associations
end

# Analyze previously collected information
# and reset ready for a new query
#
Expand All @@ -66,63 +66,63 @@ def reset
associations.reset
end
end

def register_result_set(result_set)
if has_associations?
associations.register_result_set(result_set)
end
end

private

def associations_for_inspect
if has_associations?
associations.to_preload.map{|a| ":#{a.to_s}" }.join(', ')
else
""
end
end

# Only register associations that isn't polymorphic or a collection
#
def preloadable_association?( association )
@klass.preloadable_associations.include?( association.to_sym )
end

# Is the table a container for STI models ?
#
#
def inheritable?
@klass.columns_hash.has_key?( inheritance_column )
end

# Ensure that at least the primary key and optionally the inheritance
# column ( for STI ) is set.
# column ( for STI ) is set.
#
def setup_columns
if inheritable?
SimpleSet.new([primary_key, inheritance_column])
else
primary_key.blank? ? SimpleSet.new : SimpleSet.new([primary_key])
end
end
end

# Start with no registered associations
#
def setup_associations
Optimizations::Associations::AssociationSet.new
end

# Memoize a string representation of the inheritance column
#
def inheritance_column
@inheritance_column ||= @klass.inheritance_column.to_s
end

# Memoize a string representation of the primary
#
#
def primary_key
@primary_key ||= @klass.primary_key.to_s
end
end

end
end
30 changes: 15 additions & 15 deletions lib/optimizations/associations/association_set.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module Scrooge
module Optimizations
module Optimizations
module Associations

# Keeps track of how a result set is used to access associations
# Each callsite where associations are accessed will contain one of
# Each callsite where associations are accessed will contain one of
# these objects.
# Each thread will collect data, and we check this data before each
# fetch from the database, adding any associations that are needed
Expand All @@ -21,58 +21,58 @@ def initialize
@associations = SimpleSet.new
@as_data_id = :"association_data_#{object_id}"
end

def register(association, record_id)
assoc_data.register(association, record_id)
end

def register_result_set(result_set)
assoc_data.register_result_set(result_set)
end

def reset
Mtx.synchronize do
@associations |= assoc_data.to_preload
end
assoc_data.reset
end

def to_preload
@associations.to_a
end

private

def assoc_data
Thread.current[@as_data_id] ||= AssociationData.new
end
end

class AssociationData
def initialize
reset
end

def reset
@associations = {}
@result_set_size = 0
end

def register(association, record_id)
if @result_set_size > 1
assoc = (@associations[association.name] ||= AssociationIdentity.new(association))
assoc.register(record_id)
end
end

def register_result_set(result_set)
@result_set_size = result_set.size
end

def to_preload
@associations.values.select { |association| preload_this_assoc?(association) }.map(&:name)
end

private

# Calculate the benefit of preloading an association
Expand All @@ -81,7 +81,7 @@ def to_preload
# to access the association - more than 25% and we preload
#
# TODO: more rules and analysis for different association types
#
#
def preload_this_assoc?(association)
if @result_set_size <= 1
false
Expand Down
Loading