Mongodb op msg database commands fix (#4004)

* * Fix: MongoDB::OpMsgCursor did not handle zero batch size properly: cursor requests failed.
* Improvement: Add emptyFirstBatch to indicate that the size of the first batch shall be zero for performance to get potential error ASAP from the server.

* Poco::MongoDB: Some database commands do not need collection as an argument. An integer "1" is passed instead.
This commit is contained in:
Matej Kenda
2023-05-05 16:27:42 +02:00
committed by GitHub
parent 9a2c16f55a
commit 971a7cc670
8 changed files with 125 additions and 17 deletions

View File

@@ -99,7 +99,7 @@ public:
/// Creates OpMsgMessage. (new wire protocol) /// Creates OpMsgMessage. (new wire protocol)
Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> createOpMsgMessage() const; Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> createOpMsgMessage() const;
/// Creates OpMsgMessage for database commands. (new wire protocol) /// Creates OpMsgMessage for database commands that do not require collection as an argument. (new wire protocol)
Poco::SharedPtr<Poco::MongoDB::OpMsgCursor> createOpMsgCursor(const std::string& collectionName) const; Poco::SharedPtr<Poco::MongoDB::OpMsgCursor> createOpMsgCursor(const std::string& collectionName) const;
/// Creates OpMsgCursor. (new wire protocol) /// Creates OpMsgCursor. (new wire protocol)
@@ -215,8 +215,8 @@ Database::createOpMsgMessage(const std::string& collectionName) const
inline Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> inline Poco::SharedPtr<Poco::MongoDB::OpMsgMessage>
Database::createOpMsgMessage() const Database::createOpMsgMessage() const
{ {
// Collection name for database commands is ignored and any value will do. // Collection name for database commands is not needed.
return createOpMsgMessage("1"); return createOpMsgMessage("");
} }
inline Poco::SharedPtr<Poco::MongoDB::OpMsgCursor> inline Poco::SharedPtr<Poco::MongoDB::OpMsgCursor>

View File

@@ -36,11 +36,16 @@ public:
virtual ~OpMsgCursor(); virtual ~OpMsgCursor();
/// Destroys the OpMsgCursor. /// Destroys the OpMsgCursor.
void setEmptyFirstBatch(bool empty);
/// Empty first batch is used to get error response faster with little server processing
bool emptyFirstBatch() const;
void setBatchSize(Int32 batchSize); void setBatchSize(Int32 batchSize);
/// Set non-default batch size /// Set non-default batch size
Int32 batchSize() const; Int32 batchSize() const;
/// Current batch size (negative number indicates default batch size) /// Current batch size (zero or negative number indicates default batch size)
Int64 cursorID() const; Int64 cursorID() const;
@@ -60,8 +65,9 @@ private:
OpMsgMessage _query; OpMsgMessage _query;
OpMsgMessage _response; OpMsgMessage _response;
bool _emptyFirstBatch { false };
Int32 _batchSize { -1 }; Int32 _batchSize { -1 };
/// Batch size used in the cursor. Negative value means that default shall be used. /// Batch size used in the cursor. Zero or negative value means that default shall be used.
Int64 _cursorID { 0 }; Int64 _cursorID { 0 };
}; };

View File

@@ -52,6 +52,8 @@ public:
// Replication and administration // Replication and administration
static const std::string CMD_HELLO; static const std::string CMD_HELLO;
static const std::string CMD_REPL_SET_GET_STATUS;
static const std::string CMD_REPL_SET_GET_CONFIG;
static const std::string CMD_CREATE; static const std::string CMD_CREATE;
static const std::string CMD_CREATE_INDEXES; static const std::string CMD_CREATE_INDEXES;

View File

