summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db/oplog.cpp30
-rw-r--r--db/oplog.h1
-rw-r--r--dbtests/querytests.cpp29
-rw-r--r--dbtests/repltests.cpp28
4 files changed, 78 insertions, 10 deletions
diff --git a/db/oplog.cpp b/db/oplog.cpp
index 90a5544788b..d58cdcff9ba 100644
--- a/db/oplog.cpp
+++ b/db/oplog.cpp
@@ -473,9 +473,9 @@ namespace mongo {
return _qp.nsd()->capFirstNewRecord;
}
- void assertExtentNonempty( const Extent *e ) {
+ void wassertExtentNonempty( const Extent *e ) {
// TODO ensure this requirement is clearly enforced, or fix.
- massert( 14834, "empty extent found during finding start scan", !e->firstRecord.isNull() );
+ wassert( !e->firstRecord.isNull() );
}
DiskLoc FindingStartCursor::prevExtentFirstLoc( const DiskLoc &rec ) {
@@ -488,14 +488,14 @@ namespace mongo {
e = e->xprev.ext();
}
if ( e->myLoc != _qp.nsd()->capExtent ) {
- assertExtentNonempty( e );
+ wassertExtentNonempty( e );
return e->firstRecord;
}
}
else {
if ( !e->xprev.isNull() ) {
e = e->xprev.ext();
- assertExtentNonempty( e );
+ wassertExtentNonempty( e );
return e->firstRecord;
}
}
@@ -506,20 +506,30 @@ namespace mongo {
shared_ptr<Cursor> c = _qp.newCursor( startLoc );
_findingStartCursor.reset( new ClientCursor(QueryOption_NoCursorTimeout, c, _qp.ns()) );
}
+
+ bool FindingStartCursor::firstDocMatches() const {
+ shared_ptr<Cursor> c = _qp.newCursor();
+ return _matcher->matchesCurrent( c.get() );
+ }
void FindingStartCursor::init() {
- // Use a ClientCursor here so we can release db mutex while scanning
- // oplog (can take quite a while with large oplogs).
- shared_ptr<Cursor> c = _qp.newReverseCursor();
- _findingStartCursor.reset( new ClientCursor(QueryOption_NoCursorTimeout, c, _qp.ns(), BSONObj()) );
- _findingStartTimer.reset();
- _findingStartMode = Initial;
BSONElement tsElt = _qp.originalQuery()[ "ts" ];
massert( 13044, "no ts field in query", !tsElt.eoo() );
BSONObjBuilder b;
b.append( tsElt );
BSONObj tsQuery = b.obj();
_matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey()));
+ if ( firstDocMatches() ) {
+ _c = _qp.newCursor();
+ _findingStart = false;
+ return;
+ }
+ // Use a ClientCursor here so we can release db mutex while scanning
+ // oplog (can take quite a while with large oplogs).
+ shared_ptr<Cursor> c = _qp.newReverseCursor();
+ _findingStartCursor.reset( new ClientCursor(QueryOption_NoCursorTimeout, c, _qp.ns(), BSONObj()) );
+ _findingStartTimer.reset();
+ _findingStartMode = Initial;
}
// -------------------------------------
diff --git a/db/oplog.h b/db/oplog.h
index f87a1c85e04..223e8fbe9bb 100644
--- a/db/oplog.h
+++ b/db/oplog.h
@@ -118,6 +118,7 @@ namespace mongo {
_findingStartCursor.reset( 0 );
}
void init();
+ bool firstDocMatches() const;
};
void pretouchOperation(const BSONObj& op);
diff --git a/dbtests/querytests.cpp b/dbtests/querytests.cpp
index a50eadfcd31..b2ba9d02587 100644
--- a/dbtests/querytests.cpp
+++ b/dbtests/querytests.cpp
@@ -1146,7 +1146,35 @@ namespace QueryTests {
private:
int _old;
};
+
+ /**
+ * Check OplogReplay mode where query timestamp is earlier than the earliest
+ * entry in the collection.
+ */
+ class FindingStartStale : public CollectionBase {
+ public:
+ FindingStartStale() : CollectionBase( "findingstart" ) {}
+ void run() {
+ unsigned startNumCursors = ClientCursor::numCursors();
+
+ BSONObj info;
+ ASSERT( client().runCommand( "unittests", BSON( "create" << "querytests.findingstart" << "capped" << true << "$nExtents" << 5 << "autoIndexId" << false ), info ) );
+
+ // Check OplogReplay mode with empty collection.
+ auto_ptr< DBClientCursor > c = client().query( ns(), QUERY( "ts" << GTE << 50 ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( !c->more() );
+
+ // Check with some docs in the collection.
+ for( int i = 100; i < 150; client().insert( ns(), BSON( "ts" << i++ ) ) );
+ c = client().query( ns(), QUERY( "ts" << GTE << 50 ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 100, c->next()[ "ts" ].numberInt() );
+
+ // Check that no persistent cursors outlast our queries above.
+ ASSERT_EQUALS( startNumCursors, ClientCursor::numCursors() );
+ }
+ };
class WhatsMyUri : public CollectionBase {
public:
@@ -1362,6 +1390,7 @@ namespace QueryTests {
add< HelperTest >();
add< HelperByIdTest >();
add< FindingStartPartiallyFull >();
+ add< FindingStartStale >();
add< WhatsMyUri >();
add< parsedtests::basic1 >();
diff --git a/dbtests/repltests.cpp b/dbtests/repltests.cpp
index ecaacf74874..7009b556899 100644
--- a/dbtests/repltests.cpp
+++ b/dbtests/repltests.cpp
@@ -25,6 +25,8 @@
#include "../db/json.h"
#include "dbtests.h"
+#include "../db/oplog.h"
+#include "../db/queryoptimizer.h"
namespace mongo {
void createOplog();
@@ -1049,6 +1051,31 @@ namespace ReplTests {
}
};
+ /**
+ * Check against oldest document in the oplog before scanning backward
+ * from the newest document.
+ */
+ class FindingStartCursorStale : public Base {
+ public:
+ void run() {
+ for( int i = 0; i < 10; ++i ) {
+ client()->insert( ns(), BSON( "_id" << i ) );
+ }
+ dblock lk;
+ Client::Context ctx( cllNS() );
+ NamespaceDetails *nsd = nsdetails( cllNS() );
+ BSONObjBuilder b;
+ b.appendTimestamp( "$gte" );
+ BSONObj query = BSON( "ts" << b.obj() );
+ FieldRangeSetPair frsp( cllNS(), query );
+ BSONObj order = BSON( "$natural" << 1 );
+ QueryPlan qp( nsd, -1, frsp, frsp, query, order );
+ FindingStartCursor fsc( qp );
+ ASSERT( fsc.done() );
+ ASSERT_EQUALS( 0, fsc.cursor()->current()[ "o" ].Obj()[ "_id" ].Int() );
+ }
+ };
+
class All : public Suite {
public:
All() : Suite( "repl" ) {
@@ -1103,6 +1130,7 @@ namespace ReplTests {
add< DeleteOpIsIdBased >();
add< DatabaseIgnorerBasic >();
add< DatabaseIgnorerUpdate >();
+ add< FindingStartCursorStale >();
}
} myall;