From: APTX Date: Wed, 18 Sep 2013 18:16:36 +0000 (+0200) Subject: Add active connection checking. X-Git-Url: https://gitweb.aptx.org/?a=commitdiff_plain;h=7b2539d95578eb651566bd8ea5c5ec959855f2c9;p=localmylist.git Add active connection checking. QtSQL (pg driver) does not send any notification that the connection to the database has terminated. LML tries to reconnect if a query fails with a connection error. That check is not sufficient for the objects that wait for DB notifications. This solution checks if the connection is still alive by executing a simple query every minute. If the query fails on a connection error the reconnect mechanism triggers as before. A new reconnected() signal is emitted when a reconnect occurs. The active connection checking is only done in the main thread. Request-/Rename-handler listen for the new signal. --- diff --git a/localmylist/database.cpp b/localmylist/database.cpp index 34c0d61..6a49653 100644 --- a/localmylist/database.cpp +++ b/localmylist/database.cpp @@ -15,7 +15,7 @@ namespace LocalMyList { struct DatabaseInternal { - DatabaseInternal() : transactionLevel(0) {} + DatabaseInternal() : transactionLevel(0), thread(0) {} QSqlDatabase db; @@ -46,10 +46,20 @@ struct DatabaseInternal } }; -Database::Database(const QString &connectionName) : d(0) +Database::Database(const QString &connectionName) : d(0), pingTimer(0) { this->connectionName = connectionName; reconnectAttempt = false; + + if (connectionName != defaultConnectionName) + return; + + qDebug() << "Default connection. Setting up pingTimer"; + + pingTimer = new QTimer(this); + QObject::connect(this, SIGNAL(connected()), this, SLOT(startPingTimer())); + QObject::connect(this, SIGNAL(disconnected()), this, SLOT(stopPingTimer())); + QObject::connect(pingTimer, SIGNAL(timeout()), this, SLOT(pingTimeout())); } Database::~Database() @@ -1739,6 +1749,18 @@ void Database::disconnect() emit disconnected(); } +bool Database::reconnect() +{ + disconnect(); + bool ret = connect(); + if (ret) + { + emit reconnected(); + qDebug() << "reconnected"; + } + return ret; +} + void Database::readAnimeTitleData(const SqlResultIteratorInterface &result, AnimeTitle &data, int offset) { data.titleId = result.value(offset++).toInt(); @@ -1982,8 +2004,7 @@ QSqlQuery &Database::prepare(const char *const sql) { if (!isConnected()) { - disconnect(); - if (!connect()) + if (!reconnect()) { auto ite = d->preparedQueries.insert("___invalid", QSqlQuery(d->db)); return ite.value(); @@ -2007,8 +2028,7 @@ QSqlQuery Database::prepareOneShot(const QString &sql) if (!isConnected()) { - disconnect(); - if (!connect()) + if (!reconnect()) return query; } @@ -2023,13 +2043,14 @@ bool Database::exec(QSqlQuery &query) if (!isConnected()) { - disconnect(); - connect(); + reconnect(); // TODO can more be done? return false; } + lastQuery = QDateTime::currentDateTime(); + if (!query.exec()) return checkError(query); return true; @@ -2041,12 +2062,12 @@ bool Database::exec(const QString &sql) if (!isConnected()) { - disconnect(); - if (!connect()) + if (!reconnect()) return false; } QSqlQuery query = QSqlQuery(d->db); + lastQuery = QDateTime::currentDateTime(); if (!query.exec(sql)) return checkError(query); @@ -2066,9 +2087,7 @@ bool Database::checkError(QSqlQuery &query, bool prepared) if (query.lastError().type() == QSqlError::ConnectionError) { - disconnect(); - - if (!connect()) + if (!reconnect()) return false; return retryExec(query, prepared); @@ -2266,6 +2285,29 @@ void Database::handleNotification(const QString &name) #endif } +void Database::startPingTimer() +{ + qDebug() << "Starting pingtimer"; + lastQuery = QDateTime::currentDateTime(); + + pingTimer->setSingleShot(false); + pingTimer->start(1000 * 60); +} + +void Database::stopPingTimer() +{ + qDebug() << "Stoppting pingtimer"; + pingTimer->stop(); +} + +void Database::pingTimeout() +{ + if (lastQuery.addSecs(60) > QDateTime::currentDateTime()) + return; + + exec("SELECT 1"); +} + RaiiTransaction::RaiiTransaction(Database *db) : d(db), c(false) { diff --git a/localmylist/database.h b/localmylist/database.h index 6ed2a7b..89e700d 100644 --- a/localmylist/database.h +++ b/localmylist/database.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "databaseclasses.h" #include "sqlresultiteratorinterface.h" @@ -30,11 +31,13 @@ private: struct DatabaseInternal; +static const QString defaultConnectionName = [](){return QString("default");}(); + class LOCALMYLISTSHARED_EXPORT Database : public QObject { Q_OBJECT public: - Database(const QString &connectionName = "default"); + Database(const QString &connectionName = defaultConnectionName); ~Database(); public slots: @@ -154,6 +157,7 @@ public slots: bool connect(); void disconnect(); + bool reconnect(); static void readAnimeTitleData(const SqlResultIteratorInterface &result, AnimeTitle &data, int offset = 0); static void readAnimeData(const SqlResultIteratorInterface &result, Anime &data, int offset = 0); @@ -174,6 +178,7 @@ public slots: signals: void connected(); void disconnected(); + void reconnected(); void newPendingRequest(); void newPendingMyListUpdate(); @@ -201,6 +206,10 @@ private slots: void handleNotification(const QString &name); #endif + void startPingTimer(); + void stopPingTimer(); + void pingTimeout(); + private: void subscribeToNotifications(); @@ -214,6 +223,9 @@ private: QString connectionName; bool reconnectAttempt; + + QTimer *pingTimer; + QDateTime lastQuery; }; class LOCALMYLISTSHARED_EXPORT RaiiTransaction diff --git a/localmylist/mylist.cpp b/localmylist/mylist.cpp index 58fbc1b..acc9038 100644 --- a/localmylist/mylist.cpp +++ b/localmylist/mylist.cpp @@ -32,8 +32,7 @@ MyList::MyList() m_directoryWatcher = 0; workThread = 0; m_defaultLocalQSettings = new QSettings(QSettings::IniFormat, QSettings::UserScope, organizationName, libraryName, this); - - db = new Database("main"); + db = new Database(); connect(db, SIGNAL(connected()), this, SLOT(setupHostInfo())); m_settings = new Settings(db, this); @@ -222,6 +221,7 @@ void MyList::setupWorkThread() return; workThread = new WorkThread("workThread", dbs, this); connect(workThread, SIGNAL(finished()), workThread, SLOT(deleteLater())); + connect(database(), SIGNAL(reconnected()), workThread->database(), SLOT(reconnect()), Qt::QueuedConnection); workThread->start(); } @@ -445,7 +445,10 @@ bool MyList::isUdpClientAvailable() void MyList::signalDebugMessage(const QString &message) { - emit instance()->debugMessage(message); + // Do not create a mylist instance here. + // Can cause a loop if message handler is called from MyList constructor. + if (m_instance) + emit instance()->debugMessage(message); } const char *MyList::revision() diff --git a/localmylist/renamehandler.cpp b/localmylist/renamehandler.cpp index fd59717..3e8cdf2 100644 --- a/localmylist/renamehandler.cpp +++ b/localmylist/renamehandler.cpp @@ -20,6 +20,7 @@ RenameHandler::RenameHandler(Database *db, Settings *settings, QObject *parent) connect(db, SIGNAL(renameDataChanged()), this, SLOT(handleRename()), Qt::QueuedConnection); connect(this, SIGNAL(renameBatchFinished()), this, SLOT(handleRename()), Qt::QueuedConnection); connect(settings, SIGNAL(settingsChanged()), this, SLOT(clearSetup())); + connect(db, SIGNAL(reconnected()), this, SLOT(handleRename()), Qt::QueuedConnection); m_setup = false; } diff --git a/localmylist/requesthandler.cpp b/localmylist/requesthandler.cpp index cbc6c4c..1327682 100644 --- a/localmylist/requesthandler.cpp +++ b/localmylist/requesthandler.cpp @@ -21,6 +21,8 @@ RequestHandler::RequestHandler(Database *db, QObject *parent) : this->db = db; connect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()), Qt::QueuedConnection); connect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection); + connect(db, SIGNAL(reconnected()), this, SLOT(handleRequests()), Qt::QueuedConnection); + connect(db, SIGNAL(reconnected()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection); db->failPendingRequestsFromOldClients(); }