Skip to content

Commit e3b4a46

Browse files
committed
Add ability to ignore tables in the schema cache
In cases where an application uses pt-osc or lhm they may have temporary tables being used for migrations. Those tables shouldn't be included by the schema cache because it makes the cache bigger and those tables shouldn't ever be queried by the application. This feature allows applications to configure a list of or regex of tables to ignore. The behavior for ignored tables for each method in the schema cache (`columns`, `columns_hash`, `primary_keys`, and `indexes`) matches the behavior of a non-existent table. `columns` and `columns_hash` will raise a database not found error, `primary_keys` will return `nil`. and `indexes` will return an empty array. See rails#43105 which make behavior across adapters consistent. To use in an application configure `config.active_record.schema_cache_ignored_tables` to an array of tables or regex's. Those tables will not be included in the schema cache dump.
1 parent 1b2fa18 commit e3b4a46

File tree

4 files changed

+82
-2
lines changed

4 files changed

+82
-2
lines changed

activerecord/CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
* Add config option for ignoring tables when dumping the schema cache.
2+
3+
Applications can now be configured to ignore certain tables when dumping the schema cache.
4+
5+
The configuration option can table an array of tables:
6+
7+
```ruby
8+
config.active_record.schema_cache_ignored_tables = ["ignored_table", "another_ignored_table"]
9+
```
10+
11+
Or a regex:
12+
13+
```ruby
14+
config.active_record.schema_cache_ignored_tables = [/^_/]
15+
```
16+
17+
*Eileen M. Uchitelle*
18+
119
* Make schema cache methods return consistent results.
220

321
Previously the schema cache methods `primary_keys`, `columns, `columns_hash`, and `indexes`

activerecord/lib/active_record.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ module Tasks
170170
autoload :TestDatabases, "active_record/test_databases"
171171
autoload :TestFixtures, "active_record/fixtures"
172172

173+
# A list of tables or regex's to match tables to ignore when
174+
# dumping the schema cache. For example if this is set to +[/^_/]+
175+
# the schema cache will not dump tables named with an underscore.
176+
singleton_class.attr_accessor :schema_cache_ignored_tables
177+
self.schema_cache_ignored_tables = []
178+
173179
singleton_class.attr_accessor :legacy_connection_handling
174180
self.legacy_connection_handling = true
175181

activerecord/lib/active_record/connection_adapters/schema_cache.rb

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def primary_keys(table_name)
8686

8787
# A cached lookup for table existence.
8888
def data_source_exists?(name)
89+
return if ignored_table?(name)
8990
prepare_data_sources if @data_sources.empty?
9091
return @data_sources[name] if @data_sources.key? name
9192

@@ -108,6 +109,10 @@ def data_sources(name)
108109

109110
# Get the columns for a table
110111
def columns(table_name)
112+
if ignored_table?(table_name)
113+
raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist"
114+
end
115+
111116
@columns.fetch(table_name) do
112117
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
113118
end
@@ -166,7 +171,7 @@ def clear_data_source_cache!(name)
166171

167172
def dump_to(filename)
168173
clear!
169-
connection.data_sources.each { |table| add(table) }
174+
tables_to_cache.each { |table| add(table) }
170175
open(filename) { |f|
171176
if filename.include?(".dump")
172177
f.write(Marshal.dump(self))
@@ -190,6 +195,18 @@ def marshal_load(array)
190195
end
191196

192197
private
198+
def tables_to_cache
199+
connection.data_sources.reject do |table|
200+
ignored_table?(table)
201+
end
202+
end
203+
204+
def ignored_table?(table_name)
205+
ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
206+
ignored === table_name
207+
end
208+
end
209+
193210
def reset_version!
194211
@version = connection.migration_context.current_version
195212
end
@@ -216,7 +233,9 @@ def deep_deduplicate(value)
216233
end
217234

218235
def prepare_data_sources
219-
connection.data_sources.each { |source| @data_sources[source] = true }
236+
tables_to_cache.each do |source|
237+
@data_sources[source] = true
238+
end
220239
end
221240

222241
def open(filename)

activerecord/test/cases/connection_adapters/schema_cache_test.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,43 @@ def test_marshal_dump_and_load_via_disk
220220
tempfile.unlink
221221
end
222222

223+
def test_marshal_dump_and_load_with_ignored_tables
224+
old_ignore = ActiveRecord.schema_cache_ignored_tables
225+
ActiveRecord.schema_cache_ignored_tables = ["p_schema_migrations"]
226+
# Create an empty cache.
227+
cache = SchemaCache.new @connection
228+
229+
tempfile = Tempfile.new(["schema_cache-", ".dump"])
230+
# Dump it. It should get populated before dumping.
231+
cache.dump_to(tempfile.path)
232+
233+
# Load a new cache.
234+
cache = SchemaCache.load_from(tempfile.path)
235+
cache.connection = @connection
236+
237+
# Assert a table in the cache
238+
assert cache.data_sources("posts"), "expected posts to be in the cached data_sources"
239+
assert_equal 12, cache.columns("posts").size
240+
assert_equal 12, cache.columns_hash("posts").size
241+
assert cache.data_sources("posts")
242+
assert_equal "id", cache.primary_keys("posts")
243+
assert_equal 1, cache.indexes("posts").size
244+
245+
# Assert ignored table. Behavior should match non-existent table.
246+
assert_nil cache.data_sources("p_schema_migrations"), "expected comments to not be in the cached data_sources"
247+
assert_raises ActiveRecord::StatementInvalid do
248+
cache.columns("p_schema_migrations")
249+
end
250+
assert_raises ActiveRecord::StatementInvalid do
251+
cache.columns_hash("p_schema_migrations").size
252+
end
253+
assert_nil cache.primary_keys("p_schema_migrations")
254+
assert_equal [], cache.indexes("p_schema_migrations")
255+
ensure
256+
tempfile.unlink
257+
ActiveRecord.schema_cache_ignored_tables = old_ignore
258+
end
259+
223260
def test_marshal_dump_and_load_with_gzip
224261
# Create an empty cache.
225262
cache = SchemaCache.new @connection

0 commit comments

Comments
 (0)