Rex Dieter fcd69d
From 24413dc44b0637d6c64e6b2105c2bcf1b99849a5 Mon Sep 17 00:00:00 2001
Rex Dieter fcd69d
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
Rex Dieter fcd69d
Date: Sun, 6 Apr 2014 19:50:38 +0200
Rex Dieter fcd69d
Subject: [PATCH 13/16] Disable global transaction mutex for QSQLITE3 and
Rex Dieter fcd69d
 enable transaction recording
Rex Dieter fcd69d
Rex Dieter fcd69d
Our QSQLITE3 driver now supports concurrency, so we don't need to serialize
Rex Dieter fcd69d
transactions in DataStore anymore. It is however still needed for the
Rex Dieter fcd69d
QSQLITE driver shipped with Qt.
Rex Dieter fcd69d
Rex Dieter fcd69d
Secondary, concurrency support also means possible transactions deadlocks and
Rex Dieter fcd69d
timeouts, so we also need to enable transaction recording and replaying for
Rex Dieter fcd69d
the QSQLITE3 backend.
Rex Dieter fcd69d
---
Rex Dieter fcd69d
 server/src/storage/datastore.cpp    | 18 +++++++++++-------
Rex Dieter fcd69d
 server/src/storage/datastore.h      |  2 +-
Rex Dieter fcd69d
 server/src/storage/dbtype.cpp       |  5 +++++
Rex Dieter fcd69d
 server/src/storage/dbtype.h         |  3 +++
Rex Dieter fcd69d
 server/src/storage/querybuilder.cpp | 20 ++++++++++++++++----
Rex Dieter fcd69d
 server/src/storage/querybuilder.h   |  2 +-
Rex Dieter fcd69d
 6 files changed, 37 insertions(+), 13 deletions(-)
Rex Dieter fcd69d
Rex Dieter fcd69d
diff --git a/server/src/storage/datastore.cpp b/server/src/storage/datastore.cpp
Rex Dieter fcd69d
index 57d1e4e..0f04fa5 100644
Rex Dieter fcd69d
--- a/server/src/storage/datastore.cpp
Rex Dieter fcd69d
+++ b/server/src/storage/datastore.cpp
Rex Dieter fcd69d
@@ -61,8 +61,8 @@ using namespace Akonadi::Server;
Rex Dieter fcd69d
 static QMutex sTransactionMutex;
Rex Dieter fcd69d
 bool DataStore::s_hasForeignKeyConstraints = false;
Rex Dieter fcd69d
 
Rex Dieter fcd69d
-#define TRANSACTION_MUTEX_LOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.lock()
Rex Dieter fcd69d
-#define TRANSACTION_MUTEX_UNLOCK if ( DbType::type( m_database ) == DbType::Sqlite ) sTransactionMutex.unlock()
Rex Dieter fcd69d
+#define TRANSACTION_MUTEX_LOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.lock()
Rex Dieter fcd69d
+#define TRANSACTION_MUTEX_UNLOCK if ( DbType::isSystemSQLite( m_database ) ) sTransactionMutex.unlock()
Rex Dieter fcd69d
 