@@ -71,6 +71,18 @@ OpMsgCursor::~OpMsgCursor()
} }
void OpMsgCursor::setEmptyFirstBatch(bool empty)
{
_emptyFirstBatch = empty;
}
bool OpMsgCursor::emptyFirstBatch() const
{
return _emptyFirstBatch;
}
void OpMsgCursor::setBatchSize(Int32 batchSize) void OpMsgCursor::setBatchSize(Int32 batchSize)
{ {
_batchSize = batchSize; _batchSize = batchSize;
@@ -89,16 +101,18 @@ OpMsgMessage& OpMsgCursor::next(Connection& connection)
{ {
_response.clear(); _response.clear();
if (_query.commandName() == OpMsgMessage::CMD_FIND) if (_emptyFirstBatch || _batchSize > 0)
{ {
if (_batchSize >= 0) Int32 bsize = _emptyFirstBatch ? 0 : _batchSize;
_query.body().add("batchSize", _batchSize); if (_query.commandName() == OpMsgMessage::CMD_FIND)
} {
else if (_query.commandName() == OpMsgMessage::CMD_AGGREGATE) _query.body().add("batchSize", bsize);
{ }
auto cursorDoc = _query.body().addNewDocument("cursor"); else if (_query.commandName() == OpMsgMessage::CMD_AGGREGATE)
if (_batchSize >= 0) {
cursorDoc.add("batchSize", _batchSize); auto& cursorDoc = _query.body().addNewDocument("cursor");
cursorDoc.add("batchSize", bsize);
}
} }
connection.sendRequest(_query, _response); connection.sendRequest(_query, _response);

View File

@@ -38,6 +38,8 @@ const std::string OpMsgMessage::CMD_MAP_REDUCE { "mapReduce" };
// Replication and administration // Replication and administration
const std::string OpMsgMessage::CMD_HELLO { "hello" }; const std::string OpMsgMessage::CMD_HELLO { "hello" };
const std::string OpMsgMessage::CMD_REPL_SET_GET_STATUS { "replSetGetStatus" };
const std::string OpMsgMessage::CMD_REPL_SET_GET_CONFIG { "replSetGetConfig" };
const std::string OpMsgMessage::CMD_CREATE { "create" }; const std::string OpMsgMessage::CMD_CREATE { "create" };
const std::string OpMsgMessage::CMD_CREATE_INDEXES { "createIndexes" }; const std::string OpMsgMessage::CMD_CREATE_INDEXES { "createIndexes" };
@@ -100,7 +102,16 @@ void OpMsgMessage::setCommandName(const std::string& command)
_body.clear(); _body.clear();
// IMPORTANT: Command name must be first // IMPORTANT: Command name must be first
_body.add(_commandName, _collectionName); if (_collectionName.empty())
{
// Collection is not specified. It is assumed that this particular command does
// not need it.
_body.add(_commandName, Int32(1));
}
else
{
_body.add(_commandName, _collectionName);
}
_body.add("$db", _databaseName); _body.add("$db", _databaseName);
} }
@@ -114,7 +125,7 @@ void OpMsgMessage::setCursor(Poco::Int64 cursorID, Poco::Int32 batchSize)
_body.add(_commandName, cursorID); _body.add(_commandName, cursorID);
_body.add("$db", _databaseName); _body.add("$db", _databaseName);
_body.add("collection", _collectionName); _body.add("collection", _collectionName);
if (batchSize >= 0) if (batchSize > 0)
{ {
_body.add("batchSize", batchSize); _body.add("batchSize", batchSize);
} }

View File

@@ -556,7 +556,11 @@ CppUnit::Test* MongoDBTest::suite()
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdCursor); CppUnit_addTest(pSuite, MongoDBTest, testOpCmdCursor);
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdCursorAggregate); CppUnit_addTest(pSuite, MongoDBTest, testOpCmdCursorAggregate);
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdKillCursor); CppUnit_addTest(pSuite, MongoDBTest, testOpCmdKillCursor);
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdCursorEmptyFirstBatch);
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdUUID); CppUnit_addTest(pSuite, MongoDBTest, testOpCmdUUID);
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdDropDatabase);
} }
return pSuite; return pSuite;

