class QueryCacheTest

Public Instance Methods

teardown() click to toggle source
Calls superclass method
# File activerecord/test/cases/query_cache_test.rb, line 35
def teardown
  Task.connection.clear_query_cache
  ActiveRecord::Base.connection.disable_query_cache!
  super
end
test_cache_does_not_raise_exceptions() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 268
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
test_cache_does_not_wrap_string_results_in_arrays() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 305
def test_cache_does_not_wrap_string_results_in_arrays
  Task.cache do
    # Oracle adapter returns count() as Integer or Float
    if current_adapter?(:OracleAdapter)
      assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
    elsif current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter)
      # Future versions of the sqlite3 adapter will return numeric
      assert_instance_of 0.class, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
    else
      assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
    end
  end
end
test_cache_enabled_during_call() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 162
def test_cache_enabled_during_call
  assert_cache :off

  mw = middleware { |env|
    assert_cache :clean
    [200, {}, nil]
  }
  mw.call({})
end
test_cache_is_available_when_using_a_not_connected_connection() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 327
def test_cache_is_available_when_using_a_not_connected_connection
  skip "In-Memory DB can't test for using a not connected connection" if in_memory_db?
  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"
    refute Task.connected?

    Task.cache do
      begin
        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
      end
    end
  end
end
test_cache_is_flat() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 295
def test_cache_is_flat
  Task.cache do
    assert_queries(1) { Topic.find(1); Topic.find(1); }
  end

  ActiveRecord::Base.cache do
    assert_queries(1) { Task.find(1); Task.find(1) }
  end
end
test_cache_is_ignored_for_locked_relations() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 319
def test_cache_is_ignored_for_locked_relations
  task = Task.find 1

  Task.cache do
    assert_queries(2) { task.lock!; task.lock! }
  end
end
test_cache_passing_a_relation() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 172
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
test_count_queries_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 205
def test_count_queries_with_cache
  Task.cache do
    assert_queries(1) { Task.count; Task.count }
  end
end
test_exceptional_middleware_clears_and_disables_cache_on_error() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 41
def test_exceptional_middleware_clears_and_disables_cache_on_error
  assert_cache :off

  mw = middleware { |env|
    Task.find 1
    Task.find 1
    query_cache = ActiveRecord::Base.connection.query_cache
    assert_equal 1, query_cache.length, query_cache.keys
    raise "lol borked"
  }
  assert_raises(RuntimeError) { mw.call({}) }

  assert_cache :off
end
test_exists_queries_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 211
def test_exists_queries_with_cache
  Post.cache do
    assert_queries(1) { Post.exists?; Post.exists? }
  end
end
test_find_queries() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 180
def test_find_queries
  assert_queries(2) { Task.find(1); Task.find(1) }
end
test_find_queries_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 184
def test_find_queries_with_cache
  Task.cache do
    assert_queries(1) { Task.find(1); Task.find(1) }
  end
end
test_find_queries_with_cache_multi_record() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 190
def test_find_queries_with_cache_multi_record
  Task.cache do
    assert_queries(2) { Task.find(1); Task.find(1); Task.find(2) }
  end
end
test_find_queries_with_multi_cache_blocks() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 196
def test_find_queries_with_multi_cache_blocks
  Task.cache do
    Task.cache do
      assert_queries(2) { Task.find(1); Task.find(2) }
    end
    assert_queries(0) { Task.find(1); Task.find(1); Task.find(2) }
  end
end
test_middleware_caches() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 151
def test_middleware_caches
  mw = middleware { |env|
    Task.find 1
    Task.find 1
    query_cache = ActiveRecord::Base.connection.query_cache
    assert_equal 1, query_cache.length, query_cache.keys
    [200, {}, nil]
  }
  mw.call({})
end
test_middleware_delegates() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 141
def test_middleware_delegates
  called = false
  mw = middleware { |env|
    called = true
    [200, {}, nil]
  }
  mw.call({})
  assert called, "middleware should delegate"
end
test_query_cache_across_threads() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 66
def test_query_cache_across_threads
  with_temporary_connection_pool do
    begin
      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
          end
        end
        ActiveRecord::FixtureSet.create_fixtures(self.class.fixture_path, ["tasks"], {}, ActiveRecord::Base)
      end

      ActiveRecord::Base.connection_pool.connections.each do |conn|
        assert_cache :off, conn
      end

      assert !ActiveRecord::Base.connection.nil?
      assert_cache :off

      middleware {
        assert_cache :clean

        Task.find 1
        assert_cache :dirty

        thread_1_connection = ActiveRecord::Base.connection
        ActiveRecord::Base.clear_active_connections!
        assert_cache :off, thread_1_connection

        started = Concurrent::Event.new
        checked = Concurrent::Event.new

        thread_2_connection = nil
        thread = Thread.new {
          thread_2_connection = ActiveRecord::Base.connection

          assert_equal thread_2_connection, thread_1_connection
          assert_cache :off

          middleware {
            assert_cache :clean

            Task.find 1
            assert_cache :dirty

            started.set
            checked.wait

            ActiveRecord::Base.clear_active_connections!
          }.call({})
        }

        started.wait

        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

        assert_cache :off, thread_2_connection
      }.call({})

      ActiveRecord::Base.connection_pool.connections.each do |conn|
        assert_cache :off, conn
      end
    ensure
      ActiveRecord::Base.connection_pool.disconnect!
    end
  end
