]> Some of my projects - localmylist.git/commitdiff
Add active connection checking.
authorAPTX <marek321@gmail.com>
Wed, 18 Sep 2013 18:16:36 +0000 (20:16 +0200)
committerAPTX <marek321@gmail.com>
Wed, 18 Sep 2013 18:16:36 +0000 (20:16 +0200)
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.

localmylist/database.cpp
localmylist/database.h
localmylist/mylist.cpp
localmylist/renamehandler.cpp
localmylist/requesthandler.cpp

index 34c0d617c31ac5d78850f240f3ec5eb4bc29d6df..6a4965325d866d3db98c5df015b4cbf62068119a 100644 (file)
@@ -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)
 {
index 6ed2a7b83fa5a65b6b0a7bf906225eef3e4be995..89e700d90dcc12b0e1ffa75a873ad9c26b2930f4 100644 (file)
@@ -10,6 +10,7 @@
 #include <QStringList>
 #include <QSqlDriver>
 #include <QVariant>
+#include <QTimer>
 
 #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
index 58fbc1b3e8370bb96e54c24b555e6d13c52f12bb..acc9038eb2d8ebaa36759aa4405d500cb8d63a20 100644 (file)
@@ -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()
index fd597179e5b466285c21bd6e0b5e041a7c303b14..3e8cdf21dcd698ea2229a3ab169cb10e53142adb 100644 (file)
@@ -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;
 }
 
index cbc6c4c07c4b642c4e9e6d350ea0c8c88b587319..132768224f1aa98c6e142c9c32960efec1736b97 100644 (file)
@@ -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();
 }