Status getOplogStartHack(OperationContext* txn, Collection* collection, CanonicalQuery* cq, PlanExecutor** execOut) { invariant(cq); auto_ptr<CanonicalQuery> autoCq(cq); if ( collection == NULL ) return Status(ErrorCodes::InternalError, "getOplogStartHack called with a NULL collection" ); // A query can only do oplog start finding if it has a top-level $gt or $gte predicate over // the "ts" field (the operation's timestamp). Find that predicate and pass it to // the OplogStart stage. MatchExpression* tsExpr = NULL; if (MatchExpression::AND == cq->root()->matchType()) { // The query has an AND at the top-level. See if any of the children // of the AND are $gt or $gte predicates over 'ts'. for (size_t i = 0; i < cq->root()->numChildren(); ++i) { MatchExpression* me = cq->root()->getChild(i); if (isOplogTsPred(me)) { tsExpr = me; break; } } } else if (isOplogTsPred(cq->root())) { // The root of the tree is a $gt or $gte predicate over 'ts'. tsExpr = cq->root(); } if (NULL == tsExpr) { return Status(ErrorCodes::OplogOperationUnsupported, "OplogReplay query does not contain top-level " "$gt or $gte over the 'ts' field."); } DiskLoc startLoc = DiskLoc().setInvalid(); // See if the RecordStore supports the oplogStartHack const BSONElement tsElem = extractOplogTsOptime(tsExpr); if (tsElem.type() == Timestamp) { StatusWith<DiskLoc> goal = oploghack::keyForOptime(tsElem._opTime()); if (goal.isOK()) { startLoc = collection->getRecordStore()->oplogStartHack(txn, goal.getValue()); } } if (startLoc.isValid()) { LOG(3) << "Using direct oplog seek"; } else { LOG(3) << "Using OplogStart stage"; // Fallback to trying the OplogStart stage. WorkingSet* oplogws = new WorkingSet(); OplogStart* stage = new OplogStart(txn, collection, tsExpr, oplogws); PlanExecutor* rawExec; // Takes ownership of oplogws and stage. Status execStatus = PlanExecutor::make(txn, oplogws, stage, collection, PlanExecutor::YIELD_AUTO, &rawExec); invariant(execStatus.isOK()); scoped_ptr<PlanExecutor> exec(rawExec); // The stage returns a DiskLoc of where to start. PlanExecutor::ExecState state = exec->getNext(NULL, &startLoc); // This is normal. The start of the oplog is the beginning of the collection. if (PlanExecutor::IS_EOF == state) { return getExecutor(txn, collection, autoCq.release(), PlanExecutor::YIELD_AUTO, execOut); } // This is not normal. An error was encountered. if (PlanExecutor::ADVANCED != state) { return Status(ErrorCodes::InternalError, "quick oplog start location had error...?"); } } // cout << "diskloc is " << startLoc.toString() << endl; // Build our collection scan... CollectionScanParams params; params.collection = collection; params.start = startLoc; params.direction = CollectionScanParams::FORWARD; params.tailable = cq->getParsed().getOptions().tailable; WorkingSet* ws = new WorkingSet(); CollectionScan* cs = new CollectionScan(txn, params, ws, cq->root()); // Takes ownership of 'ws', 'cs', and 'cq'. return PlanExecutor::make(txn, ws, cs, autoCq.release(), collection, PlanExecutor::YIELD_AUTO, execOut); }
Status getOplogStartHack(Collection* collection, CanonicalQuery* cq, Runner** runnerOut) { if ( collection == NULL ) return Status(ErrorCodes::InternalError, "getOplogStartHack called with a NULL collection" ); // A query can only do oplog start finding if it has a top-level $gt or $gte predicate over // the "ts" field (the operation's timestamp). Find that predicate and pass it to // the OplogStart stage. MatchExpression* tsExpr = NULL; if (MatchExpression::AND == cq->root()->matchType()) { // The query has an AND at the top-level. See if any of the children // of the AND are $gt or $gte predicates over 'ts'. for (size_t i = 0; i < cq->root()->numChildren(); ++i) { MatchExpression* me = cq->root()->getChild(i); if (isOplogTsPred(me)) { tsExpr = me; break; } } } else if (isOplogTsPred(cq->root())) { // The root of the tree is a $gt or $gte predicate over 'ts'. tsExpr = cq->root(); } if (NULL == tsExpr) { return Status(ErrorCodes::OplogOperationUnsupported, "OplogReplay query does not contain top-level " "$gt or $gte over the 'ts' field."); } // Make an oplog start finding stage. WorkingSet* oplogws = new WorkingSet(); OplogStart* stage = new OplogStart(cq->ns(), tsExpr, oplogws); // Takes ownership of ws and stage. auto_ptr<InternalRunner> runner(new InternalRunner(collection, stage, oplogws)); runner->setYieldPolicy(Runner::YIELD_AUTO); // The stage returns a DiskLoc of where to start. DiskLoc startLoc; Runner::RunnerState state = runner->getNext(NULL, &startLoc); // This is normal. The start of the oplog is the beginning of the collection. if (Runner::RUNNER_EOF == state) { return getRunner(cq, runnerOut); } // This is not normal. An error was encountered. if (Runner::RUNNER_ADVANCED != state) { return Status(ErrorCodes::InternalError, "quick oplog start location had error...?"); } // cout << "diskloc is " << startLoc.toString() << endl; // Build our collection scan... CollectionScanParams params; params.ns = cq->ns(); params.start = startLoc; params.direction = CollectionScanParams::FORWARD; params.tailable = cq->getParsed().hasOption(QueryOption_CursorTailable); WorkingSet* ws = new WorkingSet(); CollectionScan* cs = new CollectionScan(params, ws, cq->root()); // Takes ownership of cq, cs, ws. *runnerOut = new SingleSolutionRunner(collection, cq, NULL, cs, ws); return Status::OK(); }