end
test_query_cache_does_not_allow_sql_key_mutation() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 281
def test_query_cache_does_not_allow_sql_key_mutation
  subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |_, _, _, _, payload|
    payload[:sql].downcase!
  end

  assert_raises RuntimeError do
    ActiveRecord::Base.cache do
      assert_queries(1) { Task.find(1); Task.find(1) }
    end
  end
ensure
  ActiveSupport::Notifications.unsubscribe subscriber
end
test_query_cache_does_not_establish_connection_if_unconnected() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 418
def test_query_cache_does_not_establish_connection_if_unconnected
  with_temporary_connection_pool do
    ActiveRecord::Base.clear_active_connections!
    refute ActiveRecord::Base.connection_handler.active_connections? # sanity check

    middleware {
      refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup"
    }.call({})

    refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup"
  end
end
test_query_cache_doesnt_leak_cached_results_of_rolled_back_queries() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 368
def test_query_cache_doesnt_leak_cached_results_of_rolled_back_queries
  ActiveRecord::Base.connection.enable_query_cache!
  post = Post.first

  Post.transaction do
    post.update_attributes(title: "rollback")
    assert_equal 1, Post.where(title: "rollback").to_a.count
    raise ActiveRecord::Rollback
  end

  assert_equal 0, Post.where(title: "rollback").to_a.count

  ActiveRecord::Base.connection.uncached do
    assert_equal 0, Post.where(title: "rollback").to_a.count
  end

  begin
    Post.transaction do
      post.update_attributes(title: "rollback")
      assert_equal 1, Post.where(title: "rollback").to_a.count
      raise "broken"
    end
  rescue Exception
  end

  assert_equal 0, Post.where(title: "rollback").to_a.count

  ActiveRecord::Base.connection.uncached do
    assert_equal 0, Post.where(title: "rollback").to_a.count
  end
end
test_query_cache_dups_results_correctly() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 257
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
test_query_cache_executes_new_queries_within_block() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 347
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
    assert_equal 0, Post.where(title: "test").to_a.count
  end

  # Check that if the same query is run again, no queries are executed
  assert_queries(0) do
    assert_equal 0, Post.where(title: "test").to_a.count
  end

  ActiveRecord::Base.connection.uncached do
    # Check that new query is executed, avoiding the cache
    assert_queries(1) do
      assert_equal 0, Post.where(title: "test").to_a.count
    end
  end
end
test_query_cache_is_enabled_on_connections_established_after_middleware_runs() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 431
def test_query_cache_is_enabled_on_connections_established_after_middleware_runs
  with_temporary_connection_pool do
    ActiveRecord::Base.clear_active_connections!
    refute ActiveRecord::Base.connection_handler.active_connections? # sanity check

    middleware {
      assert ActiveRecord::Base.connection.query_cache_enabled, "QueryCache did not get lazily enabled"
    }.call({})
  end
end
test_query_cached_even_when_types_are_reset() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 400
def test_query_cached_even_when_types_are_reset
  Task.cache do
    # Warm the cache
    Task.find(1)

    # Preload the type cache again (so we don't have those queries issued during our assertions)
    Task.connection.send(:reload_type_map)

    # Clear places where type information is cached
    Task.reset_column_information
    Task.initialize_find_by_cache

    assert_queries(0) do
      Task.find(1)
    end
  end
end
test_query_caching_is_local_to_the_current_thread() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 442
def test_query_caching_is_local_to_the_current_thread
  with_temporary_connection_pool do
    ActiveRecord::Base.clear_active_connections!

    middleware {
      assert ActiveRecord::Base.connection_pool.query_cache_enabled
      assert ActiveRecord::Base.connection.query_cache_enabled

      Thread.new {
        refute ActiveRecord::Base.connection_pool.query_cache_enabled
        refute ActiveRecord::Base.connection.query_cache_enabled
      }.join
    }.call({})

  end
end
test_select_all_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 217
def test_select_all_with_cache
  Post.cache do
    assert_queries(1) do
      2.times { Post.connection.select_all(Post.all) }
    end
  end
end
test_select_one_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 225
def test_select_one_with_cache
  Post.cache do
    assert_queries(1) do
      2.times { Post.connection.select_one(Post.all) }
    end
  end
end
test_select_rows_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 249
def test_select_rows_with_cache
  Post.cache do
    assert_queries(1) do
      2.times { Post.connection.select_rows(Post.all) }
    end
  end
end
test_select_value_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 233
def test_select_value_with_cache
  Post.cache do
    assert_queries(1) do
      2.times { Post.connection.select_value(Post.all) }
    end
  end
end
test_select_values_with_cache() click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 241
def test_select_values_with_cache
  Post.cache do
    assert_queries(1) do
      2.times { Post.connection.select_values(Post.all) }
    end
  end
end

Private Instance Methods

assert_cache(state, connection = ActiveRecord::Base.connection) click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 466
def assert_cache(state, connection = ActiveRecord::Base.connection)
  case state
  when :off
    assert !connection.query_cache_enabled, "cache should be off"
    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"
    assert !connection.query_cache.empty?, "cache should be dirty"
  else
    raise "unknown state"
  end
end
middleware(&app) click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 460
def middleware(&app)
  executor = Class.new(ActiveSupport::Executor)
  ActiveRecord::QueryCache.install_executor_hooks executor
  lambda { |env| executor.wrap { app.call(env) } }
end
with_temporary_connection_pool() { || ... } click to toggle source
# File activerecord/test/cases/query_cache_test.rb, line 56
        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