View File

@@ -57,11 +57,13 @@ public:
void testOpCmdFind(); void testOpCmdFind();
void testOpCmdCursor(); void testOpCmdCursor();
void testOpCmdCursorAggregate(); void testOpCmdCursorAggregate();
void testOpCmdCursorEmptyFirstBatch();
void testOpCmdKillCursor(); void testOpCmdKillCursor();
void testOpCmdCount(); void testOpCmdCount();
void testOpCmdDelete(); void testOpCmdDelete();
void testOpCmdUnaknowledgedInsert(); void testOpCmdUnaknowledgedInsert();
void testOpCmdConnectionPool(); void testOpCmdConnectionPool();
void testOpCmdDropDatabase();
static CppUnit::Test* suite(); static CppUnit::Test* suite();

View File

@@ -327,7 +327,11 @@ void MongoDBTest::testOpCmdCursorAggregate()
auto cresponse = cursor->next(*_mongo); auto cresponse = cursor->next(*_mongo);
while(true) while(true)
{ {
n += static_cast<int>(cresponse.documents().size()); int batchDocSize = cresponse.documents().size();
if (cursor->cursorID() != 0)
assertEquals (1000, batchDocSize);
n += batchDocSize;
if ( cursor->cursorID() == 0 ) if ( cursor->cursorID() == 0 )
break; break;
cresponse = cursor->next(*_mongo); cresponse = cursor->next(*_mongo);
@@ -400,6 +404,53 @@ void MongoDBTest::testOpCmdCount()
} }
void MongoDBTest::testOpCmdCursorEmptyFirstBatch()
{
Database db("team");
Poco::SharedPtr<OpMsgMessage> request = db.createOpMsgMessage("numbers");
OpMsgMessage response;
request->setCommandName(OpMsgMessage::CMD_DROP);
_mongo->sendRequest(*request, response);
request->setCommandName(OpMsgMessage::CMD_INSERT);
for(int i = 0; i < 10000; ++i)
{
Document::Ptr doc = new Document();
doc->add("number", i);
request->documents().push_back(doc);
}
_mongo->sendRequest(*request, response);
assertTrue(response.responseOk());
Poco::SharedPtr<OpMsgCursor> cursor = db.createOpMsgCursor("numbers");
cursor->query().setCommandName(OpMsgMessage::CMD_AGGREGATE);
cursor->setEmptyFirstBatch(true);
cursor->setBatchSize(0); // Will be ignored, default is used
// Empty pipeline: get all documents
cursor->query().body().addNewArray("pipeline");
auto cresponse = cursor->next(*_mongo);
assertEquals (0, cresponse.documents().size()); // First batch is empty
int n = 0;
while(true)
{
n += static_cast<int>(cresponse.documents().size());
if ( cursor->cursorID() == 0 )
break;
cresponse = cursor->next(*_mongo);
}
assertEquals (10000, n);
request->setCommandName(OpMsgMessage::CMD_DROP);
_mongo->sendRequest(*request, response);
assertTrue(response.responseOk());
}
void MongoDBTest::testOpCmdDelete() void MongoDBTest::testOpCmdDelete()
{ {
Database db("team"); Database db("team");
@@ -442,3 +493,21 @@ void MongoDBTest::testOpCmdConnectionPool()
assertEquals (1, doc.getInteger("n")); assertEquals (1, doc.getInteger("n"));
} }
void MongoDBTest::testOpCmdDropDatabase()
{
Database db("team");
Poco::SharedPtr<OpMsgMessage> request = db.createOpMsgMessage();
request->setCommandName(OpMsgMessage::CMD_DROP_DATABASE);
OpMsgMessage response;
_mongo->sendRequest(*request, response);
std::cout << request->body().toString(2) << std::endl;
std::cout << response.body().toString(2) << std::endl;
assertTrue(response.responseOk());
}