Dear all
I am developing a web application using Qt 5.7.0 and the MSVC 2015 64-bit compiler. I am using Qt Creator as IDE. I managed to build and link the mongoc, mongocxx and bsoncxx libraries with the system. These libraries have been built in release mode with the same compilers and runtimes as the Qt web application.
In the debug build everything works fine. However, in the release build of the web application, I manage to create an instance and connection pool in the main thread of the application but when I try to acquire a connection from this pool in a child thread, the system crashes.
I am not sure if this is a bug in the driver or in my multithreaded code so I include the relevant snippets of code below. The MongoPool object is created in the main thread. The MongoConnector objects are created in the child threads when a new user request is received.
#include <QCoreApplication>
#include <QDir>
#include <QSettings>
#include "mongoconnector.h"
#include "mongopool.h"
#include <cstdlib>
#include <iostream>
#include <string>
#include <memory>
MongoPool *pool;
int main(int argc, char *argv[])
{
QCoreApplication app(argc,argv);
// Initialize MongoDB
pool = new MongoPool();
qWarning("Application has started");
app.exec();
qWarning("Application has stopped");
}
#include "mongoconnector.h"
extern MongoPool *pool;
MongoConnector::MongoConnector()
{
}
bool MongoConnector::storeDocument(QString dbName,
QString collectionName,
QString docID,
QString doc)
{
std::cout << "Storing document" << "\n";
// Initialize MongoDB
using bsoncxx::builder::basic::document;
using bsoncxx::builder::basic::kvp;
try {
std::cout << "Creating database connection" << "\n";
mongocxx::pool::entry client = pool->get_connection();
if(!client)
{
std::cout << "No Connection!";
}
std::cout << "Obtaining database and collection" << "\n";
mongocxx::database dataDB = client->database(dbName.toLatin1().toStdString());
mongocxx::collection dataCollection = dataDB[collectionName.toLatin1().toStdString()];
std::string docDB = doc.toLatin1().toStdString();
std::cout << "Creating bson object" << "\n";
bsoncxx::document::value obj = bsoncxx::from_json(docDB);
std::cout << "Creating builder" << "\n";
bsoncxx::builder::basic::document basic_builder{};
basic_builder.append(kvp("_id", docID.toStdString()));
basic_builder.append(kvp("doc", obj));
bsoncxx::document::value doc_value = basic_builder.extract();
bsoncxx::document::view view = doc_value.view();
std::cout << "Inserting document" << "\n";
dataCollection.insert_one(view);
std::cout << bsoncxx::to_json(view) << "\n";
} catch (const std::exception& xcp) {
std::cout << "connection failed: " << xcp.what() << "\n";
}
return true;
}
QString MongoConnector::retrieveDocument(QString dbName,
QString collectionName,
QString docID,
bool retrieveMultipleDocuments)
{
QString result;
// Initialize MongoDB
using bsoncxx::builder::stream::document;
try {
mongocxx::pool::entry client = pool->get_connection();
mongocxx::database dataDB = client->database(dbName.toLatin1().toStdString());
mongocxx::collection dataCollection = dataDB[collectionName.toLatin1().toStdString()];
std::string docIDStd = docID.toLatin1().toStdString();
auto builder = bsoncxx::builder::stream::document{};
bsoncxx::document::value doc_value = builder
<< "_id" << docIDStd
<< bsoncxx::builder::stream::finalize;
bsoncxx::document::view view = doc_value.view();
if(retrieveMultipleDocuments)
{
mongocxx::cursor cursor =
dataCollection.find(view);
for(auto doc : cursor)
{
std::cout << bsoncxx::to_json(doc) << "\n";
result += QString::fromStdString(bsoncxx::to_json(doc))+"\n\n**********\n\n";
}
}
else
{
bsoncxx::stdx::optional<bsoncxx::document::value> maybe_result =
dataCollection.find_one(view);
if(maybe_result)
{
std::cout << bsoncxx::to_json(*maybe_result) << "\n";
result = QString::fromStdString(bsoncxx::to_json(*maybe_result));
}
}
} catch (const std::exception& xcp) {
std::cout << "connection failed: " << xcp.what() << "\n";
}
return result;
}
#ifndef MONGOCONNECTOR_H
#define MONGOCONNECTOR_H
#include <QtCore>
#include "mongopool.h"
#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/json.hpp>
#include <bsoncxx/string/view_or_value.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/stdx.hpp>
#include <mongocxx/uri.hpp>
#include <mongocxx/database.hpp>
#include <mongocxx/collection.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <memory>
#include <bsoncxx/stdx/make_unique.hpp>
#include <mongocxx/logger.hpp>
#include <mongocxx/options/client.hpp>
#include <mongocxx/pool.hpp>
class MongoConnector
{
public:
MongoConnector();
bool storeDocument(QString dbName,
QString collectionName,
QString docID,
QString doc);
QString retrieveDocument(QString dbName,
QString collectionName,
QString docID,
bool retrieveMultipleDocuments);
private:
};
#endif // MONGOCONNECTOR_H
#include "mongopool.h"
MongoPool::MongoPool()
{
using bsoncxx::builder::stream::document;
auto uri = mongocxx::uri{mongocxx::uri::k_default_uri};
configure(std::move(uri));
try {
mongocxx::pool::entry client = get_connection();
auto admin = client->database("admin");
document ismaster;
ismaster << "isMaster" << 1;
auto result = admin.run_command(ismaster.view());
} catch (const std::exception& xcp) {
std::cout << "connection failed: " << xcp.what() << "\n";
}
}
void MongoPool::configure(mongocxx::uri uri) {
class noop_logger : public mongocxx::logger {
public:
virtual void operator()(mongocxx::log_level, mongocxx::stdx::string_view,
mongocxx::stdx::string_view) noexcept {
}
};
auto instance =
mongocxx::stdx::make_unique<mongocxx::instance>(mongocxx::stdx::make_unique<noop_logger>());
configure(std::move(instance),
mongocxx::stdx::make_unique<mongocxx::pool>(std::move(uri)));
}
void MongoPool::configure(std::unique_ptr<mongocxx::instance> instance,
std::unique_ptr<mongocxx::pool> pool) {
_instance = std::move(instance);
_pool = std::move(pool);
}
mongocxx::pool::entry MongoPool::get_connection() {
return _pool->acquire();
}
mongocxx::stdx::optional<mongocxx::pool::entry> MongoPool::try_get_connection() {
return _pool->try_acquire();
}
#ifndef MONGOPOOL_H
#define MONGOPOOL_H
#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/json.hpp>
#include <bsoncxx/string/view_or_value.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/stdx.hpp>
#include <mongocxx/uri.hpp>
#include <mongocxx/database.hpp>
#include <mongocxx/collection.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <memory>
#include <bsoncxx/stdx/make_unique.hpp>
#include <mongocxx/logger.hpp>
#include <mongocxx/options/client.hpp>
#include <mongocxx/pool.hpp>
class MongoPool
{
public:
MongoPool();
void configure(mongocxx::uri uri);
void configure(std::unique_ptr<mongocxx::instance> instance,
std::unique_ptr<mongocxx::pool> pool);
mongocxx::pool::entry get_connection();
mongocxx::stdx::optional<mongocxx::pool::entry> try_get_connection();
private:
std::unique_ptr<mongocxx::instance> _instance;
std::unique_ptr<mongocxx::pool> _pool;
};
#endif // MONGOPOOL_H