module ActiveRecord::ConnectionAdapters::QueryCache
def cache(&block)
def cache(&block) pool.enable_query_cache(&block) end
def cache_notification_info(sql, name, binds)
Database adapters can override this method to
def cache_notification_info(sql, name, binds) { sql: sql, binds: binds, type_casted_binds: -> { type_casted_binds(binds) }, name: name, connection: self, transaction: current_transaction.user_transaction.presence, cached: true } end
def cache_notification_info_result(sql, name, binds, result)
def cache_notification_info_result(sql, name, binds, result) payload = cache_notification_info(sql, name, binds) payload[:row_count] = result.length payload end
def cache_sql(sql, name, binds)
def cache_sql(sql, name, binds) key = binds.empty? ? sql : [sql, binds] result = nil hit = true @lock.synchronize do result = @query_cache.compute_if_absent(key) do hit = false yield end end if hit ActiveSupport::Notifications.instrument( "sql.active_record", cache_notification_info_result(sql, name, binds, result) ) end result.dup end
def clear_query_cache
the same SQL query and repeatedly return the same result each time, silently
that ask the database to randomize results. Otherwise the cache would see
One reason you may wish to call this method explicitly is between queries
Clears the query cache.
def clear_query_cache pool.clear_query_cache end
def dirties_query_cache(base, *method_names)
def dirties_query_cache(base, *method_names) method_names.each do |method_name| base.class_eval <<-end_code, __FILE__, __LINE__ + 1 def #{method_name}(...) if pool.dirties_query_cache ActiveRecord::Base.clear_query_caches_for_current_thread end super end end_code end end
def disable_query_cache!
def disable_query_cache! pool.disable_query_cache! end
def enable_query_cache!
def enable_query_cache! pool.enable_query_cache! end
def included(base) # :nodoc:
def included(base) # :nodoc: dirties_query_cache base, :exec_query, :execute, :create, :insert, :update, :delete, :truncate, :truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction, :exec_insert_all base.set_callback :checkin, :after, :unset_query_cache! end
def initialize(*)
def initialize(*) super @query_cache = nil end
def lookup_sql_cache(sql, name, binds)
def lookup_sql_cache(sql, name, binds) key = binds.empty? ? sql : [sql, binds] result = nil @lock.synchronize do result = @query_cache[key] end if result ActiveSupport::Notifications.instrument( "sql.active_record", cache_notification_info_result(sql, name, binds, result) ) end result end
def query_cache_enabled
def query_cache_enabled @query_cache&.enabled? end
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc: arel = arel_from_relation(arel) # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. # Such queries should not be cached. if @query_cache&.enabled? && !(arel.respond_to?(:locked) && arel.locked) sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable) if async result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry) FutureResult.wrap(result) else cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry) } end else super end end
def uncached(dirties: true, &block)
Set dirties: false to prevent query caches on all connections from being cleared by write operations.
Disable the query cache within the block.
def uncached(dirties: true, &block) pool.disable_query_cache(dirties: dirties, &block) end
def unset_query_cache!
def unset_query_cache! @query_cache = nil end