本文整理汇总了C++中PlanExecutor类的典型用法代码示例。如果您正苦于以下问题:C++ PlanExecutor类的具体用法?C++ PlanExecutor怎么用?C++ PlanExecutor使用的例子?那么, 这里精选的类代码示例或许可以为您提供帮助。
在下文中一共展示了PlanExecutor类的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。
示例1: run
void run() {
OldClientWriteContext ctx(&_txn, nss.ns());
insert(BSON("a" << 1 << "b" << 1));
Collection* collection = ctx.getCollection();
BSONObj filterObj = fromjson("{_id: {$gt: 0}, b: {$gt: 0}}");
PlanExecutor* exec = makeCollScanExec(collection, filterObj);
// Make a client cursor from the runner.
ClientCursor* cc =
new ClientCursor(collection->getCursorManager(), exec, nss.ns(), false, 0, BSONObj());
ClientCursorPin ccPin(collection->getCursorManager(), cc->cursorid());
// If the cursor is pinned, it sticks around,
// even after invalidation.
ASSERT_EQUALS(1U, numCursors());
const std::string invalidateReason("InvalidatePinned Test");
collection->getCursorManager()->invalidateAll(false, invalidateReason);
ASSERT_EQUALS(1U, numCursors());
// The invalidation should have killed the runner.
BSONObj objOut;
ASSERT_EQUALS(PlanExecutor::DEAD, exec->getNext(&objOut, NULL));
ASSERT(WorkingSetCommon::isValidStatusMemberObject(objOut));
const Status status = WorkingSetCommon::getMemberObjectStatus(objOut);
ASSERT(status.reason().find(invalidateReason) != string::npos);
// Deleting the underlying cursor should cause the
// number of cursors to return to 0.
ccPin.deleteUnderlying();
ASSERT_EQUALS(0U, numCursors());
}
示例2: getNumDeleted
// static
long long DeleteStage::getNumDeleted(const PlanExecutor& exec) {
invariant(exec.getRootStage()->isEOF());
invariant(exec.getRootStage()->stageType() == STAGE_DELETE);
DeleteStage* deleteStage = static_cast<DeleteStage*>(exec.getRootStage());
const DeleteStats* deleteStats =
static_cast<const DeleteStats*>(deleteStage->getSpecificStats());
return deleteStats->docsDeleted;
}
示例3: generateBatch
/**
* Uses 'cursor' and 'request' to fill out 'nextBatch' with the batch of result documents to
* be returned by this getMore.
*
* Returns the number of documents in the batch in *numResults, which must be initialized to
* zero by the caller. Returns the final ExecState returned by the cursor in *state.
*
* Returns an OK status if the batch was successfully generated, and a non-OK status if the
* PlanExecutor encounters a failure.
*/
Status generateBatch(ClientCursor* cursor,
const GetMoreRequest& request,
BSONArrayBuilder* nextBatch,
PlanExecutor::ExecState* state,
int* numResults) {
PlanExecutor* exec = cursor->getExecutor();
const bool isAwaitData = isCursorAwaitData(cursor);
// If an awaitData getMore is killed during this process due to our max time expiring at
// an interrupt point, we just continue as normal and return rather than reporting a
// timeout to the user.
BSONObj obj;
try {
while (PlanExecutor::ADVANCED == (*state = exec->getNext(&obj, NULL))) {
// If adding this object will cause us to exceed the BSON size limit, then we
// stash it for later.
if (nextBatch->len() + obj.objsize() > BSONObjMaxUserSize && *numResults > 0) {
exec->enqueue(obj);
break;
}
// Add result to output buffer.
nextBatch->append(obj);
(*numResults)++;
if (enoughForGetMore(request.batchSize.value_or(0),
*numResults, nextBatch->len())) {
break;
}
}
}
catch (const UserException& except) {
if (isAwaitData && except.getCode() == ErrorCodes::ExceededTimeLimit) {
// We ignore exceptions from interrupt points due to max time expiry for
// awaitData cursors.
}
else {
throw;
}
}
if (PlanExecutor::FAILURE == *state) {
const std::unique_ptr<PlanStageStats> stats(exec->getStats());
error() << "GetMore executor error, stats: " << Explain::statsToBSON(*stats);
return Status(ErrorCodes::OperationFailed,
str::stream() << "GetMore executor error: "
<< WorkingSetCommon::toStatusString(obj));
}
else if (PlanExecutor::DEAD == *state) {
return Status(ErrorCodes::OperationFailed,
str::stream() << "Plan executor killed during getMore command, "
<< "ns: " << request.nss.ns());
}
return Status::OK();
}
示例4: getSummaryStats
// static
void Explain::getSummaryStats(const PlanExecutor& exec, PlanSummaryStats* statsOut) {
invariant(NULL != statsOut);
PlanStage* root = exec.getRootStage();
// We can get some of the fields we need from the common stats stored in the
// root stage of the plan tree.
const CommonStats* common = root->getCommonStats();
statsOut->nReturned = common->advanced;
statsOut->executionTimeMillis = common->executionTimeMillis;
// The other fields are aggregations over the stages in the plan tree. We flatten
// the tree into a list and then compute these aggregations.
std::vector<const PlanStage*> stages;
flattenExecTree(root, &stages);
for (size_t i = 0; i < stages.size(); i++) {
statsOut->totalKeysExamined +=
getKeysExamined(stages[i]->stageType(), stages[i]->getSpecificStats());
statsOut->totalDocsExamined +=
getDocsExamined(stages[i]->stageType(), stages[i]->getSpecificStats());
if (STAGE_IDHACK == stages[i]->stageType()) {
statsOut->isIdhack = true;
}
if (STAGE_SORT == stages[i]->stageType()) {
statsOut->hasSortStage = true;
}
}
}
示例5: countResults
int countResults(const IndexScanParams& params, BSONObj filterObj = BSONObj()) {
Client::ReadContext ctx(ns());
PlanExecutor runner;
StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj);
verify(swme.isOK());
auto_ptr<MatchExpression> filterExpr(swme.getValue());
runner.setRoot(new IndexScan(params, runner.getWorkingSet(), filterExpr.get()));
int count = 0;
for (BSONObj obj; Runner::RUNNER_ADVANCED == runner.getNext(&obj); ) {
++count;
}
return count;
}
示例6: endQueryOp
void endQueryOp(OperationContext* txn,
Collection* collection,
const PlanExecutor& exec,
int dbProfilingLevel,
long long numResults,
CursorId cursorId) {
auto curop = CurOp::get(txn);
// Fill out basic curop query exec properties.
curop->debug().nreturned = numResults;
curop->debug().cursorid = (0 == cursorId ? -1 : cursorId);
curop->debug().cursorExhausted = (0 == cursorId);
// Fill out curop based on explain summary statistics.
PlanSummaryStats summaryStats;
Explain::getSummaryStats(exec, &summaryStats);
curop->debug().hasSortStage = summaryStats.hasSortStage;
curop->debug().keysExamined = summaryStats.totalKeysExamined;
curop->debug().docsExamined = summaryStats.totalDocsExamined;
curop->debug().idhack = summaryStats.isIdhack;
if (collection) {
collection->infoCache()->notifyOfQuery(txn, summaryStats.indexesUsed);
}
const logger::LogComponent commandLogComponent = logger::LogComponent::kCommand;
const logger::LogSeverity logLevelOne = logger::LogSeverity::Debug(1);
// Set debug information for consumption by the profiler and slow query log.
if (dbProfilingLevel > 0 || curop->elapsedMillis() > serverGlobalParams.slowMS ||
logger::globalLogDomain()->shouldLog(commandLogComponent, logLevelOne)) {
// Generate plan summary string.
stdx::lock_guard<Client>(*txn->getClient());
curop->setPlanSummary_inlock(Explain::getPlanSummary(&exec));
}
// Set debug information for consumption by the profiler only.
if (dbProfilingLevel > 0) {
// Get BSON stats.
unique_ptr<PlanStageStats> execStats(exec.getStats());
BSONObjBuilder statsBob;
Explain::statsToBSON(*execStats, &statsBob);
curop->debug().execStats.set(statsBob.obj());
// Replace exec stats with plan summary if stats cannot fit into CachedBSONObj.
if (curop->debug().execStats.tooBig() && !curop->getPlanSummary().empty()) {
BSONObjBuilder bob;
bob.append("summary", curop->getPlanSummary());
curop->debug().execStats.set(bob.done());
}
}
}
示例7: run
/**
* Runs a query using the following steps:
* --Parsing.
* --Acquire locks.
* --Plan query, obtaining an executor that can run it.
* --Generate the first batch.
* --Save state for getMore, transferring ownership of the executor to a ClientCursor.
* --Generate response to send to the client.
*/
bool run(OperationContext* txn,
const std::string& dbname,
BSONObj& cmdObj,
int options,
std::string& errmsg,
BSONObjBuilder& result) override {
const NamespaceString nss(parseNs(dbname, cmdObj));
if (!nss.isValid() || nss.isCommand() || nss.isSpecialCommand()) {
return appendCommandStatus(result,
{ErrorCodes::InvalidNamespace,
str::stream() << "Invalid collection name: " << nss.ns()});
}
// Although it is a command, a find command gets counted as a query.
globalOpCounters.gotQuery();
if (txn->getClient()->isInDirectClient()) {
return appendCommandStatus(
result,
Status(ErrorCodes::IllegalOperation, "Cannot run find command from eval()"));
}
// Parse the command BSON to a QueryRequest.
const bool isExplain = false;
auto qrStatus = QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain);
if (!qrStatus.isOK()) {
return appendCommandStatus(result, qrStatus.getStatus());
}
auto& qr = qrStatus.getValue();
// Validate term before acquiring locks, if provided.
if (auto term = qr->getReplicationTerm()) {
auto replCoord = repl::ReplicationCoordinator::get(txn);
Status status = replCoord->updateTerm(txn, *term);
// Note: updateTerm returns ok if term stayed the same.
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
}
// Fill out curop information.
//
// We pass negative values for 'ntoreturn' and 'ntoskip' to indicate that these values
// should be omitted from the log line. Limit and skip information is already present in the
// find command parameters, so these fields are redundant.
const int ntoreturn = -1;
const int ntoskip = -1;
beginQueryOp(txn, nss, cmdObj, ntoreturn, ntoskip);
// Finish the parsing step by using the QueryRequest to create a CanonicalQuery.
ExtensionsCallbackReal extensionsCallback(txn, &nss);
auto statusWithCQ = CanonicalQuery::canonicalize(txn, std::move(qr), extensionsCallback);
if (!statusWithCQ.isOK()) {
return appendCommandStatus(result, statusWithCQ.getStatus());
}
std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
// Acquire locks.
AutoGetCollectionForRead ctx(txn, nss);
Collection* collection = ctx.getCollection();
// Get the execution plan for the query.
auto statusWithPlanExecutor =
getExecutorFind(txn, collection, nss, std::move(cq), PlanExecutor::YIELD_AUTO);
if (!statusWithPlanExecutor.isOK()) {
return appendCommandStatus(result, statusWithPlanExecutor.getStatus());
}
std::unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());
{
stdx::lock_guard<Client>(*txn->getClient());
CurOp::get(txn)->setPlanSummary_inlock(Explain::getPlanSummary(exec.get()));
}
if (!collection) {
// No collection. Just fill out curop indicating that there were zero results and
// there is no ClientCursor id, and then return.
const long long numResults = 0;
const CursorId cursorId = 0;
endQueryOp(txn, collection, *exec, numResults, cursorId);
appendCursorResponseObject(cursorId, nss.ns(), BSONArray(), &result);
return true;
}
const QueryRequest& originalQR = exec->getCanonicalQuery()->getQueryRequest();
// Stream query results, adding them to a BSONArray as we go.
CursorResponseBuilder firstBatch(/*isInitialResponse*/ true, &result);
BSONObj obj;
//.........这里部分代码省略.........
示例8: newGetMore
//.........这里部分代码省略.........
}
// Swap RecoveryUnit(s) between the ClientCursor and OperationContext.
ruSwapper.reset(new ScopedRecoveryUnitSwapper(cc, txn));
}
// Reset timeout timer on the cursor since the cursor is still in use.
cc->setIdleTime(0);
// TODO: fail point?
// If the operation that spawned this cursor had a time limit set, apply leftover
// time to this getmore.
curop.setMaxTimeMicros(cc->getLeftoverMaxTimeMicros());
txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point.
if (0 == pass) {
cc->updateSlaveLocation(txn, curop);
}
if (cc->isAggCursor) {
// Agg cursors handle their own locking internally.
ctx.reset(); // unlocks
}
CollectionMetadataPtr collMetadata = cc->getCollMetadata();
// If we're replaying the oplog, we save the last time that we read.
OpTime slaveReadTill;
// What number result are we starting at? Used to fill out the reply.
startingResult = cc->pos();
// What gives us results.
PlanExecutor* exec = cc->getExecutor();
const int queryOptions = cc->queryOptions();
// Get results out of the executor.
exec->restoreState(txn);
BSONObj obj;
PlanExecutor::ExecState state;
while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
// Add result to output buffer.
bb.appendBuf((void*)obj.objdata(), obj.objsize());
// Count the result.
++numResults;
// Possibly note slave's position in the oplog.
if (queryOptions & QueryOption_OplogReplay) {
BSONElement e = obj["ts"];
if (Date == e.type() || Timestamp == e.type()) {
slaveReadTill = e._opTime();
}
}
if ((ntoreturn && numResults >= ntoreturn)
|| bb.len() > MaxBytesToReturnToClientAtOnce) {
break;
}
}
// We save the client cursor when there might be more results, and hence we may receive
// another getmore. If we receive a EOF or an error, or 'exec' is dead, then we know
// that we will not be producing more results. We indicate that the cursor is closed by
// sending a cursorId of 0 back to the client.
示例9: run
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options,
string& errmsg, BSONObjBuilder& result,
bool fromRepl = false ) {
NamespaceString ns( dbname, cmdObj[name].String() );
AutoGetCollectionForRead ctx(txn, ns.ns());
Collection* collection = ctx.getCollection();
if ( !collection )
return appendCommandStatus( result,
Status( ErrorCodes::NamespaceNotFound,
str::stream() <<
"ns does not exist: " << ns.ns() ) );
size_t numCursors = static_cast<size_t>( cmdObj["numCursors"].numberInt() );
if ( numCursors == 0 || numCursors > 10000 )
return appendCommandStatus( result,
Status( ErrorCodes::BadValue,
str::stream() <<
"numCursors has to be between 1 and 10000" <<
" was: " << numCursors ) );
OwnedPointerVector<RecordIterator> iterators(collection->getManyIterators(txn));
if (iterators.size() < numCursors) {
numCursors = iterators.size();
}
OwnedPointerVector<PlanExecutor> execs;
for ( size_t i = 0; i < numCursors; i++ ) {
WorkingSet* ws = new WorkingSet();
MultiIteratorStage* mis = new MultiIteratorStage(txn, ws, collection);
PlanExecutor* rawExec;
// Takes ownership of 'ws' and 'mis'.
Status execStatus = PlanExecutor::make(txn, ws, mis, collection,
PlanExecutor::YIELD_AUTO, &rawExec);
invariant(execStatus.isOK());
auto_ptr<PlanExecutor> curExec(rawExec);
// The PlanExecutor was registered on construction due to the YIELD_AUTO policy.
// We have to deregister it, as it will be registered with ClientCursor.
curExec->deregisterExec();
// Need to save state while yielding locks between now and getMore().
curExec->saveState();
execs.push_back(curExec.release());
}
// transfer iterators to executors using a round-robin distribution.
// TODO consider using a common work queue once invalidation issues go away.
for (size_t i = 0; i < iterators.size(); i++) {
PlanExecutor* theExec = execs[i % execs.size()];
MultiIteratorStage* mis = static_cast<MultiIteratorStage*>(theExec->getRootStage());
// This wasn't called above as they weren't assigned yet
iterators[i]->saveState();
mis->addIterator(iterators.releaseAt(i));
}
{
BSONArrayBuilder bucketsBuilder;
for (size_t i = 0; i < execs.size(); i++) {
// transfer ownership of an executor to the ClientCursor (which manages its own
// lifetime).
ClientCursor* cc = new ClientCursor( collection->getCursorManager(),
execs.releaseAt(i),
ns.ns() );
BSONObjBuilder threadResult;
appendCursorResponseObject( cc->cursorid(),
ns.ns(),
BSONArray(),
&threadResult );
threadResult.appendBool( "ok", 1 );
bucketsBuilder.append( threadResult.obj() );
}
result.appendArray( "cursors", bucketsBuilder.obj() );
}
return true;
}
示例10: run
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options,
string& errmsg, BSONObjBuilder& result,
bool fromRepl = false ) {
NamespaceString ns( dbname, cmdObj[name].String() );
AutoGetCollectionForRead ctx(txn, ns.ns());
Collection* collection = ctx.getCollection();
if ( !collection )
return appendCommandStatus( result,
Status( ErrorCodes::NamespaceNotFound,
str::stream() <<
"ns does not exist: " << ns.ns() ) );
size_t numCursors = static_cast<size_t>( cmdObj["numCursors"].numberInt() );
if ( numCursors == 0 || numCursors > 10000 )
return appendCommandStatus( result,
Status( ErrorCodes::BadValue,
str::stream() <<
"numCursors has to be between 1 and 10000" <<
" was: " << numCursors ) );
OwnedPointerVector<RecordIterator> iterators(collection->getManyIterators(txn));
if (iterators.size() < numCursors) {
numCursors = iterators.size();
}
OwnedPointerVector<PlanExecutor> execs;
for ( size_t i = 0; i < numCursors; i++ ) {
WorkingSet* ws = new WorkingSet();
MultiIteratorStage* mis = new MultiIteratorStage(txn, ws, collection);
// Takes ownership of 'ws' and 'mis'.
auto_ptr<PlanExecutor> curExec(new PlanExecutor(txn, ws, mis, collection));
// Each of the plan executors should yield automatically. We pass "false" to
// indicate that 'curExec' should not register itself, as it will get registered
// by ClientCursor instead.
curExec->setYieldPolicy(PlanExecutor::YIELD_AUTO, false);
// Need to save state while yielding locks between now and newGetMore.
curExec->saveState();
execs.push_back(curExec.release());
}
// transfer iterators to executors using a round-robin distribution.
// TODO consider using a common work queue once invalidation issues go away.
for (size_t i = 0; i < iterators.size(); i++) {
PlanExecutor* theExec = execs[i % execs.size()];
MultiIteratorStage* mis = static_cast<MultiIteratorStage*>(theExec->getRootStage());
mis->addIterator(iterators.releaseAt(i));
}
{
BSONArrayBuilder bucketsBuilder;
for (size_t i = 0; i < execs.size(); i++) {
// transfer ownership of an executor to the ClientCursor (which manages its own
// lifetime).
ClientCursor* cc = new ClientCursor( collection, execs.releaseAt(i) );
// we are mimicking the aggregation cursor output here
// that is why there are ns, ok and empty firstBatch
BSONObjBuilder threadResult;
{
BSONObjBuilder cursor;
cursor.appendArray( "firstBatch", BSONObj() );
cursor.append( "ns", ns );
cursor.append( "id", cc->cursorid() );
threadResult.append( "cursor", cursor.obj() );
}
threadResult.appendBool( "ok", 1 );
bucketsBuilder.append( threadResult.obj() );
}
result.appendArray( "cursors", bucketsBuilder.obj() );
}
return true;
}
示例11: getMore
//.........这里部分代码省略.........
// Start using a new RecoveryUnit
cc->setOwnedRecoveryUnit(
getGlobalServiceContext()->getGlobalStorageEngine()->newRecoveryUnit());
}
// Swap RecoveryUnit(s) between the ClientCursor and OperationContext.
ruSwapper.reset(new ScopedRecoveryUnitSwapper(cc, txn));
}
// Reset timeout timer on the cursor since the cursor is still in use.
cc->setIdleTime(0);
// If the operation that spawned this cursor had a time limit set, apply leftover
// time to this getmore.
curop.setMaxTimeMicros(cc->getLeftoverMaxTimeMicros());
txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point.
if (0 == pass) {
cc->updateSlaveLocation(txn);
}
if (cc->isAggCursor()) {
// Agg cursors handle their own locking internally.
ctx.reset(); // unlocks
}
// If we're replaying the oplog, we save the last time that we read.
Timestamp slaveReadTill;
// What number result are we starting at? Used to fill out the reply.
startingResult = cc->pos();
// What gives us results.
PlanExecutor* exec = cc->getExecutor();
const int queryOptions = cc->queryOptions();
// Get results out of the executor.
exec->restoreState(txn);
BSONObj obj;
PlanExecutor::ExecState state;
while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
// Add result to output buffer.
bb.appendBuf((void*)obj.objdata(), obj.objsize());
// Count the result.
++numResults;
// Possibly note slave's position in the oplog.
if (queryOptions & QueryOption_OplogReplay) {
BSONElement e = obj["ts"];
if (Date == e.type() || bsonTimestamp == e.type()) {
slaveReadTill = e.timestamp();
}
}
if (enoughForGetMore(ntoreturn, numResults, bb.len())) {
break;
}
}
if (PlanExecutor::DEAD == state || PlanExecutor::FAILURE == state) {
// Propagate this error to caller.
if (PlanExecutor::FAILURE == state) {
scoped_ptr<PlanStageStats> stats(exec->getStats());
error() << "Plan executor error, stats: "
示例12: getMore
//.........这里部分代码省略.........
{
stdx::lock_guard<Client> lk(*txn->getClient());
curop.setQuery_inlock(cc->getQuery());
}
cc->updateSlaveLocation(txn);
if (cc->isAggCursor()) {
// Agg cursors handle their own locking internally.
ctx.reset(); // unlocks
}
// If we're replaying the oplog, we save the last time that we read.
Timestamp slaveReadTill;
// What number result are we starting at? Used to fill out the reply.
startingResult = cc->pos();
uint64_t notifierVersion = 0;
std::shared_ptr<CappedInsertNotifier> notifier;
if (isCursorAwaitData(cc)) {
invariant(ctx->getCollection()->isCapped());
// Retrieve the notifier which we will wait on until new data arrives. We make sure
// to do this in the lock because once we drop the lock it is possible for the
// collection to become invalid. The notifier itself will outlive the collection if
// the collection is dropped, as we keep a shared_ptr to it.
notifier = ctx->getCollection()->getCappedInsertNotifier();
// Must get the version before we call generateBatch in case a write comes in after
// that call and before we call wait on the notifier.
notifierVersion = notifier->getVersion();
}
PlanExecutor* exec = cc->getExecutor();
exec->reattachToOperationContext(txn);
exec->restoreState();
PlanExecutor::ExecState state;
generateBatch(ntoreturn, cc, &bb, &numResults, &slaveReadTill, &state);
// If this is an await data cursor, and we hit EOF without generating any results, then
// we block waiting for new data to arrive.
if (isCursorAwaitData(cc) && state == PlanExecutor::IS_EOF && numResults == 0) {
// Save the PlanExecutor and drop our locks.
exec->saveState();
ctx.reset();
// Block waiting for data for up to 1 second.
Seconds timeout(1);
notifier->wait(notifierVersion, timeout);
notifier.reset();
// Set expected latency to match wait time. This makes sure the logs aren't spammed
// by awaitData queries that exceed slowms due to blocking on the CappedInsertNotifier.
curop.setExpectedLatencyMs(durationCount<Milliseconds>(timeout));
// Reacquiring locks.
ctx = make_unique<AutoGetCollectionForRead>(txn, nss);
exec->restoreState();
// We woke up because either the timed_wait expired, or there was more data. Either
// way, attempt to generate another batch of results.
generateBatch(ntoreturn, cc, &bb, &numResults, &slaveReadTill, &state);
}
// We have to do this before re-acquiring locks in the agg case because
示例13: run
/**
* Runs a query using the following steps:
* 1) Parsing.
* 2) Acquire locks.
* 3) Plan query, obtaining an executor that can run it.
* 4) Setup a cursor for the query, which may be used on subsequent getMores.
* 5) Generate the first batch.
* 6) Save state for getMore.
* 7) Generate response to send to the client.
*
* TODO: Rather than using the sharding version available in thread-local storage (i.e. the
* call to ShardingState::needCollectionMetadata() below), shard version information
* should be passed as part of the command parameter.
*/
bool run(OperationContext* txn,
const std::string& dbname,
BSONObj& cmdObj,
int options,
std::string& errmsg,
BSONObjBuilder& result) override {
const std::string fullns = parseNs(dbname, cmdObj);
const NamespaceString nss(fullns);
if (!nss.isValid()) {
return appendCommandStatus(result,
{ErrorCodes::InvalidNamespace,
str::stream() << "Invalid collection name: " << nss.ns()});
}
// Although it is a command, a find command gets counted as a query.
globalOpCounters.gotQuery();
if (txn->getClient()->isInDirectClient()) {
return appendCommandStatus(
result,
Status(ErrorCodes::IllegalOperation, "Cannot run find command from eval()"));
}
// 1a) Parse the command BSON to a LiteParsedQuery.
const bool isExplain = false;
auto lpqStatus = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain);
if (!lpqStatus.isOK()) {
return appendCommandStatus(result, lpqStatus.getStatus());
}
auto& lpq = lpqStatus.getValue();
// Validate term, if provided.
if (auto term = lpq->getReplicationTerm()) {
auto replCoord = repl::ReplicationCoordinator::get(txn);
Status status = replCoord->updateTerm(*term);
// Note: updateTerm returns ok if term stayed the same.
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
}
// Fill out curop information.
long long ntoreturn = lpq->getBatchSize().value_or(0);
beginQueryOp(txn, nss, cmdObj, ntoreturn, lpq->getSkip());
// 1b) Finish the parsing step by using the LiteParsedQuery to create a CanonicalQuery.
WhereCallbackReal whereCallback(txn, nss.db());
auto statusWithCQ = CanonicalQuery::canonicalize(lpq.release(), whereCallback);
if (!statusWithCQ.isOK()) {
return appendCommandStatus(result, statusWithCQ.getStatus());
}
std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
// 2) Acquire locks.
AutoGetCollectionForRead ctx(txn, nss);
Collection* collection = ctx.getCollection();
const int dbProfilingLevel =
ctx.getDb() ? ctx.getDb()->getProfilingLevel() : serverGlobalParams.defaultProfile;
ShardingState* const shardingState = ShardingState::get(txn);
// It is possible that the sharding version will change during yield while we are
// retrieving a plan executor. If this happens we will throw an error and mongos will
// retry.
const ChunkVersion shardingVersionAtStart = shardingState->getVersion(nss.ns());
// 3) Get the execution plan for the query.
auto statusWithPlanExecutor =
getExecutorFind(txn, collection, nss, std::move(cq), PlanExecutor::YIELD_AUTO);
if (!statusWithPlanExecutor.isOK()) {
return appendCommandStatus(result, statusWithPlanExecutor.getStatus());
}
std::unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());
// TODO: Currently, chunk ranges are kept around until all ClientCursors created while
// the chunk belonged on this node are gone. Separating chunk lifetime management from
// ClientCursor should allow this check to go away.
if (!shardingState->getVersion(nss.ns()).isWriteCompatibleWith(shardingVersionAtStart)) {
// Version changed while retrieving a PlanExecutor. Terminate the operation,
// signaling that mongos should retry.
throw SendStaleConfigException(nss.ns(),
"version changed during find command",
shardingVersionAtStart,
//.........这里部分代码省略.........
示例14: run
//.........这里部分代码省略.........
ChunkVersion receivedVersion = OperationShardVersion::get(txn).getShardVersion(nss);
ChunkVersion latestVersion;
// Wait for migration completion to get the correct chunk version.
const int maxTimeoutSec = 30;
int timeoutSec = cq->getParsed().getMaxTimeMS() / 1000;
if (!timeoutSec || timeoutSec > maxTimeoutSec) {
timeoutSec = maxTimeoutSec;
}
if (!shardingState->waitTillNotInCriticalSection(timeoutSec)) {
uasserted(ErrorCodes::LockTimeout, "Timeout while waiting for migration commit");
}
// If the received version is newer than the version cached in 'shardingState', then we
// have to refresh 'shardingState' from the config servers. We do this before acquiring
// locks so that we don't hold locks while waiting on the network.
uassertStatusOK(shardingState->refreshMetadataIfNeeded(
txn, nss.ns(), receivedVersion, &latestVersion));
}
// Acquire locks.
AutoGetCollectionForRead ctx(txn, nss);
Collection* collection = ctx.getCollection();
const int dbProfilingLevel =
ctx.getDb() ? ctx.getDb()->getProfilingLevel() : serverGlobalParams.defaultProfile;
// It is possible that the sharding version will change during yield while we are
// retrieving a plan executor. If this happens we will throw an error and mongos will
// retry.
const ChunkVersion shardingVersionAtStart = shardingState->getVersion(nss.ns());
// Get the execution plan for the query.
auto statusWithPlanExecutor =
getExecutorFind(txn, collection, nss, std::move(cq), PlanExecutor::YIELD_AUTO);
if (!statusWithPlanExecutor.isOK()) {
return appendCommandStatus(result, statusWithPlanExecutor.getStatus());
}
std::unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());
if (!collection) {
// No collection. Just fill out curop indicating that there were zero results and
// there is no ClientCursor id, and then return.
const long long numResults = 0;
const CursorId cursorId = 0;
endQueryOp(txn, collection, *exec, dbProfilingLevel, numResults, cursorId);
appendCursorResponseObject(cursorId, nss.ns(), BSONArray(), &result);
return true;
}
const LiteParsedQuery& pq = exec->getCanonicalQuery()->getParsed();
// Stream query results, adding them to a BSONArray as we go.
BSONArrayBuilder firstBatch;
BSONObj obj;
PlanExecutor::ExecState state = PlanExecutor::ADVANCED;
long long numResults = 0;
while (!FindCommon::enoughForFirstBatch(pq, numResults, firstBatch.len()) &&
PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
// If adding this object will cause us to exceed the BSON size limit, then we stash
// it for later.
if (firstBatch.len() + obj.objsize() > BSONObjMaxUserSize && numResults > 0) {
exec->enqueue(obj);
break;
}
示例15: run
//.........这里部分代码省略.........
return appendCommandStatus(result, Status(ErrorCodes::CursorNotFound, str::stream()
<< "Cursor not found, cursor id: " << request.cursorid));
}
if (request.nss.ns() != cursor->ns()) {
return appendCommandStatus(result, Status(ErrorCodes::Unauthorized, str::stream()
<< "Requested getMore on namespace '" << request.nss.ns()
<< "', but cursor belongs to a different namespace"));
}
// On early return, get rid of the the cursor.
ScopeGuard cursorFreer = MakeGuard(&ClientCursorPin::deleteUnderlying, ccPin);
if (!cursor->hasRecoveryUnit()) {
// Start using a new RecoveryUnit.
cursor->setOwnedRecoveryUnit(
getGlobalServiceContext()->getGlobalStorageEngine()->newRecoveryUnit());
}
// Swap RecoveryUnit(s) between the ClientCursor and OperationContext.
ScopedRecoveryUnitSwapper ruSwapper(cursor, txn);
// Reset timeout timer on the cursor since the cursor is still in use.
cursor->setIdleTime(0);
// If the operation that spawned this cursor had a time limit set, apply leftover
// time to this getmore.
txn->getCurOp()->setMaxTimeMicros(cursor->getLeftoverMaxTimeMicros());
txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point.
if (cursor->isAggCursor()) {
// Agg cursors handle their own locking internally.
ctx.reset(); // unlocks
}
PlanExecutor* exec = cursor->getExecutor();
exec->restoreState(txn);
// TODO: Handle result sets larger than 16MB.
BSONArrayBuilder nextBatch;
BSONObj obj;
PlanExecutor::ExecState state;
int numResults = 0;
while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
// Add result to output buffer.
nextBatch.append(obj);
numResults++;
if (enoughForGetMore(request.batchSize, numResults, nextBatch.len())) {
break;
}
}
// If we are operating on an aggregation cursor, then we dropped our collection lock
// earlier and need to reacquire it in order to clean up our ClientCursorPin.
//
// TODO: We need to ensure that this relock happens if we release the pin above in
// response to PlanExecutor::getNext() throwing an exception.
if (cursor->isAggCursor()) {
invariant(NULL == ctx.get());
unpinDBLock.reset(new Lock::DBLock(txn->lockState(), request.nss.db(), MODE_IS));
unpinCollLock.reset(
new Lock::CollectionLock(txn->lockState(), request.nss.ns(), MODE_IS));
}
// Fail the command if the PlanExecutor reports execution failure.
if (PlanExecutor::FAILURE == state) {
const std::unique_ptr<PlanStageStats> stats(exec->getStats());
error() << "GetMore executor error, stats: " << Explain::statsToBSON(*stats);
return appendCommandStatus(result,
Status(ErrorCodes::OperationFailed,
str::stream() << "GetMore executor error: "
<< WorkingSetCommon::toStatusString(obj)));
}
CursorId respondWithId = 0;
if (shouldSaveCursorGetMore(state, exec, isCursorTailable(cursor))) {
respondWithId = request.cursorid;
exec->saveState();
cursor->setLeftoverMaxTimeMicros(txn->getCurOp()->getRemainingMaxTimeMicros());
cursor->incPos(numResults);
if (isCursorTailable(cursor) && state == PlanExecutor::IS_EOF) {
// Rather than swapping their existing RU into the client cursor, tailable
// cursors should get a new recovery unit.
ruSwapper.dismiss();
}
}
else {
txn->getCurOp()->debug().cursorExhausted = true;
}
appendGetMoreResponseObject(respondWithId, request.nss.ns(), nextBatch.arr(), &result);
if (respondWithId) {
cursorFreer.Dismiss();
}
return true;
}