Rex Dieter fcd69d
 /***************************************************************************
Rex Dieter fcd69d
  *   DataStore                                                           *
Rex Dieter fcd69d
@@ -1083,23 +1083,27 @@ QDateTime DataStore::dateTimeToQDateTime( const QByteArray &dateTime )
Rex Dieter fcd69d
 
Rex Dieter fcd69d
 void DataStore::addQueryToTransaction( const QSqlQuery &query, bool isBatch )
Rex Dieter fcd69d
 {
Rex Dieter fcd69d
-  DbType::Type dbType = DbType::type( m_database );
Rex Dieter fcd69d
   // This is used for replaying deadlocked transactions, so only record queries
Rex Dieter fcd69d
   // for backends that support concurrent transactions.
Rex Dieter fcd69d
-  if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) {
Rex Dieter fcd69d
+  if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) {
Rex Dieter fcd69d
     return;
Rex Dieter fcd69d
   }
Rex Dieter fcd69d
 
Rex Dieter fcd69d
   m_transactionQueries.append( qMakePair( query, isBatch ) );
Rex Dieter fcd69d
 }
Rex Dieter fcd69d
 
Rex Dieter fcd69d
-QSqlQuery DataStore::retryLastTransaction()
Rex Dieter fcd69d
+QSqlQuery DataStore::retryLastTransaction( bool rollbackFirst )
Rex Dieter fcd69d
 {
Rex Dieter fcd69d
-  DbType::Type dbType = DbType::type( m_database );
Rex Dieter fcd69d
-  if ( !inTransaction() || ( dbType != DbType::MySQL && dbType != DbType::PostgreSQL ) ) {
Rex Dieter fcd69d
+  if ( !inTransaction() || DbType::isSystemSQLite( m_database ) ) {
Rex Dieter fcd69d
     return QSqlQuery();
Rex Dieter fcd69d
   }
Rex Dieter fcd69d
 
Rex Dieter fcd69d
+  if ( rollbackFirst ) {
Rex Dieter fcd69d
+    // In some cases the SQL database won't rollback the failed transaction, so
Rex Dieter fcd69d
+    // we need to do it manually
Rex Dieter fcd69d
+    m_database.driver()->rollbackTransaction();
Rex Dieter fcd69d
+  }
Rex Dieter fcd69d
+
Rex Dieter fcd69d
   // The database has rolled back the actual transaction, so reset the counter
Rex Dieter fcd69d
   // to 0 and start a new one in beginTransaction(). Then restore the level
Rex Dieter fcd69d
   // because this has to be completely transparent to the original caller
Rex Dieter fcd69d
diff --git a/server/src/storage/datastore.h b/server/src/storage/datastore.h
Rex Dieter fcd69d
index 8b4a2b7..8a0fe01 100644
Rex Dieter fcd69d
--- a/server/src/storage/datastore.h
Rex Dieter fcd69d
+++ b/server/src/storage/datastore.h
Rex Dieter fcd69d
@@ -317,7 +317,7 @@ protected:
Rex Dieter fcd69d
      * @return Returns an invalid query when error occurs, or the last replayed
Rex Dieter fcd69d
      *         query on success.
Rex Dieter fcd69d
      */
Rex Dieter fcd69d
-    QSqlQuery retryLastTransaction();
Rex Dieter fcd69d
+    QSqlQuery retryLastTransaction( bool rollbackFirst );
Rex Dieter fcd69d
 
Rex Dieter fcd69d
   private Q_SLOTS:
Rex Dieter fcd69d
     void sendKeepAliveQuery();
Rex Dieter fcd69d
diff --git a/server/src/storage/dbtype.cpp b/server/src/storage/dbtype.cpp
Rex Dieter fcd69d
index 495f532..7df2fb1 100644
Rex Dieter fcd69d
--- a/server/src/storage/dbtype.cpp
Rex Dieter fcd69d
+++ b/server/src/storage/dbtype.cpp
Rex Dieter fcd69d
@@ -39,3 +39,8 @@ DbType::Type DbType::typeForDriverName( const QString &driverName )
Rex Dieter fcd69d
   }
Rex Dieter fcd69d
   return Unknown;
Rex Dieter fcd69d
 }
Rex Dieter fcd69d
+
Rex Dieter fcd69d
+bool DbType::isSystemSQLite( const QSqlDatabase &db )
Rex Dieter fcd69d
+{
Rex Dieter fcd69d
+  return db.driverName() == QLatin1String( "QSQLITE" );
Rex Dieter fcd69d
+}
Rex Dieter fcd69d
diff --git a/server/src/storage/dbtype.h b/server/src/storage/dbtype.h
Rex Dieter fcd69d
index a95a833..3595604 100644
Rex Dieter fcd69d
--- a/server/src/storage/dbtype.h
Rex Dieter fcd69d
+++ b/server/src/storage/dbtype.h
Rex Dieter fcd69d
@@ -42,6 +42,9 @@ namespace DbType
Rex Dieter fcd69d
   /** Returns the type for the given driver name. */
Rex Dieter fcd69d
   Type typeForDriverName( const QString &driverName );
Rex Dieter fcd69d
 
Rex Dieter fcd69d
+  /** Returns true when using QSQLITE driver shipped with Qt, FALSE otherwise */
Rex Dieter fcd69d
+  bool isSystemSQLite( const QSqlDatabase &db );
Rex Dieter fcd69d
+
Rex Dieter fcd69d
 } // namespace DbType
Rex Dieter fcd69d
 } // namespace Server
Rex Dieter fcd69d
 } // namespace Akonadi
