Permalink
Browse files

Speed up table_exists? for databases with a large number of tables

At New Relic, we have hundreds of thousands of tables, and our migrations took 30 minutes without this similar patch. This cuts it down to a more reasonable amount of time.

The rescue false part is ugly, but necessary as far as I can tell. I don't know of a cross-database statement you can make that will work without trapping errors.
  • Loading branch information...
1 parent f6cc4fd commit 73a331c2acfce971aa2dda8e72af5edc6867e344 @jadeforrest jadeforrest committed Dec 5, 2011
View
7 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -23,7 +23,12 @@ def table_alias_for(table_name)
# === Example
# table_exists?(:developers)
def table_exists?(table_name)
- tables.include?(table_name.to_s)
+ begin
+ select_value("SELECT 1 FROM #{table_name.to_s} where 1=0")
+ true
+ rescue
+ false
+ end
end
# Returns an array of indexes for the given table.

6 comments on commit 73a331c

@dhh
Ruby on Rails member

This is generating a bunch of SELECT 1 log lines in development. If this really is the fastest way, then at the very least we should silence them for dev logging.

@dhh
Ruby on Rails member

At times I even get an avalanche of the same query triggered:

CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
CACHE (0.0ms)  SELECT 1 FROM `people` where 1=0
@josevalim
Ruby on Rails member

It makes sense to cache this result during the request life-cyle and eternally in production.

@bogdan

You should probably rescue ActiveRecord::StatementInvalid because rescue everything will hide other failure reason e.g. bugs in configuration or rails itself.

@josevalim
Ruby on Rails member

Btw, can we have some insight on why tables.include?() was slow in the first place?

@pixeltrix
Ruby on Rails member

@josevalim because it had hundreds of thousands of tables in it maybe? The tables method gets all the tables from the database. You may want to question the wisdom of having hundreds of thousands of tables in a database but I guess they know what they’re doing.

The queries are coming from AR::Base#inspect which does a check to see whether the table exists when building the output. We also probably want to cache the query in the tables method for the request life-cycle and eternally in production as well.

Please sign in to comment.