module Mongo::QueryCache

Public Class Methods

cache() { || ... } click to toggle source

Execute the block while using the query cache.

@example Execute with the cache.

QueryCache.cache { collection.find }

@return [ Object ] The result of the block.

# File lib/mongo/query_cache.rb, line 45
def cache
  enabled = enabled?
  self.enabled = true
  begin
    yield
  ensure
    self.enabled = enabled
  end
end
clear() click to toggle source

Clear the query cache.

@example Clear the cache.

QueryCache.clear

@return [ nil ] Always nil.

# File lib/mongo/query_cache.rb, line 87
def clear
  Thread.current["[mongo]:query_cache"] = nil
end
clear_namespace(namespace) click to toggle source

Clear the section of the query cache storing cursors with results from this namespace.

@param [ String ] namespace The namespace to be cleared, in the format

"database.collection".

@return [ nil ] Always nil.

@api private

# File lib/mongo/query_cache.rb, line 100
def clear_namespace(namespace)
  cache_table.delete(namespace)
  # The nil key is where cursors are stored that could potentially read from
  # multiple collections. This key should be cleared on every write operation
  # to prevent returning stale data.
  cache_table.delete(nil)
  nil
end
enabled=(value) click to toggle source

Set whether the cache is enabled.

@example Set if the cache is enabled.

QueryCache.enabled = true

@param [ true, false ] value The enabled value.

# File lib/mongo/query_cache.rb, line 25
def enabled=(value)
  Thread.current["[mongo]:query_cache:enabled"] = value
end
enabled?() click to toggle source

Is the query cache enabled on the current thread?

@example Is the query cache enabled?

QueryCache.enabled?

@return [ true, false ] If the cache is enabled.

# File lib/mongo/query_cache.rb, line 35
def enabled?
  !!Thread.current["[mongo]:query_cache:enabled"]
end
get(**opts) click to toggle source

For the given query options, retrieve a cached cursor that can be used to obtain the correct query results, if one exists in the cache.

@option opts [ String | nil ] namespace The namespace of the query,

in the format "database_name.collection_name".

@option opts [ Array, Hash ] selector The selector passed to the query.

For most queries, this will be a Hash, but for aggregations, this
will be an Array representing the aggregation pipeline. May not be nil.

@option opts [ Integer | nil ] skip The skip value of the query. @option opts [ Hash | nil ] sort The order of the query results

(e.g. { name: -1 }).

@option opts [ Integer | nil ] limit The limit value of the query. @option opts [ Hash | nil ] projection The projection of the query

results (e.g. { name: 1 }).

@option opts [ Hash | nil ] collation The collation of the query

(e.g. { "locale" => "fr_CA" }).

@option opts [ Hash | nil ] read_concern The read concern of the query

(e.g. { level: :majority }).

@option opts [ Hash | nil ] read_preference The read preference of

the query (e.g. { mode: :secondary }).

@option opts [ Boolean | nil ] multi_collection Whether the query

results could potentially come from multiple collections. When true,
these results will be stored under the nil namespace key and cleared
on every write command.

@return [ Mongo::CachingCursor | nil ] Returns a CachingCursor if one

exists in the query cache, otherwise returns nil.

@api private

# File lib/mongo/query_cache.rb, line 178
def get(**opts)
  limit = opts[:limit]
  _namespace_key = namespace_key(**opts)
  _cache_key = cache_key(**opts)

  namespace_hash = cache_table[_namespace_key]
  return nil unless namespace_hash

  caching_cursor = namespace_hash[_cache_key]
  return nil unless caching_cursor

  caching_cursor_limit = caching_cursor.view.limit

  # There are two scenarios in which a caching cursor could fulfill the
  # query:
  # 1. The query has a limit, and the stored cursor has no limit or
  #    a larger limit.
  # 2. The query has no limit and the stored cursor has no limit.
  #
  # Otherwise, return nil because the stored cursor will not satisfy
  # the query.
  if limit && (caching_cursor_limit.nil? || caching_cursor_limit >= limit)
    caching_cursor
  elsif limit.nil? && caching_cursor_limit.nil?
    caching_cursor
  else
    nil
  end
end
set(cursor, **opts) click to toggle source

Store a CachingCursor instance in the query cache associated with the specified query options.

@param [ Mongo::CachingCursor ] cursor The CachingCursor instance to store.

@option opts [ String | nil ] namespace The namespace of the query,

in the format "database_name.collection_name".

@option opts [ Array, Hash ] selector The selector passed to the query.

For most queries, this will be a Hash, but for aggregations, this
will be an Array representing the aggregation pipeline. May not be nil.

@option opts [ Integer | nil ] skip The skip value of the query. @option opts [ Hash | nil ] sort The order of the query results

(e.g. { name: -1 }).

@option opts [ Integer | nil ] limit The limit value of the query. @option opts [ Hash | nil ] projection The projection of the query

results (e.g. { name: 1 }).

@option opts [ Hash | nil ] collation The collation of the query

(e.g. { "locale" => "fr_CA" }).

@option opts [ Hash | nil ] read_concern The read concern of the query

(e.g. { level: :majority }).

@option opts [ Hash | nil ] read_preference The read preference of

the query (e.g. { mode: :secondary }).

@option opts [ Boolean | nil ] multi_collection Whether the query

results could potentially come from multiple collections. When true,
these results will be stored under the nil namespace key and cleared
on every write command.

@return [ true ] Always true.

@api private

# File lib/mongo/query_cache.rb, line 139
def set(cursor, **opts)
  _cache_key = cache_key(**opts)
  _namespace_key = namespace_key(**opts)

  cache_table[_namespace_key] ||= {}
  cache_table[_namespace_key][_cache_key] = cursor

  true
end
uncached() { || ... } click to toggle source

Execute the block with the query cache disabled.

@example Execute without the cache.

QueryCache.uncached { collection.find }

@return [ Object ] The result of the block.

# File lib/mongo/query_cache.rb, line 61
def uncached
  enabled = enabled?
  self.enabled = false
  begin
    yield
  ensure
    self.enabled = enabled
  end
end

Private Class Methods

cache_key(**opts) click to toggle source
# File lib/mongo/query_cache.rb, line 210
def cache_key(**opts)
  unless opts[:namespace]
    raise ArgumentError.new("Cannot generate cache key without namespace")
  end
  unless opts[:selector]
    raise ArgumentError.new("Cannot generate cache key without selector")
  end

  [
    opts[:namespace],
    opts[:selector],
    opts[:skip],
    opts[:sort],
    opts[:projection],
    opts[:collation],
    opts[:read_concern],
    opts[:read_preference]
  ]
end
cache_table() click to toggle source

Get the cached queries.

@example Get the cached queries from the current thread.

QueryCache.cache_table

@return [ Hash ] The hash of cached queries.

# File lib/mongo/query_cache.rb, line 77
        def cache_table
  Thread.current["[mongo]:query_cache"] ||= {}
end
namespace_key(**opts) click to toggle source

If the cached results can come from multiple collections, store this cursor under the nil namespace to be cleared on every write operation. Otherwise, store it under the specified namespace.

# File lib/mongo/query_cache.rb, line 233
def namespace_key(**opts)
  if opts[:multi_collection]
    nil
  else
    opts[:namespace]
  end
end