I have multi-threaded QTcpServer and for each database request, it creates new Thread to keep server Responsive. So in each thread I have to creating new QSqlDatabase connection. But I keep getting name collisions between connections.
here is my sample code to recreate issue.:-
#include <QSqlDatabase>
class DBTask
{
public:
DBTask(ClientSocket *socket,ConnectionWorker *connectionWorker);
~DBTask();
static void initStatic();
private:
static QThreadPool *pool; // all addConnection() call be be called in QtConcurrent::run with this pool
static QString host, user, type, password, name;
static quint64 dbConnectionNumber;
QSqlDatabase db;
ClientSocket *socket;
ConnectionWorker *connectionWorker;
bool addDatabase() ;
};
quint64 DBTask::dbConnectionNumber=0;
DBTask::DBTask(ClientSocket *socket, ConnectionWorker *connectionWorker):
socket(socket),
connectionWorker(connectionWorker)
{
dbConnectionNumber++;
}
bool DBTask::addDatabase() {
QSqlDatabase db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
db.setHostName(host);
db.setDatabaseName(name);
db.setUserName(user);
db.setPassword(password);
if(!db.open()){
qWarning() << "Error while opening database for socket " << socket << '\n' << db.lastError();
return false;
}
else {
return true;
}
}
this works fine when I manually check my application with GUI with human speed But when I run a c++ test code which simulates thousands of requests like this:-
void connectionTest(){
QThreadPool pool;
pool.setMaxThreadCount(10);
for(int i=0;i<10;i++){
QtConcurrent::run(&pool,[this](){
for(int i=0;i<1000;i++){
login(i%2); // login function sends request to QTcpServer
}
});
}
}
I get multiple errors like this:-
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
and Server crashes with segfault
Even if you make the counter atomic, a thread can still get interrupted in the DBTask::addDatabase
method (before creating the connection), another one can increment the counter and then they both continue and create 2 connections with the same id. You need to make both operations (increment the counter and the connection creation) in one transaction: inside the DBTask::addDatabase
, by making use of a mutex lock.