Rex Dieter fcd69d
diff --git a/server/src/storage/querybuilder.cpp b/server/src/storage/querybuilder.cpp
Rex Dieter fcd69d
index 0abad4a..0530b11 100644
Rex Dieter fcd69d
--- a/server/src/storage/querybuilder.cpp
Rex Dieter fcd69d
+++ b/server/src/storage/querybuilder.cpp
Rex Dieter fcd69d
@@ -320,10 +320,10 @@ QString QueryBuilder::buildQuery()
Rex Dieter fcd69d
   return statement;
Rex Dieter fcd69d
 }
Rex Dieter fcd69d
 
Rex Dieter fcd69d
-bool QueryBuilder::retryLastTransaction()
Rex Dieter fcd69d
+bool QueryBuilder::retryLastTransaction( bool rollback )
Rex Dieter fcd69d
 {
Rex Dieter fcd69d
 #ifndef QUERYBUILDER_UNITTEST
Rex Dieter fcd69d
-  mQuery = DataStore::self()->retryLastTransaction();
Rex Dieter fcd69d
+  mQuery = DataStore::self()->retryLastTransaction( rollback );
Rex Dieter fcd69d
   return !mQuery.lastError().isValid();
Rex Dieter fcd69d
 #else
Rex Dieter fcd69d
   return true;
Rex Dieter fcd69d
@@ -400,9 +400,21 @@ bool QueryBuilder::exec()
Rex Dieter fcd69d
         akDebug() << mQuery.lastError().text();
Rex Dieter fcd69d
         return retryLastTransaction();
Rex Dieter fcd69d
       }
Rex Dieter fcd69d
+    } else if ( mDatabaseType == DbType::Sqlite && !DbType::isSystemSQLite( DataStore::self()->database() ) ) {
Rex Dieter fcd69d
+      const int error = mQuery.lastError().number();
Rex Dieter fcd69d
+      if ( error == 6 /* SQLITE_LOCKED */ ) {
Rex Dieter fcd69d
+        akDebug() << "QueryBuilder::exec(): database reported transaction deadlock, retrying transaction";
Rex Dieter fcd69d
+        akDebug() << mQuery.lastError().text();
Rex Dieter fcd69d
+        return retryLastTransaction();
Rex Dieter fcd69d
+      } else if ( error == 5 /* SQLITE_BUSY */ ) {
Rex Dieter fcd69d
+        akDebug() << "QueryBuilder::exec(): database reported transaction timeout, retrying transaction";
Rex Dieter fcd69d
+        akDebug() << mQuery.lastError().text();
Rex Dieter fcd69d
+        return retryLastTransaction( true );
Rex Dieter fcd69d
+      }
Rex Dieter fcd69d
     } else if ( mDatabaseType == DbType::Sqlite ) {
Rex Dieter fcd69d
-      // We can't have a transaction deadlock in SQLite, because it does not support
Rex Dieter fcd69d
-      // concurrent transactions and DataStore serializes them through a global lock.
Rex Dieter fcd69d
+      // We can't have a transaction deadlock in SQLite when using driver shipped
Rex Dieter fcd69d
+      // with Qt, because it does not support concurrent transactions and DataStore
Rex Dieter fcd69d
+      // serializes them through a global lock.
Rex Dieter fcd69d
     }
Rex Dieter fcd69d
 
Rex Dieter fcd69d
     akError() << "DATABASE ERROR:";
Rex Dieter fcd69d
diff --git a/server/src/storage/querybuilder.h b/server/src/storage/querybuilder.h
Rex Dieter fcd69d
index 235a099..b380f93 100644
Rex Dieter fcd69d
--- a/server/src/storage/querybuilder.h
Rex Dieter fcd69d
+++ b/server/src/storage/querybuilder.h
Rex Dieter fcd69d
@@ -244,7 +244,7 @@ class QueryBuilder
Rex Dieter fcd69d
      */
Rex Dieter fcd69d
     void sqliteAdaptUpdateJoin( Query::Condition &cond );
Rex Dieter fcd69d
 
Rex Dieter fcd69d
-    bool retryLastTransaction();
Rex Dieter fcd69d
+    bool retryLastTransaction( bool rollback = false);
Rex Dieter fcd69d
 
Rex Dieter fcd69d
   private:
Rex Dieter fcd69d
     QString mTable;
Rex Dieter fcd69d
-- 
Rex Dieter fcd69d
1.9.0
Rex Dieter fcd69d