summaryrefslogtreecommitdiff
path: root/src/mongo/db/query
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2014-10-08 17:18:30 -0400
committerDavid Storch <david.storch@10gen.com>2014-10-14 10:30:51 -0400
commiteba8e42b691f704c523fcec1ab5acb9aee8666c3 (patch)
tree9fe1b69ba9b05b83980275ffcab5ceb30b6957e3 /src/mongo/db/query
parent282cf4fee1cc3a7660360466c923f4cfaee87499 (diff)
downloadmongo-eba8e42b691f704c523fcec1ab5acb9aee8666c3.tar.gz
SERVER-15541 add PlanExecutor yield policies and registrations
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r--src/mongo/db/query/internal_plans.h11
-rw-r--r--src/mongo/db/query/new_find.cpp13
-rw-r--r--src/mongo/db/query/plan_executor.cpp37
-rw-r--r--src/mongo/db/query/plan_executor.h90
4 files changed, 97 insertions, 54 deletions
diff --git a/src/mongo/db/query/internal_plans.h b/src/mongo/db/query/internal_plans.h
index b623e06aee5..c16aa251f2d 100644
--- a/src/mongo/db/query/internal_plans.h
+++ b/src/mongo/db/query/internal_plans.h
@@ -90,10 +90,8 @@ namespace mongo {
}
CollectionScan* cs = new CollectionScan(txn, params, ws, NULL);
- PlanExecutor* exec = new PlanExecutor(txn, ws, cs, collection);
- // 'exec' will be registered until it is destroyed.
- exec->registerExecInternalPlan();
- return exec;
+ // Takes ownership of 'ws' and 'cs'.
+ return new PlanExecutor(txn, ws, cs, collection);
}
/**
@@ -125,10 +123,7 @@ namespace mongo {
root = new FetchStage(txn, ws, root, NULL, collection);
}
- PlanExecutor* exec = new PlanExecutor(txn, ws, root, collection);
- // 'exec' will be registered until it is destroyed.
- exec->registerExecInternalPlan();
- return exec;
+ return new PlanExecutor(txn, ws, root, collection);
}
};
diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp
index 6c8682c8851..3898424d031 100644
--- a/src/mongo/db/query/new_find.cpp
+++ b/src/mongo/db/query/new_find.cpp
@@ -442,8 +442,7 @@ namespace mongo {
OplogStart* stage = new OplogStart(txn, collection, tsExpr, oplogws);
// Takes ownership of ws and stage.
- auto_ptr<PlanExecutor> exec(new PlanExecutor(txn, oplogws, stage, collection));
- exec->registerExecInternalPlan();
+ scoped_ptr<PlanExecutor> exec(new PlanExecutor(txn, oplogws, stage, collection));
// The stage returns a DiskLoc of where to start.
DiskLoc startLoc;
@@ -596,6 +595,11 @@ namespace mongo {
verify(NULL != rawExec);
auto_ptr<PlanExecutor> exec(rawExec);
+ // We want the PlanExecutor to yield automatically, but we handle registration of the
+ // executor ourselves. We want to temporarily register the executor while we are generating
+ // this batch of results, and then unregister and re-register with ClientCursor for getmore.
+ exec->setYieldPolicy(PlanExecutor::YIELD_AUTO);
+
// If it's actually an explain, do the explain and return rather than falling through
// to the normal query execution loop.
if (pq.isExplain()) {
@@ -676,9 +680,6 @@ namespace mongo {
// Do we save the PlanExecutor in a ClientCursor for getMore calls later?
bool saveClientCursor = false;
- // The executor registers itself with the active executors list in ClientCursor.
- auto_ptr<ScopedExecutorRegistration> safety(new ScopedExecutorRegistration(exec.get()));
-
BSONObj obj;
PlanExecutor::ExecState state;
// uint64_t numMisplacedDocs = 0;
@@ -728,7 +729,7 @@ namespace mongo {
// If we don't cache the executor later, we are deleting it, so it must be deregistered.
//
// So, no matter what, deregister the executor.
- safety.reset();
+ exec->deregisterExec();
// Caller expects exceptions thrown in certain cases.
if (PlanExecutor::EXEC_ERROR == state) {
diff --git a/src/mongo/db/query/plan_executor.cpp b/src/mongo/db/query/plan_executor.cpp
index af1b5f2f4aa..601091a567b 100644
--- a/src/mongo/db/query/plan_executor.cpp
+++ b/src/mongo/db/query/plan_executor.cpp
@@ -34,6 +34,7 @@
#include "mongo/db/exec/plan_stats.h"
#include "mongo/db/exec/working_set.h"
#include "mongo/db/exec/working_set_common.h"
+#include "mongo/db/query/plan_yield_policy.h"
#include "mongo/util/stacktrace.h"
@@ -251,10 +252,14 @@ namespace mongo {
return _killed || _root->isEOF();
}
- void PlanExecutor::registerExecInternalPlan() {
+ void PlanExecutor::registerExec() {
_safety.reset(new ScopedExecutorRegistration(this));
}
+ void PlanExecutor::deregisterExec() {
+ _safety.reset();
+ }
+
void PlanExecutor::kill() {
_killed = true;
_collection = NULL;
@@ -298,24 +303,40 @@ namespace mongo {
}
void PlanExecutor::setYieldPolicy(YieldPolicy policy, bool registerExecutor) {
- // TODO: implement.
+ if (PlanExecutor::YIELD_MANUAL == policy) {
+ _yieldPolicy.reset();
+ }
+ else {
+ invariant(PlanExecutor::YIELD_AUTO == policy);
+ _yieldPolicy.reset(new PlanYieldPolicy());
+
+ // Runners that yield automatically generally need to be registered so that
+ // after yielding, they receive notifications of events like deletions and
+ // index drops. The only exception is that a few PlanExecutors get registered
+ // by ClientCursor instead of being registered here.
+ if (registerExecutor) {
+ this->registerExec();
+ }
+ }
}
//
// ScopedExecutorRegistration
//
- ScopedExecutorRegistration::ScopedExecutorRegistration(PlanExecutor* exec)
+ PlanExecutor::ScopedExecutorRegistration::ScopedExecutorRegistration(PlanExecutor* exec)
: _exec(exec) {
// Collection can be null for an EOFStage plan, or other places where registration
// is not needed.
- if ( _exec->collection() )
- _exec->collection()->cursorCache()->registerExecutor( exec );
+ if (_exec->collection()) {
+ _exec->collection()->cursorCache()->registerExecutor(exec);
+ }
}
- ScopedExecutorRegistration::~ScopedExecutorRegistration() {
- if ( _exec->collection() )
- _exec->collection()->cursorCache()->deregisterExecutor( _exec );
+ PlanExecutor::ScopedExecutorRegistration::~ScopedExecutorRegistration() {
+ if (_exec->collection()) {
+ _exec->collection()->cursorCache()->deregisterExecutor(_exec);
+ }
}
} // namespace mongo
diff --git a/src/mongo/db/query/plan_executor.h b/src/mongo/db/query/plan_executor.h
index a52136abab5..db616fc7281 100644
--- a/src/mongo/db/query/plan_executor.h
+++ b/src/mongo/db/query/plan_executor.h
@@ -42,27 +42,10 @@ namespace mongo {
class PlanStage;
class PlanExecutor;
struct PlanStageStats;
+ class PlanYieldPolicy;
class WorkingSet;
/**
- * RAII approach to ensuring that plan executors are deregistered.
- *
- * While retrieving the first batch of results, newRunQuery manually registers the executor with
- * ClientCursor. Certain query execution paths, namely $where, can throw an exception. If we
- * fail to deregister the executor, we will call invalidate/kill on the
- * still-registered-yet-deleted executor.
- *
- * For any subsequent calls to getMore, the executor is already registered with ClientCursor
- * by virtue of being cached, so this exception-proofing is not required.
- */
- struct ScopedExecutorRegistration {
- ScopedExecutorRegistration(PlanExecutor* exec);
- ~ScopedExecutorRegistration();
-
- PlanExecutor* const _exec;
- };
-
- /**
* A PlanExecutor is the abstraction that knows how to crank a tree of stages into execution.
* The executor is usually part of a larger abstraction that is interacting with the cache
* and/or the query optimizer.
@@ -101,7 +84,27 @@ namespace mongo {
YIELD_AUTO,
// Owner must yield manually if yields are requested. How to yield yourself:
- // XXX: go on about this
+ //
+ // 0. Let's say you have PlanExecutor* exec.
+ //
+ // 1. Register your PlanExecutor with ClientCursor. Registered executors are informed
+ // about DiskLoc deletions and namespace invalidation, as well as other important
+ // events. Do this either by calling registerExec() on the executor. This can be done
+ // once you get your executor, or could be done per-yield.
+ //
+ // 2. Call exec->saveState() before you yield.
+ //
+ // 3. Call Yield::yieldAllLocks(), passing in the executor's OperationContext*. This
+ // causes the executor to give up its locks and block so that it goes to the back of
+ // the scheduler's queue.
+ //
+ // 4. Call exec->restoreState() before using the executor again.
+ //
+ // 5. The next call to exec->getNext() may return DEAD.
+ //
+ // 6. Make sure the executor gets deregistered from ClientCursor. PlanExecutor does
+ // this in an RAII fashion when it is destroyed, or you can explicity call
+ // unregisterExec() on the PlanExecutor.
YIELD_MANUAL,
};
@@ -251,14 +254,14 @@ namespace mongo {
* receives notifications for events that happen while yielding any locks.
*
* Deregistration happens automatically when this plan executor is destroyed.
- *
- * Used for internal clients of the query system:
- * -- InternalPlanner::collectionScan(...) (see internal_plans.h)
- * -- InternalPlanner::indexScan(...) (see internal_plans.h)
- * -- getOplogStartHack(...) (see new_find.cpp)
- * -- storeCurrentLocs(...) (see d_migrate.cpp)
*/
- void registerExecInternalPlan();
+ void registerExec();
+
+ /**
+ * Unregister this PlanExecutor. Normally you want the PlanExecutor to be registered
+ * for its lifetime, and you shouldn't have to call this explicitly.
+ */
+ void deregisterExec();
/**
* If we're yielding locks, the database we're operating over or any collection we're
@@ -281,18 +284,37 @@ namespace mongo {
static std::string statestr(ExecState s);
/**
- * Change the yield policy of the PlanExecutor to 'policy'.
+ * Change the yield policy of the PlanExecutor to 'policy'. If 'registerExecutor' is true,
+ * and the yield policy is YIELD_AUTO, then the plan executor gets registered to receive
+ * notifications of events from other threads.
*
- * XXX: everybody who sets the policy to YIELD_AUTO really wants to call
- * registerExecInternalPlan immediately after EXCEPT new_find.cpp...so we expose
- * the ability to register (or not) via registerExecutor, rather than require
- * all users to have yet another RAII object. Only new_find.cpp should ever
- * set registerExecutor to false.
+ * Everybody who sets the policy to YIELD_AUTO really wants to call registerExec()
+ * immediately after EXCEPT commands that create cursors...so we expose the ability to
+ * register (or not) here, rather than require all users to have yet another RAII object.
+ * Only cursor-creating things like new_find.cpp set registerExecutor to false.
*/
void setYieldPolicy(YieldPolicy policy, bool registerExecutor = true);
private:
/**
+ * RAII approach to ensuring that plan executors are deregistered.
+ *
+ * While retrieving the first batch of results, newRunQuery manually registers the executor
+ * with ClientCursor. Certain query execution paths, namely $where, can throw an exception.
+ * If we fail to deregister the executor, we will call invalidate/kill on the
+ * still-registered-yet-deleted executor.
+ *
+ * For any subsequent calls to getMore, the executor is already registered with ClientCursor
+ * by virtue of being cached, so this exception-proofing is not required.
+ */
+ struct ScopedExecutorRegistration {
+ ScopedExecutorRegistration(PlanExecutor* exec);
+ ~ScopedExecutorRegistration();
+
+ PlanExecutor* const _exec;
+ };
+
+ /**
* Initialize the namespace using either the canonical query or the collection.
*/
void initNs();
@@ -319,6 +341,10 @@ namespace mongo {
// Did somebody drop an index we care about or the namespace we're looking at? If so,
// we'll be killed.
bool _killed;
+
+ // If the yield policy is YIELD_AUTO, this is used to enforce automatic yielding. The plan
+ // may yield on any call to getNext() if this is non-NULL.
+ boost::scoped_ptr<PlanYieldPolicy> _yieldPolicy;
};
} // namespace mongo