class Mongoid::Contextual::Mongo::DocumentsLoader

Loads documents for the provided criteria.

@api private

Attributes

criteria[RW]

@return [ Mongoid::Criteria ] Criteria that specifies which documents should

be loaded. Exposed here because `eager_loadable?` method from
`Association::EagerLoadable` expects this to be available.

Public Class Methods

executor(name = Mongoid.async_query_executor) click to toggle source

Returns suitable executor according to Mongoid config options.

@param [ String | Symbol] name The query executor name, can be either

:immediate or :global_thread_pool. Defaulted to `async_query_executor`
config option.

@return [ Concurrent::ImmediateExecutor | Concurrent::ThreadPoolExecutor ] The executor

to be used to execute document loading tasks.

@raise [ Errors::InvalidQueryExecutor ] If an unknown name is provided.

# File lib/mongoid/contextual/mongo/documents_loader.rb, line 61
def self.executor(name = Mongoid.async_query_executor)
  case name.to_sym
  when :immediate
    immediate_executor
  when :global_thread_pool
    global_thread_pool_async_query_executor
  else
    raise Errors::InvalidQueryExecutor.new(name)
  end
end
global_thread_pool_async_query_executor() click to toggle source

Returns asynchronous executor to be used when async_query_executor config option is set to :global_thread_pool. This executor runs operations on background threads using a thread pool.

@return [ Concurrent::ThreadPoolExecutor ] The executor

to be used to execute document loading tasks.
# File lib/mongoid/contextual/mongo/documents_loader.rb, line 32
def self.global_thread_pool_async_query_executor
  create_pool = Proc.new do |concurrency|
    Concurrent::ThreadPoolExecutor.new(
      min_threads: 0,
      max_threads: concurrency,
      max_queue: concurrency * 4,
      fallback_policy: :caller_runs
    )
  end
  concurrency = Mongoid.global_executor_concurrency || 4
  @@global_thread_pool_async_query_executor ||= create_pool.call(concurrency)
  if @@global_thread_pool_async_query_executor.max_length != concurrency
    old_pool = @@global_thread_pool_async_query_executor
    @@global_thread_pool_async_query_executor = create_pool.call(concurrency)
    old_pool.shutdown
  end
  @@global_thread_pool_async_query_executor
end
immediate_executor() click to toggle source

Returns synchronous executor to be used when async_query_executor config option is set to :immediate. This executor runs all operations on the current thread, blocking as necessary.

@return [ Concurrent::ImmediateExecutor ] The executor

to be used to execute document loading tasks.
# File lib/mongoid/contextual/mongo/documents_loader.rb, line 22
def self.immediate_executor
  @@immediate_executor ||= Concurrent::ImmediateExecutor.new
end
new(view, klass, criteria, executor: self.class.executor) click to toggle source

Instantiates the document loader instance and immediately schedules its execution using the provided executor.

@param [ Mongo::Collection::View ] view The collection view to get

records from the database.

@param [ Class ] klass Mongoid model class to instantiate documents.

All records obtained from the database will be converted to an
instance of this class, if possible.

@param [ Mongoid::Criteria ] criteria Criteria that specifies which

documents should be loaded.

@param [ Concurrent::AbstractExecutorService ] executor Executor that

is capable of running `Concurrent::Promises::Future` instances.
# File lib/mongoid/contextual/mongo/documents_loader.rb, line 89
def initialize(view, klass, criteria, executor: self.class.executor)
  @view = view
  @klass = klass
  @criteria = criteria
  @mutex = Mutex.new
  @state = :pending
  @future = Concurrent::Promises.future_on(executor) do
    start && execute
  end
end

Public Instance Methods

execute() click to toggle source

Loads records specified by ‘@criteria` from the database, and convert them to Mongoid documents of `@klass` type.

This method is called by the task (possibly asynchronous) scheduled when creating an instance of the loader. However, this method can be called directly, if it is desired to execute loading on the caller thread immediately.

Calling this method does not change the state of the loader.

@return [ Array<Mongoid::Document> ] Array of document loaded from

the database.
# File lib/mongoid/contextual/mongo/documents_loader.rb, line 152
def execute
  documents = @view.map do |doc|
    Factory.from_db(@klass, doc, @criteria)
  end
  eager_load(documents) if eager_loadable?
  documents
end
pending?() click to toggle source

Returns false or true whether the loader is in pending state.

Pending state means that the loader execution has been scheduled, but has not been started yet.

@return [ true | false ] true if the loader is in pending state,

otherwise false.
# File lib/mongoid/contextual/mongo/documents_loader.rb, line 107
def pending?
  @mutex.synchronize do
    @state == :pending
  end
end
started?() click to toggle source

Returns false or true whether the loader is in started state.

Started state means that the loader execution has been started. Note that the loader stays in this state even after the execution completed (successfully or failed).

@return [ true | false ] true if the loader is in started state,

otherwise false.
# File lib/mongoid/contextual/mongo/documents_loader.rb, line 121
def started?
  @mutex.synchronize do
    @state == :started
  end
end
unschedule() click to toggle source

Mark the loader as unscheduled.

If the loader is marked unscheduled, it will not be executed. The only option to load the documents is to call ‘execute` method directly.

Please note that if execution of a task has been already started, unscheduling does not have any effect.

# File lib/mongoid/contextual/mongo/documents_loader.rb, line 134
def unschedule
  @mutex.synchronize do
    @state = :cancelled unless @state == :started
  end
end

Private Instance Methods

start() click to toggle source

Mark the loader as started if possible.

@return [ true | false ] Whether the state was changed to :started.

# File lib/mongoid/contextual/mongo/documents_loader.rb, line 165
def start
  @mutex.synchronize do
    if @state == :pending
      @state = :started
      true
    else
      false
    end
  end
end