2017-07-09 13:41:28 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2011-06-06 14:17:44 -04:00
|
|
|
require "cases/helper"
|
2016-08-06 12:26:20 -04:00
|
|
|
require "models/topic"
|
|
|
|
require "models/task"
|
|
|
|
require "models/category"
|
|
|
|
require "models/post"
|
|
|
|
require "rack"
|
2007-09-08 00:31:26 -04:00
|
|
|
|
2008-01-21 12:20:51 -05:00
|
|
|
class QueryCacheTest < ActiveRecord::TestCase
|
2016-07-17 08:41:09 -04:00
|
|
|
self.use_transactional_tests = false
|
|
|
|
|
2008-01-17 20:55:11 -05:00
|
|
|
fixtures :tasks, :topics, :categories, :posts, :categories_posts
|
2007-09-17 02:15:58 -04:00
|
|
|
|
2016-10-20 12:21:16 -04:00
|
|
|
class ShouldNotHaveExceptionsLogger < ActiveRecord::LogSubscriber
|
2018-06-12 15:36:54 -04:00
|
|
|
attr_reader :logger, :events
|
2016-10-20 12:21:16 -04:00
|
|
|
|
|
|
|
def initialize
|
|
|
|
super
|
|
|
|
@logger = ::Logger.new File::NULL
|
|
|
|
@exception = false
|
2018-06-12 15:36:54 -04:00
|
|
|
@events = []
|
2016-10-20 12:21:16 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def exception?
|
|
|
|
@exception
|
|
|
|
end
|
|
|
|
|
|
|
|
def sql(event)
|
2018-06-12 15:36:54 -04:00
|
|
|
@events << event
|
2016-10-20 12:21:16 -04:00
|
|
|
super
|
|
|
|
rescue
|
|
|
|
@exception = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def teardown
|
2010-03-22 12:53:07 -04:00
|
|
|
Task.connection.clear_query_cache
|
2011-05-02 14:30:49 -04:00
|
|
|
ActiveRecord::Base.connection.disable_query_cache!
|
2016-10-20 12:21:16 -04:00
|
|
|
super
|
2010-03-22 12:53:07 -04:00
|
|
|
end
|
|
|
|
|
2011-08-28 17:15:51 -04:00
|
|
|
def test_exceptional_middleware_clears_and_disables_cache_on_error
|
2016-10-24 14:53:00 -04:00
|
|
|
assert_cache :off
|
2011-08-28 17:15:51 -04:00
|
|
|
|
2016-02-21 20:25:52 -05:00
|
|
|
mw = middleware { |env|
|
2011-08-28 17:15:51 -04:00
|
|
|
Task.find 1
|
|
|
|
Task.find 1
|
2017-06-28 04:23:55 -04:00
|
|
|
query_cache = ActiveRecord::Base.connection.query_cache
|
|
|
|
assert_equal 1, query_cache.length, query_cache.keys
|
2011-08-28 17:15:51 -04:00
|
|
|
raise "lol borked"
|
|
|
|
}
|
|
|
|
assert_raises(RuntimeError) { mw.call({}) }
|
|
|
|
|
2016-10-24 14:53:00 -04:00
|
|
|
assert_cache :off
|
2011-08-28 17:15:51 -04:00
|
|
|
end
|
2016-09-30 01:26:19 -04:00
|
|
|
|
2018-11-20 08:20:15 -05:00
|
|
|
def test_query_cache_is_applied_to_connections_in_all_handlers
|
2018-12-19 15:11:22 -05:00
|
|
|
ActiveRecord::Base.connection_handlers = {
|
|
|
|
writing: ActiveRecord::Base.default_connection_handler,
|
|
|
|
reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
|
|
|
}
|
|
|
|
|
2018-11-20 08:20:15 -05:00
|
|
|
ActiveRecord::Base.connected_to(role: :reading) do
|
|
|
|
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["arunit"])
|
|
|
|
end
|
|
|
|
|
|
|
|
mw = middleware { |env|
|
|
|
|
ro_conn = ActiveRecord::Base.connection_handlers[:reading].connection_pool_list.first.connection
|
|
|
|
assert_predicate ActiveRecord::Base.connection, :query_cache_enabled
|
|
|
|
assert_predicate ro_conn, :query_cache_enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
mw.call({})
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
|
|
|
|
end
|
|
|
|
|
2017-01-10 05:34:23 -05:00
|
|
|
def test_query_cache_across_threads
|
|
|
|
with_temporary_connection_pool do
|
2018-12-20 12:44:01 -05:00
|
|
|
if in_memory_db?
|
|
|
|
# Separate connections to an in-memory database create an entirely new database,
|
|
|
|
# with an empty schema etc, so we just stub out this schema on the fly.
|
|
|
|
ActiveRecord::Base.connection_pool.with_connection do |connection|
|
|
|
|
connection.create_table :tasks do |t|
|
|
|
|
t.datetime :starting
|
|
|
|
t.datetime :ending
|
2017-01-10 05:34:23 -05:00
|
|
|
end
|
|
|
|
end
|
2018-12-20 12:44:01 -05:00
|
|
|
ActiveRecord::FixtureSet.create_fixtures(self.class.fixture_path, ["tasks"], {}, ActiveRecord::Base)
|
|
|
|
end
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
ActiveRecord::Base.connection_pool.connections.each do |conn|
|
|
|
|
assert_cache :off, conn
|
|
|
|
end
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
assert_not_predicate ActiveRecord::Base.connection, :nil?
|
|
|
|
assert_cache :off
|
2011-08-28 17:15:51 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
middleware {
|
|
|
|
assert_cache :clean
|
2016-10-24 14:53:00 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
Task.find 1
|
|
|
|
assert_cache :dirty
|
2016-10-24 14:53:00 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
thread_1_connection = ActiveRecord::Base.connection
|
|
|
|
ActiveRecord::Base.clear_active_connections!
|
|
|
|
assert_cache :off, thread_1_connection
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
started = Concurrent::Event.new
|
|
|
|
checked = Concurrent::Event.new
|
2016-10-24 14:53:00 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
thread_2_connection = nil
|
|
|
|
thread = Thread.new {
|
|
|
|
thread_2_connection = ActiveRecord::Base.connection
|
2016-10-24 14:53:00 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
assert_equal thread_2_connection, thread_1_connection
|
|
|
|
assert_cache :off
|
2016-10-24 14:53:00 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
middleware {
|
|
|
|
assert_cache :clean
|
2011-08-28 17:15:51 -04:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
Task.find 1
|
|
|
|
assert_cache :dirty
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
started.set
|
|
|
|
checked.wait
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
ActiveRecord::Base.clear_active_connections!
|
|
|
|
}.call({})
|
|
|
|
}
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
started.wait
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
thread_1_connection = ActiveRecord::Base.connection
|
|
|
|
assert_not_equal thread_1_connection, thread_2_connection
|
|
|
|
assert_cache :dirty, thread_2_connection
|
|
|
|
checked.set
|
|
|
|
thread.join
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
assert_cache :off, thread_2_connection
|
|
|
|
}.call({})
|
2017-01-10 05:34:23 -05:00
|
|
|
|
2018-12-20 12:44:01 -05:00
|
|
|
ActiveRecord::Base.connection_pool.connections.each do |conn|
|
|
|
|
assert_cache :off, conn
|
2017-01-10 05:34:23 -05:00
|
|
|
end
|
2018-12-20 12:44:01 -05:00
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.connection_pool.disconnect!
|
2016-10-24 14:53:00 -04:00
|
|
|
end
|
2011-08-28 17:15:51 -04:00
|
|
|
end
|
|
|
|
|
2011-05-02 14:13:49 -04:00
|
|
|
def test_middleware_delegates
|
|
|
|
called = false
|
2016-02-21 20:25:52 -05:00
|
|
|
mw = middleware { |env|
|
2011-05-02 14:13:49 -04:00
|
|
|
called = true
|
2012-01-16 06:36:41 -05:00
|
|
|
[200, {}, nil]
|
2011-05-02 14:13:49 -04:00
|
|
|
}
|
|
|
|
mw.call({})
|
2016-08-06 12:26:20 -04:00
|
|
|
assert called, "middleware should delegate"
|
2011-05-02 14:13:49 -04:00
|
|
|
end
|
|
|
|
|
2011-05-02 14:17:31 -04:00
|
|
|
def test_middleware_caches
|
2016-02-21 20:25:52 -05:00
|
|
|
mw = middleware { |env|
|
2011-05-02 14:13:49 -04:00
|
|
|
Task.find 1
|
|
|
|
Task.find 1
|
2017-06-28 14:11:15 -04:00
|
|
|
query_cache = ActiveRecord::Base.connection.query_cache
|
|
|
|
assert_equal 1, query_cache.length, query_cache.keys
|
2012-01-16 06:36:41 -05:00
|
|
|
[200, {}, nil]
|
2011-05-02 14:13:49 -04:00
|
|
|
}
|
|
|
|
mw.call({})
|
|
|
|
end
|
|
|
|
|
2011-05-02 14:17:31 -04:00
|
|
|
def test_cache_enabled_during_call
|
2016-10-24 14:53:00 -04:00
|
|
|
assert_cache :off
|
2011-05-02 14:17:31 -04:00
|
|
|
|
2016-02-21 20:25:52 -05:00
|
|
|
mw = middleware { |env|
|
2016-10-24 14:53:00 -04:00
|
|
|
assert_cache :clean
|
2012-01-16 06:36:41 -05:00
|
|
|
[200, {}, nil]
|
2011-05-02 14:17:31 -04:00
|
|
|
}
|
|
|
|
mw.call({})
|
|
|
|
end
|
|
|
|
|
2014-03-13 13:05:10 -04:00
|
|
|
def test_cache_passing_a_relation
|
|
|
|
post = Post.first
|
|
|
|
Post.cache do
|
|
|
|
query = post.categories.select(:post_id)
|
|
|
|
assert Post.connection.select_all(query).is_a?(ActiveRecord::Result)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-02-06 16:16:07 -05:00
|
|
|
def test_find_queries
|
2012-03-01 22:10:06 -05:00
|
|
|
assert_queries(2) { Task.find(1); Task.find(1) }
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_find_queries_with_cache
|
|
|
|
Task.cache do
|
2007-09-17 02:15:58 -04:00
|
|
|
assert_queries(1) { Task.find(1); Task.find(1) }
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
end
|
2007-09-02 19:52:58 -04:00
|
|
|
|
2010-10-27 17:33:02 -04:00
|
|
|
def test_find_queries_with_cache_multi_record
|
2010-10-27 17:05:40 -04:00
|
|
|
Task.cache do
|
|
|
|
assert_queries(2) { Task.find(1); Task.find(1); Task.find(2) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-07-28 15:42:34 -04:00
|
|
|
def test_find_queries_with_multi_cache_blocks
|
|
|
|
Task.cache do
|
|
|
|
Task.cache do
|
|
|
|
assert_queries(2) { Task.find(1); Task.find(2) }
|
|
|
|
end
|
2018-10-04 14:07:12 -04:00
|
|
|
assert_no_queries { Task.find(1); Task.find(1); Task.find(2) }
|
2013-07-28 15:42:34 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-09-02 19:52:58 -04:00
|
|
|
def test_count_queries_with_cache
|
|
|
|
Task.cache do
|
2007-09-17 02:15:58 -04:00
|
|
|
assert_queries(1) { Task.count; Task.count }
|
2007-09-02 19:52:58 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-14 18:33:20 -04:00
|
|
|
def test_exists_queries_with_cache
|
|
|
|
Post.cache do
|
|
|
|
assert_queries(1) { Post.exists?; Post.exists? }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-15 05:27:00 -04:00
|
|
|
def test_select_all_with_cache
|
|
|
|
Post.cache do
|
|
|
|
assert_queries(1) do
|
|
|
|
2.times { Post.connection.select_all(Post.all) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_select_one_with_cache
|
|
|
|
Post.cache do
|
|
|
|
assert_queries(1) do
|
|
|
|
2.times { Post.connection.select_one(Post.all) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_select_value_with_cache
|
|
|
|
Post.cache do
|
|
|
|
assert_queries(1) do
|
|
|
|
2.times { Post.connection.select_value(Post.all) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_select_values_with_cache
|
|
|
|
Post.cache do
|
|
|
|
assert_queries(1) do
|
|
|
|
2.times { Post.connection.select_values(Post.all) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_select_rows_with_cache
|
|
|
|
Post.cache do
|
|
|
|
assert_queries(1) do
|
|
|
|
2.times { Post.connection.select_rows(Post.all) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-07-24 23:48:30 -04:00
|
|
|
def test_query_cache_dups_results_correctly
|
|
|
|
Task.cache do
|
|
|
|
now = Time.now.utc
|
|
|
|
task = Task.find 1
|
|
|
|
assert_not_equal now, task.starting
|
|
|
|
task.starting = now
|
|
|
|
task.reload
|
|
|
|
assert_not_equal now, task.starting
|
|
|
|
end
|
|
|
|
end
|
2007-02-06 16:16:07 -05:00
|
|
|
|
2018-06-12 15:36:54 -04:00
|
|
|
def test_cache_notifications_can_be_overridden
|
|
|
|
logger = ShouldNotHaveExceptionsLogger.new
|
|
|
|
subscriber = ActiveSupport::Notifications.subscribe "sql.active_record", logger
|
|
|
|
|
|
|
|
connection = ActiveRecord::Base.connection.dup
|
|
|
|
|
|
|
|
def connection.cache_notification_info(sql, name, binds)
|
|
|
|
super.merge(neat: true)
|
|
|
|
end
|
|
|
|
|
|
|
|
connection.cache do
|
|
|
|
connection.select_all "select 1"
|
|
|
|
connection.select_all "select 1"
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_equal true, logger.events.last.payload[:neat]
|
|
|
|
ensure
|
|
|
|
ActiveSupport::Notifications.unsubscribe subscriber
|
|
|
|
end
|
|
|
|
|
2016-10-20 12:21:16 -04:00
|
|
|
def test_cache_does_not_raise_exceptions
|
|
|
|
logger = ShouldNotHaveExceptionsLogger.new
|
|
|
|
subscriber = ActiveSupport::Notifications.subscribe "sql.active_record", logger
|
|
|
|
|
|
|
|
ActiveRecord::Base.cache do
|
|
|
|
assert_queries(1) { Task.find(1); Task.find(1) }
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_not_predicate logger, :exception?
|
|
|
|
ensure
|
|
|
|
ActiveSupport::Notifications.unsubscribe subscriber
|
|
|
|
end
|
|
|
|
|
2016-12-12 16:51:39 -05:00
|
|
|
def test_query_cache_does_not_allow_sql_key_mutation
|
|
|
|
subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |_, _, _, _, payload|
|
|
|
|
payload[:sql].downcase!
|
|
|
|
end
|
|
|
|
|
2018-12-21 20:43:05 -05:00
|
|
|
assert_raises FrozenError do
|
2016-12-12 16:51:39 -05:00
|
|
|
ActiveRecord::Base.cache do
|
|
|
|
assert_queries(1) { Task.find(1); Task.find(1) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
ActiveSupport::Notifications.unsubscribe subscriber
|
|
|
|
end
|
|
|
|
|
2007-09-17 02:15:58 -04:00
|
|
|
def test_cache_is_flat
|
2007-02-06 16:16:07 -05:00
|
|
|
Task.cache do
|
2007-09-17 02:15:58 -04:00
|
|
|
assert_queries(1) { Topic.find(1); Topic.find(1); }
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
2007-09-17 02:15:58 -04:00
|
|
|
|
2007-02-20 18:42:04 -05:00
|
|
|
ActiveRecord::Base.cache do
|
2007-09-17 02:15:58 -04:00
|
|
|
assert_queries(1) { Task.find(1); Task.find(1) }
|
2007-02-20 18:42:04 -05:00
|
|
|
end
|
|
|
|
end
|
2007-09-17 02:15:58 -04:00
|
|
|
|
2017-09-01 08:18:56 -04:00
|
|
|
def test_cache_does_not_wrap_results_in_arrays
|
2007-09-17 02:15:58 -04:00
|
|
|
Task.cache do
|
2017-09-01 08:18:56 -04:00
|
|
|
if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
|
|
|
|
assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
2009-03-22 18:19:27 -04:00
|
|
|
else
|
|
|
|
assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
|
|
|
end
|
2007-02-21 16:54:41 -05:00
|
|
|
end
|
|
|
|
end
|
2012-07-06 07:03:49 -04:00
|
|
|
|
|
|
|
def test_cache_is_ignored_for_locked_relations
|
|
|
|
task = Task.find 1
|
|
|
|
|
|
|
|
Task.cache do
|
|
|
|
assert_queries(2) { task.lock!; task.lock! }
|
|
|
|
end
|
|
|
|
end
|
2012-10-30 13:21:52 -04:00
|
|
|
|
2018-02-17 03:25:08 -05:00
|
|
|
def test_cache_is_available_when_connection_is_connected
|
|
|
|
conf = ActiveRecord::Base.configurations
|
|
|
|
|
|
|
|
ActiveRecord::Base.configurations = {}
|
|
|
|
Task.cache do
|
|
|
|
assert_queries(1) { Task.find(1); Task.find(1) }
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.configurations = conf
|
|
|
|
end
|
|
|
|
|
2017-06-28 01:12:12 -04:00
|
|
|
def test_cache_is_available_when_using_a_not_connected_connection
|
Should use the same connection in using query cache
`test_cache_is_available_when_using_a_not_connected_connection` is
always failed if running only the test since #29609.
```
% ARCONN=mysql2 be ruby -w -Itest test/cases/query_cache_test.rb -n test_cache_is_available_when_using_a_not_connected_connection
Using mysql2
Run options: -n test_cache_is_available_when_using_a_not_connected_connection --seed 15043
F
Finished in 0.070519s, 14.1806 runs/s, 28.3612 assertions/s.
1) Failure:
QueryCacheTest#test_cache_is_available_when_using_a_not_connected_connection [test/cases/query_cache_test.rb:336]:
2 instead of 1 queries were executed.
Queries:
SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`id` = ? LIMIT ?
SET NAMES utf8 COLLATE utf8_unicode_ci, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483.
Expected: 1
Actual: 2
1 runs, 2 assertions, 1 failures, 0 errors, 0 skips
```
This failure is due to `LogSubscriber` will use not connected
`ActiveRecord::Base.connection` even if `Task.connection` is connected.
I fixed to always pass `type_casted_binds` to log subscriber to avoid
the issue.
2017-06-29 03:25:32 -04:00
|
|
|
skip "In-Memory DB can't test for using a not connected connection" if in_memory_db?
|
2017-01-31 09:36:15 -05:00
|
|
|
with_temporary_connection_pool do
|
|
|
|
spec_name = Task.connection_specification_name
|
|
|
|
conf = ActiveRecord::Base.configurations["arunit"].merge("name" => "test2")
|
|
|
|
ActiveRecord::Base.connection_handler.establish_connection(conf)
|
|
|
|
Task.connection_specification_name = "test2"
|
2018-01-25 18:14:09 -05:00
|
|
|
assert_not_predicate Task, :connected?
|
2016-06-14 12:31:08 -04:00
|
|
|
|
2017-01-31 09:36:15 -05:00
|
|
|
Task.cache do
|
2018-12-20 12:44:01 -05:00
|
|
|
assert_queries(1) { Task.find(1); Task.find(1) }
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.connection_handler.remove_connection(Task.connection_specification_name)
|
|
|
|
Task.connection_specification_name = spec_name
|
2017-01-31 09:36:15 -05:00
|
|
|
end
|
2016-06-14 12:31:08 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-06-21 11:30:00 -04:00
|
|
|
def test_query_cache_executes_new_queries_within_block
|
|
|
|
ActiveRecord::Base.connection.enable_query_cache!
|
|
|
|
|
|
|
|
# Warm up the cache by running the query
|
|
|
|
assert_queries(1) do
|
2017-01-05 00:40:24 -05:00
|
|
|
assert_equal 0, Post.where(title: "test").to_a.count
|
2016-06-21 11:30:00 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Check that if the same query is run again, no queries are executed
|
2018-10-04 14:07:12 -04:00
|
|
|
assert_no_queries do
|
2017-01-05 00:40:24 -05:00
|
|
|
assert_equal 0, Post.where(title: "test").to_a.count
|
2016-06-21 11:30:00 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
ActiveRecord::Base.connection.uncached do
|
|
|
|
# Check that new query is executed, avoiding the cache
|
|
|
|
assert_queries(1) do
|
2017-01-05 00:40:24 -05:00
|
|
|
assert_equal 0, Post.where(title: "test").to_a.count
|
2016-06-21 11:30:00 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-11-27 14:45:02 -05:00
|
|
|
def test_query_cache_doesnt_leak_cached_results_of_rolled_back_queries
|
|
|
|
ActiveRecord::Base.connection.enable_query_cache!
|
|
|
|
post = Post.first
|
|
|
|
|
|
|
|
Post.transaction do
|
2018-02-14 21:33:02 -05:00
|
|
|
post.update(title: "rollback")
|
2016-08-06 12:26:20 -04:00
|
|
|
assert_equal 1, Post.where(title: "rollback").to_a.count
|
2014-11-27 14:45:02 -05:00
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
end
|
|
|
|
|
2016-08-06 12:26:20 -04:00
|
|
|
assert_equal 0, Post.where(title: "rollback").to_a.count
|
2014-11-27 14:45:02 -05:00
|
|
|
|
|
|
|
ActiveRecord::Base.connection.uncached do
|
2016-08-06 12:26:20 -04:00
|
|
|
assert_equal 0, Post.where(title: "rollback").to_a.count
|
2014-11-27 14:45:02 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
begin
|
|
|
|
Post.transaction do
|
2018-02-14 21:33:02 -05:00
|
|
|
post.update(title: "rollback")
|
2016-08-06 12:26:20 -04:00
|
|
|
assert_equal 1, Post.where(title: "rollback").to_a.count
|
|
|
|
raise "broken"
|
2014-11-27 14:45:02 -05:00
|
|
|
end
|
2015-03-06 06:13:40 -05:00
|
|
|
rescue Exception
|
2014-11-27 14:45:02 -05:00
|
|
|
end
|
|
|
|
|
2016-08-06 12:26:20 -04:00
|
|
|
assert_equal 0, Post.where(title: "rollback").to_a.count
|
2014-11-27 14:45:02 -05:00
|
|
|
|
|
|
|
ActiveRecord::Base.connection.uncached do
|
2016-08-06 12:26:20 -04:00
|
|
|
assert_equal 0, Post.where(title: "rollback").to_a.count
|
2014-11-27 14:45:02 -05:00
|
|
|
end
|
|
|
|
end
|
2016-02-21 20:25:52 -05:00
|
|
|
|
2016-08-22 14:21:28 -04:00
|
|
|
def test_query_cached_even_when_types_are_reset
|
|
|
|
Task.cache do
|
|
|
|
# Warm the cache
|
2016-08-25 17:21:40 -04:00
|
|
|
Task.find(1)
|
2016-08-22 14:21:28 -04:00
|
|
|
|
|
|
|
# Preload the type cache again (so we don't have those queries issued during our assertions)
|
2017-07-20 06:46:16 -04:00
|
|
|
Task.connection.send(:reload_type_map)
|
2016-08-22 14:21:28 -04:00
|
|
|
|
|
|
|
# Clear places where type information is cached
|
|
|
|
Task.reset_column_information
|
|
|
|
Task.initialize_find_by_cache
|
2018-10-04 14:07:12 -04:00
|
|
|
Task.define_attribute_methods
|
2016-08-22 14:21:28 -04:00
|
|
|
|
2018-10-04 14:07:12 -04:00
|
|
|
assert_no_queries do
|
2016-08-22 14:21:28 -04:00
|
|
|
Task.find(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-11-06 02:19:57 -05:00
|
|
|
def test_query_cache_does_not_establish_connection_if_unconnected
|
2017-01-10 05:34:23 -05:00
|
|
|
with_temporary_connection_pool do
|
|
|
|
ActiveRecord::Base.clear_active_connections!
|
2018-01-24 22:04:11 -05:00
|
|
|
assert_not ActiveRecord::Base.connection_handler.active_connections? # sanity check
|
2016-11-06 02:19:57 -05:00
|
|
|
|
2017-01-10 05:34:23 -05:00
|
|
|
middleware {
|
2018-01-24 22:04:11 -05:00
|
|
|
assert_not ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup"
|
2017-01-10 05:34:23 -05:00
|
|
|
}.call({})
|
2016-11-06 02:19:57 -05:00
|
|
|
|
2018-01-24 22:04:11 -05:00
|
|
|
assert_not ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup"
|
2017-01-10 05:34:23 -05:00
|
|
|
end
|
2016-11-06 02:19:57 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_query_cache_is_enabled_on_connections_established_after_middleware_runs
|
2017-01-10 05:34:23 -05:00
|
|
|
with_temporary_connection_pool do
|
|
|
|
ActiveRecord::Base.clear_active_connections!
|
2018-01-24 22:04:11 -05:00
|
|
|
assert_not ActiveRecord::Base.connection_handler.active_connections? # sanity check
|
2016-11-06 02:19:57 -05:00
|
|
|
|
2017-01-10 05:34:23 -05:00
|
|
|
middleware {
|
2018-03-31 11:29:08 -04:00
|
|
|
assert_predicate ActiveRecord::Base.connection, :query_cache_enabled
|
2017-01-10 05:34:23 -05:00
|
|
|
}.call({})
|
2018-03-31 11:29:08 -04:00
|
|
|
assert_not_predicate ActiveRecord::Base.connection, :query_cache_enabled
|
2017-01-10 05:34:23 -05:00
|
|
|
end
|
2016-11-06 02:19:57 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_query_caching_is_local_to_the_current_thread
|
2017-01-10 05:34:23 -05:00
|
|
|
with_temporary_connection_pool do
|
|
|
|
ActiveRecord::Base.clear_active_connections!
|
2016-11-06 02:19:57 -05:00
|
|
|
|
2017-01-10 05:34:23 -05:00
|
|
|
middleware {
|
|
|
|
assert ActiveRecord::Base.connection_pool.query_cache_enabled
|
|
|
|
assert ActiveRecord::Base.connection.query_cache_enabled
|
2016-11-06 02:19:57 -05:00
|
|
|
|
2017-01-10 05:34:23 -05:00
|
|
|
Thread.new {
|
2018-01-24 22:04:11 -05:00
|
|
|
assert_not ActiveRecord::Base.connection_pool.query_cache_enabled
|
|
|
|
assert_not ActiveRecord::Base.connection.query_cache_enabled
|
2017-01-10 05:34:23 -05:00
|
|
|
}.join
|
|
|
|
}.call({})
|
|
|
|
end
|
2016-11-06 02:19:57 -05:00
|
|
|
end
|
|
|
|
|
2017-04-24 18:26:13 -04:00
|
|
|
def test_query_cache_is_enabled_on_all_connection_pools
|
|
|
|
middleware {
|
|
|
|
ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
|
|
|
|
assert pool.query_cache_enabled
|
|
|
|
assert pool.connection.query_cache_enabled
|
|
|
|
end
|
|
|
|
}.call({})
|
|
|
|
end
|
|
|
|
|
2019-01-29 13:18:58 -05:00
|
|
|
def test_clear_query_cache_is_called_on_all_connections
|
|
|
|
skip "with in memory db, reading role won't be able to see database on writing role" if in_memory_db?
|
|
|
|
with_temporary_connection_pool do
|
|
|
|
ActiveRecord::Base.connection_handlers = {
|
|
|
|
writing: ActiveRecord::Base.default_connection_handler,
|
|
|
|
reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
|
|
|
}
|
|
|
|
|
|
|
|
ActiveRecord::Base.connected_to(role: :reading) do
|
|
|
|
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["arunit"])
|
|
|
|
end
|
|
|
|
|
|
|
|
mw = middleware { |env|
|
|
|
|
ActiveRecord::Base.connected_to(role: :reading) do
|
|
|
|
@topic = Topic.first
|
|
|
|
end
|
|
|
|
|
|
|
|
assert @topic
|
|
|
|
|
|
|
|
ActiveRecord::Base.connected_to(role: :writing) do
|
|
|
|
@topic.title = "It doesn't have to be crazy at work"
|
|
|
|
@topic.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_equal "It doesn't have to be crazy at work", @topic.title
|
|
|
|
|
|
|
|
ActiveRecord::Base.connected_to(role: :reading) do
|
|
|
|
@topic = Topic.first
|
|
|
|
assert_equal "It doesn't have to be crazy at work", @topic.title
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
mw.call({})
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
|
|
|
|
end
|
|
|
|
|
2016-02-21 20:25:52 -05:00
|
|
|
private
|
2018-09-21 16:19:42 -04:00
|
|
|
|
|
|
|
def with_temporary_connection_pool
|
|
|
|
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
|
|
|
|
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
|
|
|
|
ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = new_pool
|
|
|
|
|
|
|
|
yield
|
|
|
|
ensure
|
|
|
|
ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = old_pool
|
|
|
|
end
|
|
|
|
|
2016-02-21 20:25:52 -05:00
|
|
|
def middleware(&app)
|
|
|
|
executor = Class.new(ActiveSupport::Executor)
|
|
|
|
ActiveRecord::QueryCache.install_executor_hooks executor
|
|
|
|
lambda { |env| executor.wrap { app.call(env) } }
|
|
|
|
end
|
2016-10-24 14:53:00 -04:00
|
|
|
|
|
|
|
def assert_cache(state, connection = ActiveRecord::Base.connection)
|
|
|
|
case state
|
|
|
|
when :off
|
2018-05-12 22:26:10 -04:00
|
|
|
assert_not connection.query_cache_enabled, "cache should be off"
|
2016-10-24 14:53:00 -04:00
|
|
|
assert connection.query_cache.empty?, "cache should be empty"
|
|
|
|
when :clean
|
|
|
|
assert connection.query_cache_enabled, "cache should be on"
|
|
|
|
assert connection.query_cache.empty?, "cache should be empty"
|
|
|
|
when :dirty
|
|
|
|
assert connection.query_cache_enabled, "cache should be on"
|
2018-05-12 22:26:10 -04:00
|
|
|
assert_not connection.query_cache.empty?, "cache should be dirty"
|
2016-10-24 14:53:00 -04:00
|
|
|
else
|
|
|
|
raise "unknown state"
|
|
|
|
end
|
|
|
|
end
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
|
2008-01-21 12:20:51 -05:00
|
|
|
class QueryCacheExpiryTest < ActiveRecord::TestCase
|
2008-07-31 16:59:53 -04:00
|
|
|
fixtures :tasks, :posts, :categories, :categories_posts
|
2007-02-06 16:16:07 -05:00
|
|
|
|
2017-01-18 07:47:11 -05:00
|
|
|
def teardown
|
|
|
|
Task.connection.clear_query_cache
|
|
|
|
end
|
|
|
|
|
2011-10-27 11:25:42 -04:00
|
|
|
def test_cache_gets_cleared_after_migration
|
|
|
|
# warm the cache
|
|
|
|
Post.find(1)
|
|
|
|
|
|
|
|
# change the column definition
|
2014-01-15 08:08:45 -05:00
|
|
|
Post.connection.change_column :posts, :title, :string, limit: 80
|
2011-10-27 11:25:42 -04:00
|
|
|
assert_nothing_raised { Post.find(1) }
|
|
|
|
|
|
|
|
# restore the old definition
|
|
|
|
Post.connection.change_column :posts, :title, :string
|
|
|
|
end
|
|
|
|
|
2007-02-06 16:16:07 -05:00
|
|
|
def test_find
|
2015-08-15 15:35:42 -04:00
|
|
|
assert_called(Task.connection, :clear_query_cache) do
|
2018-04-17 18:21:34 -04:00
|
|
|
assert_not Task.connection.query_cache_enabled
|
2015-08-15 15:35:42 -04:00
|
|
|
Task.cache do
|
|
|
|
assert Task.connection.query_cache_enabled
|
|
|
|
Task.find(1)
|
2007-09-17 02:15:58 -04:00
|
|
|
|
2015-08-15 15:35:42 -04:00
|
|
|
Task.uncached do
|
2018-04-17 18:21:34 -04:00
|
|
|
assert_not Task.connection.query_cache_enabled
|
2015-08-15 15:35:42 -04:00
|
|
|
Task.find(1)
|
|
|
|
end
|
2007-09-17 02:15:58 -04:00
|
|
|
|
2015-08-15 15:35:42 -04:00
|
|
|
assert Task.connection.query_cache_enabled
|
2007-09-17 02:15:58 -04:00
|
|
|
end
|
2018-04-17 18:21:34 -04:00
|
|
|
assert_not Task.connection.query_cache_enabled
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-09-17 02:15:58 -04:00
|
|
|
def test_update
|
2015-08-15 15:35:42 -04:00
|
|
|
assert_called(Task.connection, :clear_query_cache, times: 2) do
|
|
|
|
Task.cache do
|
|
|
|
task = Task.find(1)
|
|
|
|
task.starting = Time.now.utc
|
|
|
|
task.save!
|
|
|
|
end
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_destroy
|
2015-08-15 15:35:42 -04:00
|
|
|
assert_called(Task.connection, :clear_query_cache, times: 2) do
|
|
|
|
Task.cache do
|
|
|
|
Task.find(1).destroy
|
|
|
|
end
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-09-17 02:15:58 -04:00
|
|
|
def test_insert
|
2015-08-15 15:35:42 -04:00
|
|
|
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
|
|
|
Task.cache do
|
|
|
|
Task.create!
|
|
|
|
end
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|
|
|
|
end
|
2008-01-17 20:55:11 -05:00
|
|
|
|
|
|
|
def test_cache_is_expired_by_habtm_update
|
2015-08-15 15:35:42 -04:00
|
|
|
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
|
|
|
ActiveRecord::Base.cache do
|
|
|
|
c = Category.first
|
|
|
|
p = Post.first
|
|
|
|
p.categories << c
|
|
|
|
end
|
2008-01-17 20:55:11 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_cache_is_expired_by_habtm_delete
|
2015-08-15 15:35:42 -04:00
|
|
|
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
|
|
|
ActiveRecord::Base.cache do
|
|
|
|
p = Post.find(1)
|
2018-01-25 18:14:09 -05:00
|
|
|
assert_predicate p.categories, :any?
|
2015-08-15 15:35:42 -04:00
|
|
|
p.categories.delete_all
|
|
|
|
end
|
2008-01-17 20:55:11 -05:00
|
|
|
end
|
|
|
|
end
|
2017-02-20 13:35:19 -05:00
|
|
|
|
|
|
|
test "threads use the same connection" do
|
|
|
|
@connection_1 = ActiveRecord::Base.connection.object_id
|
|
|
|
|
|
|
|
thread_a = Thread.new do
|
|
|
|
@connection_2 = ActiveRecord::Base.connection.object_id
|
|
|
|
end
|
|
|
|
|
|
|
|
thread_a.join
|
|
|
|
|
|
|
|
assert_equal @connection_1, @connection_2
|
|
|
|
end
|
2007-02-06 16:16:07 -05:00
|
|
|
end
|